Six refusals in the runtime called _exit(134) where every other error had learned to park: no restart by that name, a restart taken with the wrong arguments or with none, a defer that invoked one, a null allocator, and free-all on something with no region. Under a merged flan dev the compiler is in that process, so a program that named a restart nobody established took the session down with it, which is the one thing the break loop exists to prevent. They park now. Not through flan_break_hook, which is what bounds and arithmetic use: that hook may answer by aiming a transfer channel, and these six are called by emitted code that falls off the end with no channel anywhere in the call, so a restart chosen against one would be accepted and dropped. flan_trap_hook says the other thing instead — stop here, let everything be read, and refuse the resume with a reason. All six park, for two reasons rather than one. Four are guards that fire before the operation they guard, so nothing is half done and the frame reads like any other. The other two fire mid-transfer, with the frame's defers possibly half run, and they park only to be looked at: stopping on a torn unwind is strictly more than exiting before anyone can ask what tore it. The break loop grew a per-snapshot resumable flag for it. Restarts are still listed and still numbered, the terminal marks them untakeable and the socket reports the same positions as unreachable, and the listener refuses a choice with the trap's own sentence rather than the thunk-boundary one. Standalone builds die exactly as they did: nothing installs the hook in a program that did not import the agent, and the acceptance case for free-all still wants exit 134 and the same message. The review entry that asked for this named flan_exit_hook, which is normal termination and not this at all; it is struck out with the correction.
35 lines
1.6 KiB
Plaintext
35 lines
1.6 KiB
Plaintext
;;;; A program that stops on a trap it cannot be resumed from, for driving the
|
|
;;;; break loop over one.
|
|
;;;;
|
|
;;;; dev-break-bounds.flan is the case where stopping and *resuming* both work:
|
|
;;;; a bad index signals BoundsError, the walk finds nothing, and the break
|
|
;;;; loop hands the program back to its own `continue`. This is the other half.
|
|
;;;; `free-all` on an allocator that has no region to release is a refusal the
|
|
;;;; emitted code makes no channel for — it calls the trap and falls off the
|
|
;;;; end — so there is nothing for a chosen restart to transfer into, and until
|
|
;;;; now that meant `_exit(134)`, which under a merged `flan dev` is the
|
|
;;;; session and the compiler as well as the program.
|
|
;;;;
|
|
;;;; The claim is that the session survives it anyway. The program stops where
|
|
;;;; it erred, everything is readable, and the *resume* is the only thing
|
|
;;;; refused — with a sentence saying why, rather than by the process being
|
|
;;;; gone before anyone could ask.
|
|
;;;;
|
|
;;;; The `restart-case` is load-bearing and not scenery: with no restart on the
|
|
;;;; stack the break loop would say "no restarts are active" and the refusal
|
|
;;;; that had to be written would never run. `continue` is live, is listed, and
|
|
;;;; is still not takeable — which is exactly the state this trap leaves a
|
|
;;;; program in.
|
|
(import agent "vendor:agent")
|
|
|
|
(defn release [] ()
|
|
;; The heap allocator frees one block and owns no region, so this traps.
|
|
(free-all (heap-allocator)))
|
|
|
|
(defn main [] i32
|
|
(agent/start "/tmp/flan-dev-trap-free-all-fallback.sock")
|
|
(restart-case
|
|
(release)
|
|
(continue [] (println "resumed")))
|
|
0)
|