feat: futures on real OS threads (ev/thread)

Implement clojure.core futures backed by Janet's ev/thread for genuine
parallelism (CPU-bound work can use a second core, unlike cooperative go
blocks):

- future / future-call, deref + (deref f timeout-ms timeout-val), future?,
  future-done?, future-cancel, future-cancelled?; realized? on futures.
- A worker OS thread computes and marshals back a [:ok v]/[:error e] result
  over a thread-chan; a parent-side collector fiber caches it and closes a
  broadcast latch so any number of deref-ers unpark.
- Snapshot semantics: separate heaps mean the body + captured state are copied
  to the worker and only the result is copied back (mutating a captured atom
  does not propagate). Documented in README.
- future-cancel can't interrupt a Janet OS thread, so it marks the future
  cancelled/done (deref throws, predicates flip) while the worker runs out.

clojure-test-suite baseline 3915 -> 3913: implementing future unskips
realized_qmark.cljc's (when-var-exists future ...) block, which depends on
JVM Thread/sleep + real thread interruption jolt can't provide; deref then
re-raises the unresolved-Thread/sleep error. Documented at the baseline.

Spec: test/spec/futures-spec.janet (18 cases).
This commit is contained in:
Yogthos 2026-06-05 20:00:02 -04:00
parent 120d6d73fa
commit 8be7743b26
4 changed files with 132 additions and 4 deletions

View file

@ -18,7 +18,14 @@
# Baseline: assertions Jolt currently passes across the suite. Raise as Jolt
# improves so a regression (previously-passing assertion breaking) is caught.
(def baseline-pass 3915)
# Lowered 3915 -> 3913 when futures landed: `realized?`/realized_qmark.cljc has a
# `(when-var-exists future ...)` block that was skipped while `future` was
# unresolved. With futures implemented the block now runs, but it depends on JVM
# `Thread/sleep` (jolt has no JVM interop) and on `future-cancel` interrupting a
# running thread (Janet OS threads can't be interrupted), so `(deref (future
# (sleep 1)))` re-raises the unresolved-`Thread/sleep` error — a documented
# platform gap, not a regression in any previously-working behavior.
(def baseline-pass 3913)
# A file is "clean" when it ran with zero failures AND zero errors.
(def baseline-clean-files 45)
# Per-file wall-clock budget (seconds). Normal files finish in well under 1s;