diff options
author | Yoshiya Hinosawa <stibium121@gmail.com> | 2021-10-26 12:03:38 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-26 12:03:38 +0900 |
commit | a9b34118a9338323532c3b6b2e0336c343a0e834 (patch) | |
tree | 9ad40347dcd92fb05a2f7f4c3a27b8cb8fd84110 /runtime/js/40_signals.js | |
parent | 56d9a020d94f022e0c9081d661c73f278d92084a (diff) |
feat(runtime): add Deno.addSignalListener API (#12512)
Diffstat (limited to 'runtime/js/40_signals.js')
-rw-r--r-- | runtime/js/40_signals.js | 107 |
1 files changed, 44 insertions, 63 deletions
diff --git a/runtime/js/40_signals.js b/runtime/js/40_signals.js index 2de58e74c..a4f3a6ccd 100644 --- a/runtime/js/40_signals.js +++ b/runtime/js/40_signals.js @@ -3,14 +3,9 @@ ((window) => { const core = window.Deno.core; - const { build } = window.__bootstrap.build; - const { errors } = window.__bootstrap.errors; const { - Error, - Promise, - PromisePrototypeThen, - PromiseResolve, - SymbolAsyncIterator, + Set, + TypeError, } = window.__bootstrap.primordials; function bindSignal(signo) { @@ -25,77 +20,63 @@ core.opSync("op_signal_unbind", rid); } - function signal(signo) { - if (build.os === "windows") { - throw new Error("Signal API is not implemented for Windows"); - } - return new SignalStream(signo); - } + // Stores signal listeners and resource data. This has type of + // `Record<string, { rid: number | undefined, listeners: Set<() => void> }` + const signalData = {}; - class SignalStream { - #disposed = false; - #pollingPromise = PromiseResolve(false); - #rid = 0; + /** Gets the signal handlers and resource data of the given signal */ + function getSignalData(signo) { + return signalData[signo] ?? + (signalData[signo] = { rid: undefined, listeners: new Set() }); + } - constructor(signo) { - this.#rid = bindSignal(signo); - this.#loop(); + function checkSignalListenerType(listener) { + if (typeof listener !== "function") { + throw new TypeError( + `Signal listener must be a function. "${typeof listener}" is given.`, + ); } + } - #pollSignal = async () => { - let done; - try { - done = await pollSignal(this.#rid); - } catch (error) { - if (error instanceof errors.BadResource) { - return true; - } - throw error; - } - return done; - }; + function addSignalListener(signo, listener) { + checkSignalListenerType(listener); - #loop = async () => { - do { - this.#pollingPromise = this.#pollSignal(); - } while (!(await this.#pollingPromise) && !this.#disposed); - }; + const sigData = getSignalData(signo); + sigData.listeners.add(listener); - then( - f, - g, - ) { - const p = PromisePrototypeThen(this.#pollingPromise, (done) => { - if (done) { - // If pollingPromise returns true, then - // this signal stream is finished and the promise API - // should never be resolved. - return new Promise(() => {}); - } - return; - }); - return PromisePrototypeThen(p, f, g); + if (!sigData.rid) { + // If signal resource doesn't exist, create it. + // The program starts listening to the signal + sigData.rid = bindSignal(signo); + loop(sigData); } + } - async next() { - return { done: await this.#pollingPromise, value: undefined }; - } + function removeSignalListener(signo, listener) { + checkSignalListenerType(listener); + + const sigData = getSignalData(signo); + sigData.listeners.delete(listener); - [SymbolAsyncIterator]() { - return this; + if (sigData.listeners.size === 0 && sigData.rid) { + unbindSignal(sigData.rid); + sigData.rid = undefined; } + } - dispose() { - if (this.#disposed) { - throw new Error("The stream has already been disposed."); + async function loop(sigData) { + while (sigData.rid) { + if (await pollSignal(sigData.rid)) { + return; + } + for (const listener of sigData.listeners) { + listener(); } - this.#disposed = true; - unbindSignal(this.#rid); } } window.__bootstrap.signals = { - signal, - SignalStream, + addSignalListener, + removeSignalListener, }; })(this); |