History

State the real launch-order guarantee in the fork comment

Rhys Sullivan committed 2 months ago

Commit 6788da01 parent b8edd4d1 file · +22 −17Browse files

Unified · Split

packages/core/sdk/src/executor.ts+22−17
@@ -5816,23 +5816,28 @@ export const createExecutor = <const TPlugins extends readonly AnyPlugin[] = rea · 5815 unmodified lines
58165816 // handler at construction), so an abandoned read cannot
58175817 // unhandled-reject; a fiber interrupted before it ever ran issues no
58185818 // read at all.
5819− // Launch order is dominant-first by construction, best-effort under
5820− // the scheduler. The dominant tool-row read is forked FIRST with
5821− // `startImmediately: true`: an immediate fork evaluates the child
5822− // INLINE (`forkUnsafe` calls `child.evaluate` on the spot), so the
5823− // read is normally issued before the speculative forks below are
5824− // even scheduled (a plain fork only queues its child on the
5825− // dispatcher for the next tick). The one exception is deliberate: if
5826− // the child's own operation budget (`MaxOpsBeforeYield`) expires
5827− // inside the launch window, the inline segment parks and a
5828− // speculative read may launch first. That reversal is materially
5829− // free, so it is NOT suppressed: on a synchronous driver the reads
5830− // serialize whichever order they start (same wall time), and on an
5831− // asynchronous driver every read suspends at its first await and
5832− // they overlap regardless of order. Suppressing the yield (a
5833− // fiber-lifetime `PreventSchedulerYield`) is NOT an option here —
5834− // the ref is inherited by everything the child runs and provably
5835− // keeps effect timeouts from firing across CPU-bound stretches.
5819+ // Launch order is dominant-first. The dominant tool-row read is
5820+ // forked FIRST with `startImmediately: true`: an immediate fork
5821+ // evaluates the child INLINE (`forkUnsafe` calls `child.evaluate` on
5822+ // the spot), and a forked fiber enters its run loop with a FRESH
5823+ // operation count (`runLoop` zeroes `currentOpCount`), so the few
5824+ // dozen operations between fork and the driver-promise suspension
5825+ // cannot reach the cooperative-yield budget (`MaxOpsBeforeYield`
5826+ // defaults to 2048) — the read is issued before the speculative
5827+ // forks below are even scheduled (a plain fork only queues its child
5828+ // on the dispatcher for the next tick). Dominant-first matters
5829+ // beyond taste: cloud's postgres pool is `max: 1`, so queries
5830+ // pipeline through one connection in issue order — a speculative
5831+ // query issued first would sit ahead of the read every branch needs,
5832+ // and a slow or lock-blocked speculative query would gate it. Under
5833+ // a pathologically small budget override (single digits — a test
5834+ // harness setting; 1-2 deadlocks the effect run loop itself) the
5835+ // inline launch can park early and a speculative read may issue
5836+ // first; that bounded case is accepted rather than suppressed,
5837+ // because the only known suppression (a fiber-lifetime
5838+ // `PreventSchedulerYield`) is inherited by everything the child runs
5839+ // and provably keeps effect timeouts from firing across CPU-bound
5840+ // stretches.
58365841 const toolRowFiber = yield* Effect.forkChild(
58375842 core.findFirst("tool", {
58385843 where: (b: AnyCb) =>