summaryrefslogtreecommitdiff
path: root/runtime/js/40_signals.js
diff options
context:
space:
mode:
authorYoshiya Hinosawa <stibium121@gmail.com>2021-10-26 12:03:38 +0900
committerGitHub <noreply@github.com>2021-10-26 12:03:38 +0900
commita9b34118a9338323532c3b6b2e0336c343a0e834 (patch)
tree9ad40347dcd92fb05a2f7f4c3a27b8cb8fd84110 /runtime/js/40_signals.js
parent56d9a020d94f022e0c9081d661c73f278d92084a (diff)
feat(runtime): add Deno.addSignalListener API (#12512)
Diffstat (limited to 'runtime/js/40_signals.js')
-rw-r--r--runtime/js/40_signals.js107
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);