Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/jsc/bindings/JSCTaskScheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ void JSCTaskScheduler::onScheduleWorkSoon(WebCore::JSVMClientData* clientData, R
Bun__eventLoop__incrementRefConcurrently(clientData->bunVM, -1);
return;
}
// An AtSomePoint ticket (Atomics.waitAsync) sat in m_pendingTicketsOther
// without an event-loop ref while dormant. The work is now imminent, so
// promote it; otherwise the queued concurrent task is invisible to
// is_event_loop_alive() and the loop can exit before draining it.
if (auto pending = scheduler.m_pendingTicketsOther.take(ticket)) {
Bun__eventLoop__incrementRefConcurrently(clientData->bunVM, 1);
scheduler.m_pendingTicketsKeepingEventLoopAlive.add(pending.releaseNonNull());
}
auto* job = new JSCDeferredWorkTask(WTF::move(ticket), WTF::move(task));
Bun__queueJSCDeferredWorkTaskConcurrently(clientData->bunVM, job);
}
Expand Down
56 changes: 56 additions & 0 deletions test/js/web/atomics.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";

describe("Atomics", () => {
describe("basic operations", () => {
Expand Down Expand Up @@ -159,6 +160,61 @@
expect(typeof result.value).toBe("string");
}
});

test("waitAsync promises resolve when notify is the last live handle", async () => {
// The notify() inside the setTimeout callback is the last thing keeping the
// event loop alive. The waiter resolutions it schedules must still run
// before the process exits (Node.js behaviour).
const src = `
const ia = new Int32Array(new SharedArrayBuffer(4));
const got = [];
for (let i = 0; i < 5; i++)
Atomics.waitAsync(ia, 0, 0, 60000).value.then(v => {
got.push("w" + i + ":" + v);
if (got.length === 5) console.log("resolved:" + got.join(","));
});
setTimeout(() => console.log("notify=" + Atomics.notify(ia, 0)), 40);
process.on("exit", () => {
if (got.length !== 5) console.log("exit-with-unsettled:" + got.length);
});
`;
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", src],
env: bunEnv,
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
expect(stderr).toBe("");

Check warning on line 191 in test/js/web/atomics.test.ts

View check run for this annotation

Claude / Claude Code Review

Tests assert stderr is exactly empty — flaky under ASAN/debug builds

Both new subprocess tests assert `expect(stderr).toBe("")` (here and at line 214), which REVIEW.md forbids because ASAN/debug builds can emit benign warnings to stderr and cause spurious CI failures. Since stderr isn't load-bearing for what's being tested, either drop these assertions or fold them into a combined `expect({ stdout, stderr, exitCode }).toEqual({...})` per repo convention.
Comment thread
robobun marked this conversation as resolved.
Outdated
expect(stdout).toBe("notify=5\nresolved:w0:ok,w1:ok,w2:ok,w3:ok,w4:ok\n");
expect(exitCode).toBe(0);
});

test("waitAsync alone does not keep the event loop alive", async () => {
// Matches Node.js: a pending waitAsync with no other live handle lets the
// process exit. Only a delivered notify should extend the loop.
const src = `
const ia = new Int32Array(new SharedArrayBuffer(4));
Atomics.waitAsync(ia, 0, 0, 60000).value.then(v => console.log("settled:" + v));
process.on("exit", () => console.log("exit"));
`;
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", src],
env: bunEnv,
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
expect(stderr).toBe("");
expect(stdout).toBe("exit\n");
expect(exitCode).toBe(0);
});
});

describe("different TypedArray types", () => {
Expand Down
Loading