feat: fibers inherit the spawn-time context (Phase 0 of Io unification)
A fiber body previously ran under the static `__sx_default_context`: the
`abi(.c)` `fib_dispatch` frame has no implicit context param, so a
`push Context { … }` around `spawn` was invisible inside the fiber. That
makes it impossible to fold a fiber scheduler behind `context.io` — a
worker's `context.io.*` would resolve to the blocking default, not the
scheduler that spawned it.
`Scheduler.spawn` now snapshots the live `context` into `Fiber.dctx`, and
`fib_dispatch` re-pushes it (`push self.dctx { self.body() }`) around the
body. So a capability installed before `spawn` (allocator, io, data) is
visible to the worker, and a worker spawned under `push Context { io = … }`
sees that `io` as `context.io`.
Behavior-preserving for fibers spawned under the default context (the
snapshot just re-pushes that same default — full suite green). Context is
parameter-threaded per fiber stack, so interleaved fibers with different
contexts don't leak across the `swap_context` (verified: two fibers with
distinct `context.data` each keep their own across a `yield_now`).
Locks: examples/concurrency/1822-concurrency-fiber-context-inherit.sx
(byte-identical aarch64-macOS host + aarch64-linux container).
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
// Stream B2/A1 — a fiber INHERITS the dynamic `context` in force when it was
|
||||
// spawned. Previously a fiber body ran under the static `__sx_default_context`
|
||||
// (the `abi(.c)` `fib_dispatch` dropped the implicit context), so a
|
||||
// `push Context { … }` around `spawn` was invisible inside the fiber. Now
|
||||
// `Scheduler.spawn` snapshots `context` into the fiber and `fib_dispatch`
|
||||
// re-pushes it around the body — so a capability installed before `spawn`
|
||||
// (here a marker in `context.data`) is visible to the worker.
|
||||
//
|
||||
// This is the foundation for folding a fiber scheduler behind `context.io`: a
|
||||
// worker's `context.io.*` must resolve to the scheduler that spawned it, not the
|
||||
// blocking default. Behavior-preserving for fibers spawned under the default
|
||||
// context (the snapshot just re-pushes that same default).
|
||||
//
|
||||
// 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";
|
||||
|
||||
Marker :: struct { id: i64; }
|
||||
|
||||
main :: () -> i64 {
|
||||
mk := Marker.{ id = 7 };
|
||||
s := sched.Scheduler.init();
|
||||
ps := @s;
|
||||
print("outside: marker id = {}\n", mk.id);
|
||||
push Context.{ allocator = context.allocator, data = xx @mk, io = context.io } {
|
||||
ps.spawn(() => {
|
||||
m : *Marker = xx context.data; // inherited from the spawn-time context
|
||||
print("inside fiber: context.data marker id = {}\n", m.id);
|
||||
});
|
||||
ps.run();
|
||||
}
|
||||
print("done\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user