The daemon caught Loc.Error at each op and nothing else. That was survivable while the frontend was the only thing that could refuse a form; it is not now that expansion is part of evaluating. Both C-c C-c and C-x C-e run a clang driver through Build.macro_module, which answers with an exit status and a Failure, and a dlopen that finds no symbol answers with another one. Neither is a Loc.Error, so neither was answered, and an exception past serve is not a refused evaluation — it is a dead daemon with the program still on screen and a closed socket waiting for the editor's next request. The boundary is now one place, around the whole of a request, rather than a new arm at each of the dozens of calls. Out_of_memory, Stack_overflow and Sys.Break go through it: those say the process cannot continue, and answering "error" to them would claim a session survived something it did not. Everything else is about the form that was sent, and the message it carries is the one the user can act on, so a clang exit status reaches :message instead of being flattened to "internal error". The session's own state goes with it. Session.eval wrote the imported macro set above the checker, so a form that did not check left the session holding a package's macros and none of its declarations; it is held and committed at the bottom with decls, program and env. Session.eval_expr committed the generic copies it had instantiated before emitting the module that carries them, which is the session believing it holds a body nothing was written for; that assignment moved below Emit. Both are pinned. test_session drives the two rollbacks in process, and test_dev drives a real daemon whose macro module cannot be built — the expression path and the redefinition path, each followed by the same evaluation succeeding and by the session still knowing the program.
29 lines
1.2 KiB
Plaintext
29 lines
1.2 KiB
Plaintext
;;;; A program to send failing evaluations at, for as long as it takes.
|
|
;;;;
|
|
;;;; Same shape as dev-repl.flan and for the same reason — C-x C-e is a thunk
|
|
;;;; the agent runs at a frame boundary, so a program under test has to keep
|
|
;;;; reaching them — but with room to spare. What test_dev.ml drives here is
|
|
;;;; the *failing* path, and a failure in this loop costs a clang driver that a
|
|
;;;; success does not: the macro module is built from scratch, thrown away, and
|
|
;;;; built again. dev-repl.flan's 4000 frames are twenty seconds, which is less
|
|
;;;; than that sequence takes on a cold cache, and a program that ran out mid
|
|
;;;; test would look exactly like the session death the test is here to deny.
|
|
;;;;
|
|
;;;; Two minutes, then, against a block that runs in well under one: enough
|
|
;;;; margin for a cold machine, and short enough that an aborted run does not
|
|
;;;; leave a process of this behind for the rest of the afternoon.
|
|
(import agent "vendor:agent")
|
|
|
|
(defvar ticks i64)
|
|
|
|
(defn step [] i64
|
|
(set ticks (+ ticks 1))
|
|
ticks)
|
|
|
|
(defn main [] i32
|
|
(agent/start "/tmp/flan-dev-robust-fallback.sock")
|
|
(dotimes [i 24000]
|
|
(agent/wait 5)
|
|
(set ticks (step)))
|
|
0)
|