ReviewOS

home-lang/home

Native MessagePort parity: synchronous receive during pending local close

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

Follow-up to #66, #461, and the asynchronous close-event implementation in #462. Node 24.18.0 still permits a queued local inbox value to be received synchronously immediately after port.close(), before the close event. Home currently marks the port detached and discards its local inbox synchronously, so the same read returns undefined. Both are empty after close-event completion.

Minimal control: create MessageChannel from node:worker_threads; port2.postMessage("queued"); port1.close(); receiveMessageOnPort(port1). Node returns {message:"queued"}; Home returns undefined. Existing Home message-presence tests explicitly describe the immediate Home behavior; they are not proof of Node parity for this timing window. The shared #462 close-event test checks inbox disposal after both asynchronous close events, where the contracts agree.

Implement the pending-local-close state logically, retaining accepted data only for the correct window, while preserving both-end exactly-once events, transfer restrictions, no later delivery after completion, and teardown/GC safety. Add real Node controls for repeated close and synchronous reads before/after notification. Do not weaken those tests or claim this gap resolved by only moving assertions.

9 days ago

Implementation is building after independent native ownership/contract review. Fifteen exact Node24.18 control cases pass in three fresh runs. The port keeps the native detached flag for transfer validation, but distinguishes pending local close from a transferred-away wrapper so only the former can synchronously read its retained inbox. Incoming peer data is accepted until completion; outgoing sends and asynchronous delivery are stopped immediately. Completion iteratively disposes leftovers; teardown/orphans have no pending window. Inactive senders still serialize and dispose transferred buffers/ports, including former peers/replacement endpoints, without closing the replacement through the old wrapper accidentally. Existing presence regression now positively checks its queued pending-close payload instead of asserting the old Home-only empty result. No native success claim until the new binary and adjacent leak/transfer tests pass. #465 remains an independently reproduced pending-task leak, with a new explicit failing regression.

9 days ago

The new binary now passes all 15 pending-close cases (three fresh native runs), the prior 13 close-event cases, and all 14 other native regression files. Build and unchanged rebuild are 13/13. The pipe and context-teardown suites also pass.

Three old closed-port RSS tests expose a fixture protocol conflict: they close the receiver, synchronously queue hundreds of MB, and measure without letting close complete. Their +333/+132/+132 MB is the intentionally retained pending inbox, which the independent Node controls require to remain readable. The Home ported leak fixture is being corrected to await bounded real close-event completion before measuring fully closed state, without changing payload sizes, iteration counts, memory thresholds, or assertions. The 20,000-deep cascade test will also await completion before declaring PASS. Original failures are recorded, not counted as passes. #465 remains a separately failing cancellation regression.

9 days ago

Implemented and verified locally in 00362c8 (Chris Breuer sole author and committer; no coauthors). Pending close preserves synchronous FIFO receive, including peer sends during the window, while outgoing delivery/asynchronous local delivery and transfer of the closing endpoint are disabled. Inactive senders still validate/dispose transfer lists, including former peers and replacement endpoints; old wrappers cannot read or close replacements accidentally. Completion and teardown retain iterative nested cleanup and GC protection without changing class ABI.

Verification: build + cached rebuild 13/13; all 16 native regression files pass. Fifteen new pending-close cases pass in three fresh Home runs and three exact Node24.18 control runs; previous13 close cases pass. Focused corpus:15 pass,3 upstream skips,0 failures /529 assertions across pipe/closed-port/context-teardown. Two exact FileHandle/transfer-terminate standalone fixtures pass with real exit cleanup. Generator/ABI8 pass/69assertions; Zighelper4pass.

The closed-port memory fixture now waits for bounded actual close events before measuring completed closure. Workloads, payloads, thresholds and outer assertions are unchanged. Original synchronous measurements retained legitimate pending data (+333.06/+132.28/+132.30MB), so those original runs remain recorded as failures. A sentinel keeps the sender open while the target finishes closing, preserving the closed-destination rejection path. The20,000-deep cascade now waits for its final peer close before PASS. External Bun sources were not edited.

#465 remains an independent real failure, preserved in worker-queued-transfer-close.pending.mjs with normal-delivery positive control. No full parity claim (#66/#461). Around0.75GiB of older owned native outputs removed, latest binary retained. Normal git push still fails DNS for github.com, so commit publication is pending and this issue remains open pending publication/review.

9 days ago

Publication gate is now satisfied. 00362c8a0 is reachable on current main and contains the pending-local-close receive window verified above, including FIFO reads before completion, sends during the window, transfer rejection, iterative disposal, and close-event completion. Generic queued-task ownership remains #465. Closing this bounded timing issue as completed.

Sign in to comment on this issue.