summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--std/signal/mod.ts46
-rw-r--r--std/signal/test.ts36
2 files changed, 80 insertions, 2 deletions
diff --git a/std/signal/mod.ts b/std/signal/mod.ts
index 0f2b47534..a463142e6 100644
--- a/std/signal/mod.ts
+++ b/std/signal/mod.ts
@@ -1,8 +1,28 @@
import { MuxAsyncIterator } from "../util/async.ts";
+export type Disposable = { dispose: () => void };
+
+/**
+ * Generates an AsyncIterable which can be awaited on for one or more signals.
+ * `dispose()` can be called when you are finished waiting on the events.
+ *
+ * Example:
+ *
+ * const sig = signal(Deno.Signal.SIGUSR1, Deno.Signal.SIGINT);
+ * setTimeout(() => {}, 5000); // Prevents exiting immediately
+ *
+ * for await (const _ of sig) {
+ * console.log("interrupt or usr1 signal received");
+ * }
+ *
+ * // At some other point in your code when finished listening:
+ * sig.dispose();
+ *
+ * @param signos - one or more `Deno.Signal`s to await on
+ */
export function signal(
...signos: [number, ...number[]]
-): AsyncIterable<void> & { dispose: () => void } {
+): AsyncIterable<void> & Disposable {
const mux = new MuxAsyncIterator<void>();
if (signos.length < 1) {
@@ -26,3 +46,27 @@ export function signal(
return Object.assign(mux, { dispose });
}
+
+/**
+ * Registers a callback function to be called on triggering of a signal event.
+ *
+ * const handle = onSignal(Deno.Signal.SIGINT, () => {
+ * console.log('Received SIGINT');
+ * handle.dispose(); // de-register from receiving further events
+ * });
+ *
+ * @param signo One of Deno.Signal (e.g. Deno.Signal.SIGINT)
+ * @param callback Callback function triggered upon signal event
+ */
+export function onSignal(signo: number, callback: () => void): Disposable {
+ const sig = signal(signo);
+
+ //setTimeout allows `sig` to be returned before blocking on the await
+ setTimeout(async () => {
+ for await (const _ of sig) {
+ callback();
+ }
+ }, 0);
+
+ return sig;
+}
diff --git a/std/signal/test.ts b/std/signal/test.ts
index 16d1458a2..e36a697a5 100644
--- a/std/signal/test.ts
+++ b/std/signal/test.ts
@@ -1,7 +1,7 @@
const { test } = Deno;
import { assertEquals, assertThrows } from "../testing/asserts.ts";
import { delay } from "../util/async.ts";
-import { signal } from "./mod.ts";
+import { signal, onSignal } from "./mod.ts";
if (Deno.build.os !== "win") {
test("signal() throws when called with empty signals", (): void => {
@@ -58,4 +58,38 @@ if (Deno.build.os !== "win") {
await delay(10);
},
});
+
+ test({
+ name: "onSignal() registers and disposes of event handler",
+ async fn() {
+ // This prevents the program from exiting.
+ const t = setInterval(() => {}, 1000);
+
+ let calledCount = 0;
+ const handle = onSignal(Deno.Signal.SIGINT, () => {
+ calledCount++;
+ });
+
+ await delay(20);
+ Deno.kill(Deno.pid, Deno.Signal.SIGINT);
+ await delay(20);
+ Deno.kill(Deno.pid, Deno.Signal.SIGINT);
+ await delay(20);
+ Deno.kill(Deno.pid, Deno.Signal.SIGUSR2);
+ await delay(20);
+ handle.dispose(); // stop monitoring SIGINT
+ await delay(20);
+ Deno.kill(Deno.pid, Deno.Signal.SIGUSR1);
+ await delay(20);
+ Deno.kill(Deno.pid, Deno.Signal.SIGINT);
+ await delay(20);
+ assertEquals(calledCount, 2);
+
+ clearTimeout(t);
+ // Clear timeout clears interval, but interval promise is not
+ // yet resolved, delay to next turn of event loop otherwise,
+ // we'll be leaking resources.
+ await delay(10);
+ },
+ });
}