feat(stdlib): Jobs, Promises & opt-in Sync concurrency (#14)
A layered concurrency library, safe by default. The recommended tier is Job.* / Promise.*: a Job is a future — Job.run(kind, arg) starts a cooperative background compute that advances each Job.pump(budget) and finishes after enough frames (heavy work spreads out instead of hitching), or Job.defer + Job.fulfill/fail/cancel drives one by hand. Poll with done/ok/failed/cancelled, read result/error, count outstanding work with Job.pending. Promise.all/race combine handle lists into a group job resolved on the main thread; Promise.count_done/all_done power a loading bar. The advanced, opt-in Sync.* tier (mutex/atomic/channel + cpu_count) is the "here be dragons" surface for engine-level message passing. The whole thing is a deterministic cooperative scheduler: results are collected on the main thread and a Job never touches the ECS world, so lockstep and replays stay bit-exact — same jobs + same budget reproduce byte-for-byte on every target, and a preemptive OS-thread backend can slot behind this same API later. Ludic has no closures, so a Job carries a compute kind + int arg (or a hand-driven defer) rather than fn()->…, and Promise progress is polled rather than chained through then. Written in Ludic and spliced on demand (like Regex/Dict/Numeric): a program that never mentions Job.*/Promise.*/Sync.* compiles byte-identically and the C-free bootstrap fixpoint is untouched. New: runtime/native/jobs.ludic, emit_ns_call dispatch, parse-time splice, examples/library/jobs.ludic (31 self-asserting checks), 33 docs pages + inventory, changeset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
872f458cb2
commit
50ecb8472f
44 changed files with 25637 additions and 22806 deletions
7
docs/language/promise/_section.md
Normal file
7
docs/language/promise/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: promise
|
||||
title: Promise
|
||||
order: 38
|
||||
---
|
||||
|
||||
Combine several <code>Job</code> futures and resolve the group on the main thread. <code>Promise.all</code> succeeds once every member has, <code>Promise.race</code> once the first does; both return an ordinary job handle you poll like any other. For a loading screen, <code>Promise.count_done</code> over the same handles is the bar's numerator and <code>len</code> the denominator, and <code>Promise.all_done</code> is the ready check. Ludic has no closures, so progress is polled rather than chained through a <code>then</code> callback. Build the handle list with <code>new []int</code> + <code>push</code>. Spliced in only when a program mentions <code>Promise.*</code>.
|
||||
21
docs/language/promise/promise-all.md
Normal file
21
docs/language/promise/promise-all.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: promise-all
|
||||
name: Promise.all
|
||||
category: promise
|
||||
kind: namespace-method
|
||||
tokens: Promise.all
|
||||
sig: Promise.all(handles) -> Job
|
||||
tip: Succeeds when every member succeeds.
|
||||
order: 0
|
||||
ns: Promise
|
||||
member: all
|
||||
---
|
||||
|
||||
Succeeds when every member succeeds.
|
||||
|
||||
```ludic
|
||||
let hs = new []int
|
||||
push(hs, a)
|
||||
push(hs, b)
|
||||
let loaded = Promise.all(hs)
|
||||
```
|
||||
21
docs/language/promise/promise-all_done.md
Normal file
21
docs/language/promise/promise-all_done.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: promise-all_done
|
||||
name: Promise.all_done
|
||||
category: promise
|
||||
kind: namespace-method
|
||||
tokens: Promise.all_done
|
||||
sig: Promise.all_done(handles) -> bool
|
||||
tip: Have all members resolved?
|
||||
order: 3
|
||||
ns: Promise
|
||||
member: all_done
|
||||
---
|
||||
|
||||
Have all members resolved?
|
||||
|
||||
```ludic
|
||||
let hs = new []int
|
||||
push(hs, a)
|
||||
push(hs, b)
|
||||
let ready = Promise.all_done(hs)
|
||||
```
|
||||
21
docs/language/promise/promise-count_done.md
Normal file
21
docs/language/promise/promise-count_done.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: promise-count_done
|
||||
name: Promise.count_done
|
||||
category: promise
|
||||
kind: namespace-method
|
||||
tokens: Promise.count_done
|
||||
sig: Promise.count_done(handles) -> int
|
||||
tip: How many members have resolved.
|
||||
order: 2
|
||||
ns: Promise
|
||||
member: count_done
|
||||
---
|
||||
|
||||
How many members have resolved.
|
||||
|
||||
```ludic
|
||||
let hs = new []int
|
||||
push(hs, a)
|
||||
push(hs, b)
|
||||
let progress = Promise.count_done(hs)
|
||||
```
|
||||
21
docs/language/promise/promise-race.md
Normal file
21
docs/language/promise/promise-race.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
id: promise-race
|
||||
name: Promise.race
|
||||
category: promise
|
||||
kind: namespace-method
|
||||
tokens: Promise.race
|
||||
sig: Promise.race(handles) -> Job
|
||||
tip: Succeeds when the first member does.
|
||||
order: 1
|
||||
ns: Promise
|
||||
member: race
|
||||
---
|
||||
|
||||
Succeeds when the first member does.
|
||||
|
||||
```ludic
|
||||
let hs = new []int
|
||||
push(hs, a)
|
||||
push(hs, b)
|
||||
let first = Promise.race(hs)
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue