Files
sx/examples/1804-concurrency-context-snapshot.sx
agra bab4886346 fibers B1.1: per-fiber context root is library-only (no compiler change)
A fiber needs its own root Context (the spawner's snapshot), not the
ambient one. Probed whether that needs compiler support: it does not.
context is an implicit slot-0 *Context param (call-carried, rides the
callee's own stack) and push Context allocates on the caller frame —
never TLS, never re-read from the __sx_default_context global mid-stack.
So the spawn convention is pure library sx:

  snap := context;            // snapshot the spawner's context
  f := Fiber.{ root = snap }; // store it
  push f.root { entry(args) } // trampoline installs it as the fiber root

examples/1804-concurrency-context-snapshot.sx locks it: a trampoline
running under ambient ctx 99 installs a stored snapshot (42); the body
reads 42, and the push scope restores 99 on exit. No fiber runtime yet
(B1.3) — this proves the plumbing it builds on.

The design doc's "lower context as swappable indirection, never raw
TLS" guarded a non-problem — context was already param-carried.

Suite green (726/0).
2026-06-20 17:09:26 +03:00

49 lines
2.2 KiB
Plaintext

// Stream B1 (fibers) step B1.1 — per-fiber `context` root, via plain
// snapshot + `push` (NO compiler change — the substrate the fiber spawn relies on).
//
// `context` is an implicit `*Context` parameter (slot 0) threaded through every
// sx call, and `push Context` allocates the new context on the caller's stack
// frame — so a function's context is CALL-CARRIED and rides its own stack, never
// read from a global. That is exactly what a fiber needs: each fiber, on its own
// stack, reads its own root context.
//
// This locks the spawn convention end-to-end with ordinary language features:
// 1. capture the spawner's context as a value (`snap := context`)
// 2. store the snapshot in a struct (the stand-in `Fiber`)
// 3. a trampoline, running under a DIFFERENT ambient context, installs the
// fiber's stored root before entering the body (`push f.root { … }`)
// The body sees the snapshot (42), not the trampoline's ambient context (99),
// and the `push` scope restores the ambient context on exit. No fiber runtime
// exists yet (that is B1.3) — this proves the context plumbing it will build on.
#import "modules/std.sx";
// Stand-in for a Fiber: stores the spawner's snapshotted root Context.
Fiber :: struct { root: Context; }
// Reads this call's context sentinel (carried in `context.data` here for the
// test; a real fiber carries its allocator / io the same way).
sentinel :: () -> i64 { return xx context.data; }
// Trampoline: runs under its own ambient context, but installs the fiber's
// stored root before entering the fiber body.
run_fiber :: (f: *Fiber) -> i64 {
push f.root {
return sentinel();
}
}
main :: () {
snap := context;
snap.data = xx 42; // the spawner's sentinel
f := Fiber.{ root = snap }; // snapshot stored in the fiber
other := context;
other.data = xx 99; // a DIFFERENT ambient context
push other {
// Ambient is 99 here; the trampoline must install f.root (42).
print("fiber root: {}\n", run_fiber(@f));
// After run_fiber returns, this scope's ambient context is intact.
print("ambient after: {}\n", sentinel());
}
}