Skip to content

Fire the close event on MessagePort - #32565

Closed
robobun wants to merge 9 commits into
mainfrom
farm/ac509d10/messageport-close-event
Closed

Fire the close event on MessagePort#32565
robobun wants to merge 9 commits into
mainfrom
farm/ac509d10/messageport-close-event

MessagePort: fix close-event wrapper leaks found in review

c76e234
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Jun 21, 2026 in 20m 24s

Code review found 1 important issue

Found 5 candidates, confirmed 2. See review comments for details.

Details

Severity Count
🔴 Important 1
🟡 Nit 1
🟣 Pre-existing 0
Severity File:Line Issue
🔴 Important src/jsc/bindings/webcore/MessagePortPipe.cpp:159-169 Close-only port with buffered messages: close never fires and wrapper leaks forever
🟡 Nit src/jsc/bindings/webcore/MessagePort.cpp:192-207 Closing port's own 'close' skipped when listener added after close()

Annotations

Check failure on line 169 in src/jsc/bindings/webcore/MessagePortPipe.cpp

See this annotation in the file changed.

@claude claude / Claude Code Review

Close-only port with buffered messages: close never fires and wrapper leaks forever

A close-only port whose peer posts a message and then closes never receives `close`, and its wrapper is pinned forever. The drain hits the new `!port->isListeningForMessages()` branch, clears `DrainScheduled` and breaks without setting `emptied`, so `dispatchCloseEventFromPeer()` is never called and nothing ever reschedules a drain — yet `hasPendingActivity()` returns `true` permanently via `m_hasCloseEventListener && !m_closeEventDispatched && (Attached && !isOtherSideOpen)`. This is a wrapper-

Check warning on line 207 in src/jsc/bindings/webcore/MessagePort.cpp

See this annotation in the file changed.

@claude claude / Claude Code Review

Closing port's own 'close' skipped when listener added after close()

Node-compat gap: `port.close(); port.on('close', cb)` never fires on the closing port, because `scheduleCloseEvent()` returns false when `!m_hasCloseEventListener` and the fallthrough then sets `m_closeEventDispatched = true`. Node and the HTML spec both queue the close task unconditionally, so a listener added in the same tick still receives it. The leak-safe fix is to drop the `!m_hasCloseEventListener` guard and always post the task — `dispatchCloseEventSelf()` already sets `m_closeEventDispa