Chez concurrency pt.1: real OS-thread futures + blocking promises (shared heap)
future/future-call run the body on a native thread (fork-thread) over the SAME heap — JVM semantics, not Janet's isolated-heap snapshot. deref blocks on a mutex+condition latch; timed (deref f ms val) uses an absolute deadline. promise is a real blocking promise (deref parks until deliver), replacing the Janet non-blocking atom shim. future?/future-done?/future-cancelled?/future-cancel /realized? are native (the overlay versions read Janet map keys); re-asserted in post-prelude over the overlay. pmap/pcalls/pvalues (overlay, over future) light up for free. Thread-safety this forces: - atoms get a per-atom mutex; swap!/swap-vals! are a JVM-style CAS loop (f runs outside the lock, so a watch/validator can deref the same atom); reset!/ compare-and-set! are atomic. - the dynamic binding stack becomes a Chez thread-parameter, so each future/thread has its own; Chez inherits it at fork, giving binding conveyance (the shim also installs an explicit snapshot). - Thread/sleep really sleeps now (a worker sleeping doesn't block the parent). Re-minted the seed: future-call now resolves at compile time, so pmap compiles to a var-deref instead of the host-static-call fallback that crashed. image.ss unchanged. Corpus: the 2 snapshot cases now match the JVM (shared) not Janet (isolated) — allowlisted on both Chez gates; the two racy future-cancel cases allowlisted; "promise undelivered" (blocks on JVM/Chez, profile :bucket :timeout) skipped like :throws. Zero-Janet corpus 2544 -> 2569, 0 new divergences, floor raised. Full Janet gate + JVM cert green. jolt-byjr
This commit is contained in:
parent
a23095b502
commit
ec30c9e405
10 changed files with 330 additions and 42 deletions
|
|
@ -16,16 +16,21 @@
|
|||
;; Janet table, which a Chez atom record is not — so the peripheral ops + the
|
||||
;; notify/validate behaviour live natively here, and post-prelude.ss re-asserts
|
||||
;; them over the overlay's def-var! (jolt-mn9o).
|
||||
;; `lock` (jolt-byjr) is a per-atom mutex guarding the read-modify-write critical
|
||||
;; sections, so swap!/reset!/compare-and-set! are atomic under real OS threads
|
||||
;; (futures/go blocks share the heap on Chez). The user fn in swap! runs OUTSIDE
|
||||
;; the lock (a CAS retry loop, like the JVM) so it never deadlocks on re-entrant
|
||||
;; access and a watch/validator can deref the same atom.
|
||||
(define-record-type jolt-atom
|
||||
(fields (mutable val) (mutable watches) (mutable validator))
|
||||
(nongenerative jolt-atom-v2))
|
||||
(fields (mutable val) (mutable watches) (mutable validator) lock)
|
||||
(nongenerative jolt-atom-v3))
|
||||
|
||||
;; (atom init) / (atom init :validator f :meta m): scan the trailing keyword opts
|
||||
;; for :validator (the only one with runtime behaviour; :meta is accepted/ignored).
|
||||
(define (jolt-atom-new v . opts)
|
||||
(let loop ((o opts) (validator jolt-nil))
|
||||
(cond
|
||||
((or (null? o) (null? (cdr o))) (make-jolt-atom v '() validator))
|
||||
((or (null? o) (null? (cdr o))) (make-jolt-atom v '() validator (make-mutex)))
|
||||
((and (keyword-t? (car o)) (string=? (keyword-t-name (car o)) "validator"))
|
||||
(loop (cddr o) (cadr o)))
|
||||
(else (loop (cddr o) validator)))))
|
||||
|
|
@ -50,37 +55,57 @@
|
|||
((jolt-reduced? x) (jolt-reduced-val x))
|
||||
(else (error #f "deref: unsupported reference type" x))))
|
||||
|
||||
;; (swap! a f arg*) -> (reset! a (f @a arg*)); f is invoked through jolt-invoke.
|
||||
;; Validate the new value BEFORE storing, notify watches AFTER (the seed order).
|
||||
;; CAS the val from `old` to `nv` by identity (eq?), atomically. Returns #t on
|
||||
;; success. The compute step (f) runs outside this, so we re-check under the lock.
|
||||
(define (jolt-atom-cas! a old nv)
|
||||
(with-mutex (jolt-atom-lock a)
|
||||
(if (eq? (jolt-atom-val a) old)
|
||||
(begin (jolt-atom-val-set! a nv) #t)
|
||||
#f)))
|
||||
|
||||
;; (swap! a f arg*): JVM-style CAS loop — read, compute f OUTSIDE the lock, then
|
||||
;; atomically compare-and-set; retry if another thread changed it. Validate the
|
||||
;; new value before storing, notify watches after (the seed order).
|
||||
(define (jolt-swap! a f . args)
|
||||
(let* ((old (jolt-atom-val a))
|
||||
(nv (apply jolt-invoke f old args)))
|
||||
(jolt-atom-validate a nv)
|
||||
(jolt-atom-val-set! a nv)
|
||||
(jolt-atom-notify a old nv)
|
||||
nv))
|
||||
(let retry ()
|
||||
(let* ((old (jolt-atom-val a))
|
||||
(nv (apply jolt-invoke f old args)))
|
||||
(jolt-atom-validate a nv)
|
||||
(if (jolt-atom-cas! a old nv)
|
||||
(begin (jolt-atom-notify a old nv) nv)
|
||||
(retry)))))
|
||||
|
||||
(define (jolt-reset! a v)
|
||||
(let ((old (jolt-atom-val a)))
|
||||
(jolt-atom-validate a v)
|
||||
(jolt-atom-val-set! a v)
|
||||
(jolt-atom-validate a v)
|
||||
(let ((old (with-mutex (jolt-atom-lock a)
|
||||
(let ((o (jolt-atom-val a))) (jolt-atom-val-set! a v) o))))
|
||||
(jolt-atom-notify a old v)
|
||||
v))
|
||||
|
||||
;; compare-and-set! keeps jolt= (value) semantics, done atomically under the lock.
|
||||
(define (jolt-compare-and-set! a oldv newv)
|
||||
(if (jolt= (jolt-atom-val a) oldv)
|
||||
(begin (jolt-reset! a newv) #t)
|
||||
#f))
|
||||
(jolt-atom-validate a newv)
|
||||
(let ((swapped (with-mutex (jolt-atom-lock a)
|
||||
(if (jolt= (jolt-atom-val a) oldv)
|
||||
(begin (jolt-atom-val-set! a newv) #t)
|
||||
#f))))
|
||||
(when swapped (jolt-atom-notify a oldv newv))
|
||||
swapped))
|
||||
|
||||
(define (jolt-swap-vals! a f . args)
|
||||
(let* ((old (jolt-atom-val a))
|
||||
(nv (apply jolt-invoke f old args)))
|
||||
(jolt-reset! a nv)
|
||||
(jolt-vector old nv)))
|
||||
(let retry ()
|
||||
(let* ((old (jolt-atom-val a))
|
||||
(nv (apply jolt-invoke f old args)))
|
||||
(jolt-atom-validate a nv)
|
||||
(if (jolt-atom-cas! a old nv)
|
||||
(begin (jolt-atom-notify a old nv) (jolt-vector old nv))
|
||||
(retry)))))
|
||||
|
||||
(define (jolt-reset-vals! a v)
|
||||
(let ((old (jolt-atom-val a)))
|
||||
(jolt-reset! a v)
|
||||
(jolt-atom-validate a v)
|
||||
(let ((old (with-mutex (jolt-atom-lock a)
|
||||
(let ((o (jolt-atom-val a))) (jolt-atom-val-set! a v) o))))
|
||||
(jolt-atom-notify a old v)
|
||||
(jolt-vector old v)))
|
||||
|
||||
;; --- watches / validators (jolt-mn9o) ---------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue