Repository navigation
Conversation
2b1c310 to
cde3bed
Compare
|
I'm closing this for now as to free up some CI resources. Reply from Slack on this PR: I looked at the forkable worlds work on replikativ/sci. Interesting idea. I measured the cost in the default :standard mode against 0.15.59 on the JVM, best of 3 isolated JVMs:
The closure numbers come from fns/interpreted-fn, which puts every fn object into a global synchronized WeakHashMap, also when forking is off. The conveyor slowdown comes from the apply and try/finally in binding-frame-fn. Even with those fixed I'm not sure I want to merge this in SCI. It is a lot of code that touches many parts of SCI, which is a lot to maintain for a feature I expect few users to turn on. For now I'm happy to point users who need this to your fork. If enough people ask for it, I may reconsider. |
Summary
This draft adds forkable SCI runtime worlds behind an explicit opt-in:
The default
:standardmode does not allocate a runtime world and retains SCI's direct Var, function, atom, delay, memoize, and namespace paths. In standard mode,sci/forkkeeps its historical namespace-environment behavior. Full live-state isolation is enabled only in forkable mode.This split is intended to make the feature usable by embeddings such as Spindel and Dvergr without imposing its indirections on babashka, nbb, or ordinary SCI users.
Forkable runtime
In forkable mode,
sci/forktakes a quiescent snapshot of interpreter-owned state while preserving stable SCI handles. Parent, child, siblings, and repeated descendants then evolve independently.The implementation uses dense lineage-local slots rather than a persistent-map lookup on every access. It covers:
Forkablecooperation protocolThe accompanying audit documents the ownership boundary. Arbitrary host closure graphs are not traversed, and external capabilities such as clocks, RNGs, I/O, and running futures require an embedding policy.
Suspended continuations
Forking a quiescent runtime does not implicitly copy a running continuation. Embeddings that suspend interpreted work can now do this explicitly:
The capsule is opaque. It captures the complete persistent dynamic-binding frame immediately, preserves binding-box aliases, gives each retarget an independent set of boxes, selects the target world during invocation, unwinds nested bindings correctly, and rejects unrelated SCI lineages. The explicit-context capture arity supports host-invoked interpreted functions after top-level evaluation has returned.
sci/with-detached-contextis the complementary host boundary for recursively constructing an independent interpreter without inheriting the caller's active world or binding frame.Interactive demo
Run a tree of forked REPL worlds with:
The demo supports
:fork NAME,:use ID, and:treealongside ordinary SCI forms. Each node shows inherited history and later divergence in functions and state created before the fork.Performance
These are local JVM median-of-medians across three fresh processes. Each process warmed up and took seven samples. The machine was held in its
powersaveprofile, so the relative comparison matters more than the absolute milliseconds.master:standard:forkableThe important result is that standard mode shows no regression in these hot-path probes. Forkable mode still pays a material 2.5-3.9x cost relative to standard mode depending on the operation; this draft exposes that tradeoff rather than making every embedding pay it.
Verification
This remains a draft for architectural discussion. The commits are intentionally split into reviewable checkpoints and can be decomposed into smaller PRs if that better fits SCI.
Closes #1083