ReviewOS

home-lang/home

Blob I/O ownership: cancel poll-parked work during VM shutdown

#569
Closed chris opened this 9 days ago ·
9 days ago

Follow-up to #66, #465, and #468. The post-WorkTask audit found a distinct lifetime family in Blob read/write operations: a task can leave the shared work pool, register with the process-wide io.Loop poller, and later re-enter the work pool using the same intrusive task node.

The existing per-VM native work-pool barrier is correct for one-shot jobs that publish their owner-thread completion before leaving the barrier. Applying it directly to Blob I/O would be unsafe in both obvious forms:

  • Holding admission while a request is parked on a blocked FIFO/stdin/socket can make Worker teardown wait forever.
  • Releasing admission while the poller retains the request allows a later readiness callback to touch a destroyed VM.

The I/O request state machine and FileCloser callback swap provide useful primitives, but they do not by themselves define ownership across poll registration, readiness dispatch, work-pool re-entry, cancellation, and VM teardown.

Implement a per-VM shutdown protocol for poll-parkable Blob I/O:

  • register ownership before the first work-pool dispatch and close new admission during teardown;
  • synchronize poll registration/readiness/cancellation so exactly one path owns final cleanup;
  • cancel or detach parked requests without waiting indefinitely for external readiness;
  • close owned descriptors and release Blob/JS roots on the correct thread;
  • prevent work-pool re-entry and user callbacks after the VM stops;
  • preserve normal completion, error, partial-I/O, and retry behavior;
  • audit all Blob-backed read/write entry points that use this task family;
  • add bounded repeated-worker regressions with deliberately blocked reads/writes, exact admission/cancellation counters, natural cleanup, and no relaxed timeouts or memory thresholds.

This is a source-audited ownership gap, not a claimed reproduced use-after-free and not evidence of whole-suite parity. One-shot DNS and native node:zlib work use the existing barrier/cancellation protocol; generic queued task disposal remains tracked in #465.

9 days ago

Completed by b2d764a (fix(runtime): cancel parked Blob I/O on shutdown).

The port now gives Blob read/byte-write tasks VM-scoped ownership across work-pool hops and the dedicated process-wide I/O watcher. Shutdown closes pollable admission, drains active native hops, detaches parked registrations, closes owned descriptors, and releases Blob/promise state without re-entering a stopped VM. The watcher has an atomic queued/running/rerun handoff, a real platform waker/readiness probe, and safe Darwin EV_DELETE completion; EAGAIN now parks instead of spinning on an immutable syscall result.

Validation:

  • deterministic real-FIFO read/write Worker shutdown regression: 6 consecutive Debug processes plus fresh Debug and ReleaseFast installation-shaped runs
  • complete installation-shaped runtime aggregate: 68/68 in Debug and 68/68 in ReleaseFast
  • native gate: 1,827 passed / 19 skipped / 0 failed (zig build test -Dfilter=home_rt -j1)
  • unchanged upstream bun-file-read.test.ts, blob-write.test.ts, and worker_blob.test.ts: 16/16 in Debug and ReleaseFast
  • Zig formatting, diff check, and scoped pickier: clean (only 21 pre-existing markdown warnings in the long parity plan)

No assertion, workload, deadline, fixture, or skip was weakened. The broader audit found a distinct optimized regular-file Blob-to-Blob Bun.write gap, tracked separately in #676; whole-suite parity remains open under #66.

Sign in to comment on this issue.