feat: impl Io for Scheduler — fiber scheduler as a context.io vtable (Phase 1)
`impl Io for Scheduler` folds the M:1 fiber scheduler behind the same `Io`
protocol (core.sx) the blocking `CBlockingIo` implements, so the async layer
can run colorblind over whichever impl is installed via
`push Context { io = xx scheduler }`. The six methods are thin adapters over
the existing fiber primitives:
- `spawn_raw(entry, arg, opts)` — spawn a fiber that calls the erased
`(*void)->void` worker thunk `entry(arg)` (fn-ptr round-trip through
`*void`); returns the `*Fiber` handle. The fiber inherits this context
(Phase 0), so the worker's own `context.io` is this scheduler.
- `suspend_raw(park) -> !` — park the running fiber; the `!` is the
cancellation channel a suspending impl raises on (wired in Phase 3).
- `ready(park)` — `wake` the fiber recorded in the token (guarded on
`.suspended`).
- `poll(deadline_ms)` — one step of the run loop (drain ready + fire the
earliest virtual-time timer); fd-readiness stays on `run`.
- `now_ms` — the deterministic virtual clock.
- `arm_timer(deadline_ms, park)` — arm a virtual-time timer that re-readies
the current fiber.
Locked by examples/concurrency/1823 — two workers spawned + suspended +
resumed entirely through `context.io`, deterministic deadline order
(byte-identical aarch64-macOS host + aarch64-linux container). Full suite
green (828/0).
This commit is contained in:
42
examples/concurrency/1823-concurrency-fiber-io-vtable.sx
Normal file
42
examples/concurrency/1823-concurrency-fiber-io-vtable.sx
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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 Context.{ allocator = context.allocator, data = null, 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;
|
||||
}
|
||||
Reference in New Issue
Block a user