`context.io.async(worker)` / `await` now run over the `Io` PROTOCOL, so the
same code interleaves under the fiber scheduler or runs inline under the
blocking `CBlockingIo` — one async stack, reached purely through `context.io`.
- Protocol: `suspend_raw(park: *ParkToken)` (was by-value). A suspending impl
records the parked execution context into `park.handle` before parking, so a
cross-context `ready(park)` knows whom to resume; `Scheduler.suspend_raw`
writes `self.current`, `CBlockingIo` ignores it.
- io.sx async layer rewritten colorblind: `async` submits the worker through
`io.spawn_raw` (inline under blocking, a fiber under the scheduler) and returns
a HEAP `*Future($R)` the worker fills later; `await` suspends via `suspend_raw`
until ready, then returns/raises. The generic worker is bridged to spawn_raw's
raw `(*void)->void` entry via a monomorphic `ThunkBox` (a heap-boxed nullary
completion closure) — all genericity lives in the closure env. Workers are
nullary (inputs captured at the call site) because a variadic pack can't cross
the fiber boundary. `CBlockingIo.spawn_raw` now runs the worker inline.
- Migrated 1805/1806 to the nullary `*Future` form; retrofit 1822/1823 to the
`push .{ … }` partial-context literal (inherits allocator/data).
- The async machinery adds a few prelude types, shifting the type-name table —
40 `.ir` snapshots regenerated (no behavior change; only `.exit`/`.stdout`/
`.stderr` would signal that, and none changed).
Locked by examples/concurrency/1824 — two async tasks under the fiber Io, the
completion log proving deferral (1 2 then 10 20 then 123). Suite 829/0,
byte-identical aarch64-macOS host + aarch64-linux container.
43 lines
1.8 KiB
Plaintext
43 lines
1.8 KiB
Plaintext
// Stream B2/A1 — the M:1 fiber scheduler installed AS an `Io` capability vtable,
|
|
// driven entirely through `context.io`. `impl Io for Scheduler` (sched.sx) folds
|
|
// the scheduler behind the same `Io` protocol the blocking `CBlockingIo`
|
|
// implements, so the worker below reaches real suspension/timers through the
|
|
// PROTOCOL (`context.io.spawn_raw`/`arm_timer`/`suspend_raw`/`now_ms`) rather
|
|
// than bespoke scheduler methods — the foundation for a colorblind
|
|
// `async`/`await`/`race` that runs over whichever `Io` is installed.
|
|
//
|
|
// Two workers are spawned via `context.io.spawn_raw`; each arms a virtual-time
|
|
// timer and `suspend_raw`s until it fires. They resume in DEADLINE order (10 then
|
|
// 20), deterministic on the virtual clock — proving the protocol round-trips
|
|
// spawn → arm → suspend → ready → resume against the fiber engine. Phase 0 (fibers
|
|
// inherit the spawn-time context) is what lets the worker's own `context.io`
|
|
// resolve back to this scheduler.
|
|
//
|
|
// aarch64-pinned (the scheduler's per-arch asm): runs end-to-end on a matching
|
|
// host (macOS + linux), ir-only on a mismatch.
|
|
#import "modules/std.sx";
|
|
sched :: #import "modules/std/sched.sx";
|
|
|
|
// Worker entry: an sx (*void)->void fn, erased to *void by spawn_raw.
|
|
sleeper :: (arg: *void) {
|
|
n : *i64 = xx arg;
|
|
tok : ParkToken = .{ handle = null };
|
|
context.io.arm_timer(context.io.now_ms() + n.*, tok);
|
|
context.io.suspend_raw(@tok) catch {};
|
|
print("worker(sleep {}) resumed at now_ms = {}\n", n.*, context.io.now_ms());
|
|
}
|
|
|
|
main :: () -> i64 {
|
|
s := sched.Scheduler.init();
|
|
ps := @s;
|
|
d1 : i64 = 20;
|
|
d2 : i64 = 10;
|
|
push .{ io = xx s } {
|
|
context.io.spawn_raw(xx sleeper, xx @d1, .{});
|
|
context.io.spawn_raw(xx sleeper, xx @d2, .{});
|
|
ps.run();
|
|
}
|
|
print("final clock: {}ms\n", ps.now_ms());
|
|
return 0;
|
|
}
|