feat: core.async Phase 2 — dynamic var binding conveyance

Jolt's dynamic-var binding stack was a single global array, so concurrent go
blocks interleaved each other's bindings and a go block didn't see the bindings
in effect when it was spawned.

Move the binding stack into Janet's fiber-local dyn (:jolt/binding-stack): each
fiber (go block) lazily gets its own array, so bindings can't interleave. Janet
ev/go fibers inherit the parent's dyn, but go-spawn now snapshots the binding
stack at spawn time and installs a private copy in the new fiber — Clojure
binding conveyance. A go block's own (binding ...) shadows the conveyed frame.

types.janet: cur-binding-stack / snapshot-bindings / install-bindings; var-get/
var-set/push/pop use the fiber-local stack. async.janet: go-spawn conveys.

spec: core.async/binding-conveyance (4 cases — conveyance, isolation, no leak to
root, inner shadowing). jpm test green.
This commit is contained in:
Yogthos 2026-06-05 15:22:38 -04:00
parent 7d1e1f42e1
commit e8cb4605ac
4 changed files with 52 additions and 9 deletions

View file

@ -53,3 +53,19 @@
"99" (a "(<! (go (try (<! (go 99)) (catch :default e -1))))")]
["<! inside a nested fn called in a go"
"7" (a "(def c (chan)) (go (>! c 7)) (<! (go ((fn [] (<! c)))))")])
# Dynamic-var binding conveyance (Phase 2): a go block sees the dynamic bindings
# in effect when it was spawned, concurrent go blocks don't interleave, and a
# go block's own binding shadows the conveyed one.
(defn- d [body] (string "(do " REQ "(def ^:dynamic *x* 0) " body ")"))
(defspec "core.async / binding conveyance"
["go conveys the binding"
"10" (d "(<! (binding [*x* 10] (go (<! (timeout 5)) *x*)))")]
["concurrent go blocks isolated"
"[:a :b]"
(d "(def ra (binding [*x* :a] (go (<! (timeout 20)) *x*))) (def rb (binding [*x* :b] (go (<! (timeout 5)) *x*))) [(<! ra) (<! rb)]")]
["binding doesn't leak to root"
"0" (d "(<! (binding [*x* 99] (go (<! (timeout 5))))) *x*")]
["go's own binding shadows conveyed"
":inner"
(d "(<! (binding [*x* :outer] (go (binding [*x* :inner] (<! (timeout 5)) *x*))))")])