spec-conditions.md §2, and the reason the transfer was worth building. An unhandled error runs a hook instead of rt_die(), on the frame that erred with nothing unwound, lists the restarts between there and the top, and waits. A hook rather than a direct call because the loop lives in vendor/agent, which is an optional package, and flan_rt.c is the release runtime - a program with no agent leaves it null and dies the way it always did. The hook resumes by writing a restart into the transfer channel, which is the channel an invoke-restart writes and reaches the same guard, so choosing from the break loop and choosing from a handler are one act lowered once. §6 needed no change. The break loop is the poll loop, run from the error rather than from the frame boundary. That is load-bearing: an expression evaluated while stopped is a module the listener queues and the game thread runs, so a loop that did not drain that queue would hang C-x C-e exactly when it is wanted most. Installing while stopped is allowed, which contradicts the rule that a redefined function must not be swapped while it is on the stack - that rule is about mid-frame consistency and there is no frame in progress here. The old body keeps running and a retry reaches the new one through the cell, which is the whole point. A restart frame carries its name now, beside the hash. Matching never needs it; showing someone their choices does, and nothing at run time can turn a hash back into a name. A choice is checked on the listener thread against a stack the stopped game thread is holding still. Answering ok and finding out on the game thread that nothing offers that name would report success for something that cannot happen. The test errors twice and takes a different restart each time, so a loop that always resumed the same way fails it.
22 lines
699 B
Plaintext
22 lines
699 B
Plaintext
;;;; The dev-build break loop — spec-conditions.md §2.
|
|
;;;;
|
|
;;;; An unhandled error used to kill the program. Here it stops instead, on the
|
|
;;;; frame that erred with nothing unwound, and waits for someone to pick a
|
|
;;;; restart. Two of them, and the run takes a different one each time, so a
|
|
;;;; break loop that always resumed the same way could not pass.
|
|
(import agent "vendor:agent")
|
|
|
|
(defstruct Missing [id i32])
|
|
|
|
(defn fetch [n i32] i32
|
|
(restart-case
|
|
(do (error (Missing {:id n})) 0)
|
|
(use-placeholder [] -1)
|
|
(retry [] 7)))
|
|
|
|
(defn main [] i32
|
|
(agent/start "/tmp/flan-break.sock")
|
|
(print-i64 (i64 (fetch 1))) (newline)
|
|
(print-i64 (i64 (fetch 2))) (newline)
|
|
0)
|