Skip to content

File: keep the FileReader listener snapshot rooted while dispatching - #248

Open
matthargett wants to merge 1 commit into
BabylonJS:mainfrom
rebeckerspecialties:filereader-dispatch-snapshot
Open

matthargett wants to merge 1 commit into
BabylonJS:mainfrom
rebeckerspecialties:filereader-dispatch-snapshot

Conversation

@matthargett

@matthargett matthargett commented Sep 16, 2026

Copy link
Copy Markdown

Problem

FileReader::Dispatch snapshots the listener list as std::vector<Napi::Function> — bare napi_values — before calling each listener. If a listener removes a later listener (removeEventListener inside a handler), that later function's only strong reference (its FunctionReference in m_eventHandlerRefs) goes away mid-dispatch. On JavaScriptCore nothing else roots a value that lives only on the C++ heap — the backend's handle scopes are stubs and it scans just the C stack — so a collection triggered by the running handler can reclaim the function before the loop reaches it. Same class of bug as the CompressionStream output-chunk corruption on #211 (reproducible there with JSC_collectContinuously=1).

Change

Snapshot Napi::FunctionReferences (Napi::Persistent) instead of values; they are released when dispatch returns. Behaviour is otherwise unchanged.

#221 mirrors the same snapshot pattern into XMLHttpRequest ("Mirrors FileReader::Dispatch"); I left a note there so it can take the same shape.

Verified on macOS (JavaScriptCore): full UnitTests green, also under JSC_collectContinuously=1. Fork twin: rebeckerspecialties#32.

FileReader::Dispatch copied the listeners into a std::vector of bare
Napi::Function values before calling them. A listener that removes a
later listener drops the only strong reference to it, and on
JavaScriptCore nothing else roots a napi_value that lives on the C++
heap (its handle scopes are stubs; only the C stack is scanned), so the
later function could be collected before the loop reached it. Snapshot
FunctionReferences instead, released when dispatch returns.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The garbage-collection scenario lacks a focused automated regression test.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Balanced

for (const auto& ref : it->second)
{
snapshot.push_back(ref.Value());
snapshot.push_back(Napi::Persistent(ref.Value()));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants