mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 18:25:13 +08:00
Close the outside port and remove its message forwarding listeners synchronously. Closing the port alone is asynchronous and can leave queued messages dispatching when terminate() runs inside a listener. Allow the current event to finish while discarding subsequent messages, including those drained when the backing thread exits. Signed-off-by: Filip Skokan <panva.ip@gmail.com> Assisted-by: Codex PR-URL: https://github.com/nodejs/node/pull/66354 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Aviv Keller <me@aviv.sh>
79 lines
2.9 KiB
JavaScript
79 lines
2.9 KiB
JavaScript
// Flags: --experimental-web-worker
|
|
'use strict';
|
|
|
|
const common = require('../common');
|
|
const assert = require('node:assert');
|
|
|
|
function checkSerialization(worker) {
|
|
assert.throws(() => worker.postMessage(() => {}), { name: 'DataCloneError' });
|
|
const buffer = new ArrayBuffer(8);
|
|
assert.throws(() => worker.postMessage(null, [buffer, buffer]), { name: 'DataCloneError' });
|
|
assert.strictEqual(buffer.byteLength, 8);
|
|
worker.postMessage(null, [buffer]);
|
|
assert.strictEqual(buffer.byteLength, 0);
|
|
}
|
|
|
|
// A failed script fetch still leaves an outside port that serializes messages.
|
|
{
|
|
const worker = new Worker('data:text/plain,');
|
|
worker.onerror = common.mustCall(() => checkSerialization(worker));
|
|
checkSerialization(worker);
|
|
}
|
|
|
|
// Serialization is required even after the backing thread has exited.
|
|
{
|
|
const worker = new Worker('data:text/javascript,close()');
|
|
worker.onerror = common.mustNotCall('worker failed');
|
|
process.once('worker', common.mustCall((thread) => {
|
|
thread.once('exit', common.mustCall(() => checkSerialization(worker)));
|
|
}));
|
|
}
|
|
|
|
// Queue messages before terminating so the test does not depend on thread speed.
|
|
{
|
|
const source = `
|
|
onmessage = ({ data }) => {
|
|
for (let i = 0; i < 3; i++) postMessage(i);
|
|
const state = new Int32Array(data);
|
|
Atomics.store(state, 0, 1);
|
|
Atomics.notify(state, 0);
|
|
};
|
|
`;
|
|
const worker = new Worker(`data:text/javascript,${encodeURIComponent(source)}`);
|
|
worker.onerror = common.mustNotCall('worker failed');
|
|
worker.onmessage = common.mustNotCall('message delivered after terminate()');
|
|
const state = new Int32Array(new SharedArrayBuffer(4));
|
|
worker.postMessage(state.buffer);
|
|
assert.notStrictEqual(Atomics.wait(state, 0, 0, common.platformTimeout(10000)), 'timed-out');
|
|
worker.terminate();
|
|
worker.terminate();
|
|
checkSerialization(worker);
|
|
}
|
|
|
|
// terminate() can run during message dispatch, including while the backing
|
|
// thread is draining messages on exit. Finish this event but discard later ones.
|
|
for (const closeAfterPosting of [false, true]) {
|
|
const source = `
|
|
onmessage = ({ data }) => {
|
|
for (let i = 0; i < 3; i++) postMessage(i);
|
|
const state = new Int32Array(data);
|
|
Atomics.store(state, 0, 1);
|
|
Atomics.notify(state, 0);
|
|
if (${closeAfterPosting}) close();
|
|
};
|
|
`;
|
|
const worker = new Worker(`data:text/javascript,${encodeURIComponent(source)}`);
|
|
worker.onerror = common.mustNotCall('worker failed');
|
|
worker.addEventListener('message', common.mustCall(({ data }) => {
|
|
assert.strictEqual(data, 0);
|
|
worker.terminate();
|
|
checkSerialization(worker);
|
|
}));
|
|
worker.addEventListener('message', common.mustCall(({ data }) => {
|
|
assert.strictEqual(data, 0);
|
|
}));
|
|
const state = new Int32Array(new SharedArrayBuffer(4));
|
|
worker.postMessage(state.buffer);
|
|
assert.notStrictEqual(Atomics.wait(state, 0, 0, common.platformTimeout(10000)), 'timed-out');
|
|
}
|