mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
lib: make AbortSignal cloneable/transferable
Allows for using `AbortSignal` across worker threads and contexts. ```js const ac = new AbortController(); const mc = new MessageChannel(); mc.port1.onmessage = ({ data }) => { data.addEventListener('abort', () => { console.log('aborted!'); }); }; mc.port2.postMessage(ac.signal, [ac.signal]); ``` Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/41050 Refs: https://github.com/whatwg/dom/issues/948 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Robert Nagy <ronagy@icloud.com>
This commit is contained in:
parent
2e1fa8ab28
commit
29a909ab22
2 changed files with 169 additions and 5 deletions
|
@ -47,13 +47,37 @@ const {
|
|||
setTimeout,
|
||||
} = require('timers');
|
||||
|
||||
const kAborted = Symbol('kAborted');
|
||||
const kReason = Symbol('kReason');
|
||||
const kTimeout = Symbol('kTimeout');
|
||||
const {
|
||||
messaging_deserialize_symbol: kDeserialize,
|
||||
messaging_transfer_symbol: kTransfer,
|
||||
messaging_transfer_list_symbol: kTransferList
|
||||
} = internalBinding('symbols');
|
||||
|
||||
const timeOutSignals = new SafeSet();
|
||||
let _MessageChannel;
|
||||
let makeTransferable;
|
||||
|
||||
// Loading the MessageChannel and makeTransferable have to be done lazily
|
||||
// because otherwise we'll end up with a require cycle that ends up with
|
||||
// an incomplete initialization of abort_controller.
|
||||
|
||||
function lazyMessageChannel() {
|
||||
_MessageChannel ??= require('internal/worker/io').MessageChannel;
|
||||
return new _MessageChannel();
|
||||
}
|
||||
|
||||
function lazyMakeTransferable(obj) {
|
||||
makeTransferable ??=
|
||||
require('internal/worker/js_transferable').makeTransferable;
|
||||
return makeTransferable(obj);
|
||||
}
|
||||
|
||||
const clearTimeoutRegistry = new SafeFinalizationRegistry(clearTimeout);
|
||||
const timeOutSignals = new SafeSet();
|
||||
|
||||
const kAborted = Symbol('kAborted');
|
||||
const kReason = Symbol('kReason');
|
||||
const kCloneData = Symbol('kCloneData');
|
||||
const kTimeout = Symbol('kTimeout');
|
||||
|
||||
function customInspect(self, obj, depth, options) {
|
||||
if (depth < 0)
|
||||
|
@ -172,8 +196,69 @@ class AbortSignal extends EventTarget {
|
|||
timeOutSignals.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
[kTransfer]() {
|
||||
validateAbortSignal(this);
|
||||
const aborted = this.aborted;
|
||||
if (aborted) {
|
||||
const reason = this.reason;
|
||||
return {
|
||||
data: { aborted, reason },
|
||||
deserializeInfo: 'internal/abort_controller:ClonedAbortSignal',
|
||||
};
|
||||
}
|
||||
|
||||
const { port1, port2 } = this[kCloneData];
|
||||
this[kCloneData] = undefined;
|
||||
|
||||
this.addEventListener('abort', () => {
|
||||
port1.postMessage(this.reason);
|
||||
port1.close();
|
||||
}, { once: true });
|
||||
|
||||
return {
|
||||
data: { port: port2 },
|
||||
deserializeInfo: 'internal/abort_controller:ClonedAbortSignal',
|
||||
};
|
||||
}
|
||||
|
||||
[kTransferList]() {
|
||||
if (!this.aborted) {
|
||||
const { port1, port2 } = lazyMessageChannel();
|
||||
port1.unref();
|
||||
port2.unref();
|
||||
this[kCloneData] = {
|
||||
port1,
|
||||
port2,
|
||||
};
|
||||
return [port2];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
[kDeserialize]({ aborted, reason, port }) {
|
||||
if (aborted) {
|
||||
this[kAborted] = aborted;
|
||||
this[kReason] = reason;
|
||||
return;
|
||||
}
|
||||
|
||||
port.onmessage = ({ data }) => {
|
||||
abortSignal(this, data);
|
||||
port.close();
|
||||
port.onmessage = undefined;
|
||||
};
|
||||
// The receiving port, by itself, should never keep the event loop open.
|
||||
// The unref() has to be called *after* setting the onmessage handler.
|
||||
port.unref();
|
||||
}
|
||||
}
|
||||
|
||||
function ClonedAbortSignal() {
|
||||
return createAbortSignal();
|
||||
}
|
||||
ClonedAbortSignal.prototype[kDeserialize] = () => {};
|
||||
|
||||
ObjectDefineProperties(AbortSignal.prototype, {
|
||||
aborted: { enumerable: true }
|
||||
});
|
||||
|
@ -192,7 +277,7 @@ function createAbortSignal(aborted = false, reason = undefined) {
|
|||
ObjectSetPrototypeOf(signal, AbortSignal.prototype);
|
||||
signal[kAborted] = aborted;
|
||||
signal[kReason] = reason;
|
||||
return signal;
|
||||
return lazyMakeTransferable(signal);
|
||||
}
|
||||
|
||||
function abortSignal(signal, reason) {
|
||||
|
@ -259,4 +344,5 @@ module.exports = {
|
|||
kAborted,
|
||||
AbortController,
|
||||
AbortSignal,
|
||||
ClonedAbortSignal,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue