feat(tasks): #408 increment 1a — task_spawn + task_alive foundation#472
Merged
Conversation
The foundational, lowest-risk slice of the deterministic cooperative task layer (design + prior-art research + memory pass all on issue #408): the Task struct, HANDLE_TASK, task_spawn/task_alive, and leak-clean teardown. The copying-stack scheduler that actually runs and interleaves tasks — the vm_run suspend/resume surgery, task_yield/task_join — is the next increment (1b); this slice validates the table, args deep-copy, held-ref lifetime, and teardown independently before the delicate core-VM work. - Task (vm.h): full struct incl. the copying-stack save-buffer fields (unused until 1b) so 1b doesn't reshape it. TaskState enum. - HANDLE_TASK reuses the id-keyed handle table + handle_table_drain, the same deterministic-teardown path channels/threads use. - task_spawn: deep-copies args via val_clone_for_send (share-nothing at the boundary, unlike spawn's by-reference threads), holds the entry closure, returns a numeric task id. Non-callable target → type_mismatch (#406). - task_alive: 1 while READY/RUNNING/SUSPENDED, 0 on DONE/DEAD/unknown id. - Single OS thread: never flips g_vm_multithreaded, so the JIT and the non-atomic refcount fast paths stay live (the #297-cliff-avoidance the cooperative model is for). - GC: held refs need NO root registration — the trial-deletion collector (Bacon-Rajan) already keeps them live because a counted ref exceeds the internal-edge count within its candidate set U, exactly as ThreadHandle ->fn does. (This also de-risks 1b: save-buffer refs auto-protect if ownership moves with the memcpy'd bytes.) - No trace records: scheduling order is a pure function of program order, not host nondeterminism (the deterministic-by-construction ruling) — so the builtins are deliberately NOT TRACE_NONDET_RET-wrapped. Gates: release 2610/2610, ASan+UBSan detect_leaks=1 2610/2610 tally 0 (new test_tasks.eigs exercises unrun tasks holding a closure cycle + list args → reclaimed clean at teardown), doc-drift + freestanding green. Refs #408 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #408 (increment 1a — the layer is multi-increment; this is the foundation, not the whole feature).
What
The lowest-risk foundational slice of the deterministic cooperative task layer: the
Taskstruct,HANDLE_TASK,task_spawn/task_alive, and leak-clean teardown. No suspend machinery yet — the copying-stack scheduler that runs and interleaves tasks (thevm_runsuspend/resume surgery,task_yield/task_join) is increment 1b, deliberately split out as focused core-VM work. This slice validates the table, args deep-copy, held-ref lifetime, and teardown independently first.Full design trail on the issue: two design comments, two adversarially-verified prior-art research rounds, a code-grounded memory-design pass, and the implementation blueprint.
Design decisions realized here
g_vm_multithreaded, so the JIT and non-atomic refcount fast paths stay live (the whole point of the cooperative model vsspawn's Data races: parallel workers executing the same shared chunk (exec_count, JIT counters, inline caches, shared-env refcounts) #297 cliff).task_spawndeep-copies args viaval_clone_for_send, unlikespawn's by-reference threads.handle_table_drain— the same deterministic-teardown path channels/threads already use.TRACE_NONDET_RET-wrapped. Documented so a reviewer doesn't "fix" the missing wrap.U, exactly asThreadHandle->fndoes. (This finding also de-risks 1b — save-buffer refs auto-protect if ownership moves with the memcpy'd bytes.)Verification
detect_leaks=12610/2610, leak tally 0test_tasks.eigs: spawn returns an id, alive/unknown-id, args form, distinct ids, non-callable →type_mismatch, compound-arg hold — and the ASan half proves an unrun task holding a closure cycle + list args is reclaimed clean at teardowntask_spawn/task_alivedocumented in BUILTINS.md (the CI gate: every builtin and public stdlib function must be documented (regex_* currently appear nowhere) #393 discoverability gate)Not in this PR (increment 1b)
The
vm_runvm_suspend_haltlabel +vm_run_resumere-entry +scheduler_driveabovevm_execute+ realtask_yield/task_join+ the two planted-fault validations (arena guard, save-buffer lifetime). Blueprint is on the issue.🤖 Generated with Claude Code