The trio the author decided on 2026-09-20 is now all built: def is CL's defparameter — its initialiser runs on every daemon re-run, unguarded, so an edited initialiser repaints the same storage on C-c C-c plus re-run — defonce (Clojure's name for CL's defvar, per the author) initialises once behind the .init~once. flag, and defconst stays the image. One parse arm reads both forms; the difference is Ast.reinit, carried to Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and Check.check_global lifts every def initialiser — zero and literal included — into global/<n>, so the host's startup reaches it through the function cell and a re-evaluated def swaps it (Session's def_inits; Emit.redefinition declares the cell for a non-sibling target). The old defvar spelling is refused with the rename and both compiling spellings, and every program, test, doc and editor list is swept — except sand.flan, the author's live WIP, whose seven defvar lines are flagged in FIX.org and keep its three dependent tests red on this branch.
69 lines
3.1 KiB
Plaintext
69 lines
3.1 KiB
Plaintext
;;;; Allocators — spec-memory.md, "Allocators". No container here: this is the
|
|
;;;; tier on its own, so that a failure in it is not read as a Vec bug.
|
|
;;;;
|
|
;;;; What is asserted: the capability set is readable at run time and differs
|
|
;;;; per allocator; with-allocator rebinds for its dynamic extent and restores
|
|
;;;; afterwards, including out of a call and out of a transfer; free-all is
|
|
;;;; retain-capacity and bumps the epoch anyway; and nothing is released at
|
|
;;;; scope exit, which is the point the spec is most emphatic about.
|
|
|
|
;; A zeroed Allocator. A global rather than a local because the arena has to
|
|
;; outlive the frame that makes it, and because a handler cannot see a local
|
|
;; (check.ml's `captured` says so by name).
|
|
(defonce frame Allocator)
|
|
|
|
;;; The context is a dynamic variable, so a function called from inside a
|
|
;;; with-allocator body sees the rebinding without anything being passed.
|
|
(defn who-am-i [] bool
|
|
(can-free? context/allocator))
|
|
|
|
(defn main [] i32
|
|
;; The heap allocator frees one block; an arena does not. That is Odin's
|
|
;; answer too — its arena returns Mode_Not_Implemented for .Free — and it is
|
|
;; the capability spec-memory.md calls load-bearing.
|
|
(set frame (arena-new 1024))
|
|
(println (can-free? (heap-allocator))) ; true
|
|
(println (can-free? frame)) ; false
|
|
(println (can-free-all? (heap-allocator))) ; false
|
|
(println (can-free-all? frame)) ; true
|
|
|
|
;; The default context is the heap allocator, and context/temp is its own
|
|
;; arena — the per-frame tier, distinct from it.
|
|
(println (can-free? context/allocator)) ; true
|
|
(println (can-free? context/temp)) ; false
|
|
|
|
;; with-allocator rebinds for the dynamic extent, so a call made from inside
|
|
;; the body sees the arena, and the binding is gone after the body.
|
|
(println (with-allocator frame (who-am-i))) ; false
|
|
(println (who-am-i)) ; true
|
|
|
|
;; ... and it is an expression: the body's last value is the form's value.
|
|
(println (with-allocator frame 41)) ; 41
|
|
|
|
;; free-all is retain-capacity: the pages stay, the epoch moves. Both halves
|
|
;; matter — the first is what makes a per-frame reset free, and the second is
|
|
;; what a container's dev trap reads.
|
|
(println (alloc-epoch frame)) ; 0
|
|
(free-all frame)
|
|
(println (alloc-epoch frame)) ; 1
|
|
(free-all frame)
|
|
(println (alloc-epoch frame)) ; 2
|
|
|
|
;; Nothing is released at scope exit — not at the end of a let, not at the
|
|
;; end of a with-allocator body. The epoch is the observable proof: leaving
|
|
;; the body did not release the region it named.
|
|
(let [before (alloc-epoch frame)]
|
|
(with-allocator frame (println (alloc-epoch frame))) ; 2
|
|
(println (= before (alloc-epoch frame)))) ; true
|
|
|
|
;; And out of a transfer. The restart-case's clause runs after the body has
|
|
;; left through the pad, so the context allocator here is the one the
|
|
;; with-allocator displaced, not the arena.
|
|
(println
|
|
(restart-case
|
|
(with-allocator frame (invoke-restart 'resync))
|
|
(resync [] (can-free? context/allocator)))) ; true
|
|
|
|
(arena-destroy frame)
|
|
0)
|