EvalTask
One eval scheduled on a lane. Compilation runs OFF the tick (pure computation) on a short-lived thread; once compiled the task enters the lane and is EXECUTED ON THE TICK THREAD by the pump — so the snippet runs with the real server/client/render thread identity (ensureOnSameThread passes), not a surrogate thread.
Cross-tick work is native Kotlin: a snippet whose value is an iterator { ... yield() ... } hands back an IterEval, and the pump advances that iterator one element per tick on the tick thread, so yield() suspends the coroutine and resumes it on that same thread. println and whatever yield renders both accumulate in out, which is returned when the iterator is exhausted.
The result is an Outcome (text + isError), so every way an eval can end — clean finish, runtime throw, compile failure, cancellation, reap — reports the right tools/call isError AND keeps whatever was printed before it ended. Partial output is never dropped.
Constructors
Properties
Which timeout kill-id field this lane's scripts read (baked in at compile), or null for no guard at all — the off-tick ParallelLane, which has no watchdog.
This eval's token, baked into its woven guard so only THIS eval's code answers to a kill. 1-based, which is what leaves 0 free as TimeoutGuard's "nobody".
The dedicated worker thread driving this eval off-tick (ParallelLane); null for the heartbeat lanes, whose evals run on the (never-interruptible) tick thread. Set once, before the worker starts.
Functions
Publish the dedicated off-tick worker before it starts, so a cancel racing the first step has a thread to interrupt. Only ParallelLane calls this; the heartbeat lanes leave worker null.
The step threw and no inner guard owned it — in practice EvalRender.stack itself failing, since every script throw is answered before this. Completing the future is the whole job; the alternative is a caller blocked on it forever. So NOTHING here may throw: the render is deferred onto that caller, which unlike a tick thread has somewhere to put a second failure. Here rather than in the driver because out is private, and no ending may drop it.
Normal end, called by the driver once pumpStep reports done. A no-op after a kill, cancel, fail or reportTimeout: those complete the future directly and leave finalResult null.
The scriptguard threw into the step that just ran and nothing reported it, so something swallowed it: the run carried on past an interruption and whatever it produced means nothing. End the eval with the watchdog's report, keeping what it had printed. Not always a swallow, though — a blocking call (Thread.sleep, a lock, IO) gives the guard no point to fire at, so the step can return intact; that value is dropped all the same, on purpose — handing it back is the one answer that leaves the caller no way to learn the tick stood frozen for the whole step.
Captured output so far + a terminal tail marker, so a reap or cancel returns the same shape the normal-finish and mid-drive-throw paths do. Exactly one of those paths ever runs: the loser's deferred text supplier is never invoked, because completing an already-done future is a no-op.