Skip to content

lib: keep event loop alive for pending Atomics.waitAsync#61982

Open
igor-shevelenkov wants to merge 1 commit intonodejs:mainfrom
igor-shevelenkov:fix/atomics-waitasync-event-loop-ref
Open

lib: keep event loop alive for pending Atomics.waitAsync#61982
igor-shevelenkov wants to merge 1 commit intonodejs:mainfrom
igor-shevelenkov:fix/atomics-waitasync-event-loop-ref

Conversation

@igor-shevelenkov
Copy link

Summary

Fixes #61941.

Pending Atomics.waitAsync() operations now keep the Node.js event loop alive until they settle, so the process does not exit early when the wait is the only pending async operation.

Problem

Atomics.waitAsync() returns a promise-like result, but that pending wait did not ref the event loop.
If nothing else was ref'd, Node could terminate before Atomics.notify() (or timeout) resolved the wait.

This affects real-world patterns like multithreaded WASM / Emscripten where completion is signaled via atomics from non-libuv worker activity.

Approach

Introduced internal/atomics/wait_async with trackWaitAsyncResult(result):

  • Leaves sync results (result.async === false) untouched.
  • Tracks async waiters with a counter.
  • Starts one shared ref'd keepalive interval when the first async waiter appears.
  • Attaches settle handlers to decrement the counter on both resolve and reject.
  • Clears the keepalive interval when the last waiter settles.

Integrated this in two places:

  1. lib/internal/bootstrap/node.js
    Wrap globalThis.Atomics.waitAsync so userland calls are tracked.

  2. lib/internal/worker/messaging.js
    Wrap internal primordial AtomicsWaitAsync usage so postMessageToThread() also keeps the loop alive while awaiting the shared-status wait.

Tests

Added regression coverage:

  • test/parallel/test-atomics-waitasync-event-loop.mjs

    • notify path
    • timeout path
    • concurrent waiters
    • sync not-equal path
    • constructor/prototype invariants
  • test/parallel/test-worker-messaging-event-loop-ref.mjs

    • verifies postMessageToThread() internal wait path does not allow early process exit

Notes

This is a Node-side lifecycle fix around the V8-provided waitAsync result, preserving existing API behavior while correcting event-loop liveness semantics.

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/startup

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. worker Issues and PRs related to Worker support. labels Feb 25, 2026
Copy link
Member

@Renegade334 Renegade334 left a comment

Choose a reason for hiding this comment

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

Replacing global-scope ES builtins with proxies is a big no-no. Resolving the issues in #44409 will be the correct way forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. worker Issues and PRs related to Worker support.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Atomics.waitAsync() promise does not ref the event loop

3 participants