diff options
author | Yoshiya Hinosawa <stibium121@gmail.com> | 2020-01-24 22:15:31 +0900 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2020-01-24 08:15:31 -0500 |
commit | bc89f04cbf5e7a8480331eea77e57a100c8179d7 (patch) | |
tree | cb5b466f1169872f1e47d68926c27fbbd1509c3f /cli/js/signals.ts | |
parent | a6a7253df9129b51255d45bf996d9a335c809ace (diff) |
Add signal handlers (#3757)
Diffstat (limited to 'cli/js/signals.ts')
-rw-r--r-- | cli/js/signals.ts | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/cli/js/signals.ts b/cli/js/signals.ts new file mode 100644 index 000000000..02d52bc2f --- /dev/null +++ b/cli/js/signals.ts @@ -0,0 +1,148 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { Signal } from "./process.ts"; +import * as dispatch from "./dispatch.ts"; +import { sendSync, sendAsync } from "./dispatch_json.ts"; +import { build } from "./build.ts"; + +/** + * Returns the stream of the given signal number. You can use it as an async + * iterator. + * + * for await (const _ of Deno.signal(Deno.Signal.SIGTERM)) { + * console.log("got SIGTERM!"); + * } + * + * You can also use it as a promise. In this case you can only receive the + * first one. + * + * await Deno.signal(Deno.Signal.SIGTERM); + * console.log("SIGTERM received!") + * + * If you want to stop receiving the signals, you can use .dispose() method + * of the signal stream object. + * + * const sig = Deno.signal(Deno.Signal.SIGTERM); + * setTimeout(() => { sig.dispose(); }, 5000); + * for await (const _ of sig) { + * console.log("SIGTERM!") + * } + * + * The above for-await loop exits after 5 seconds when sig.dispose() is called. + */ +export function signal(signo: number): SignalStream { + if (build.os === "win") { + throw new Error("not implemented!"); + } + return new SignalStream(signo); +} + +export const signals = { + /** Returns the stream of SIGALRM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGALRM). */ + alarm(): SignalStream { + return signal(Signal.SIGALRM); + }, + /** Returns the stream of SIGCHLD signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). */ + child(): SignalStream { + return signal(Signal.SIGCHLD); + }, + /** Returns the stream of SIGHUP signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). */ + hungup(): SignalStream { + return signal(Signal.SIGHUP); + }, + /** Returns the stream of SIGINT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). */ + interrupt(): SignalStream { + return signal(Signal.SIGINT); + }, + /** Returns the stream of SIGIO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). */ + io(): SignalStream { + return signal(Signal.SIGIO); + }, + /** Returns the stream of SIGPIPE signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). */ + pipe(): SignalStream { + return signal(Signal.SIGPIPE); + }, + /** Returns the stream of SIGQUIT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). */ + quit(): SignalStream { + return signal(Signal.SIGQUIT); + }, + /** Returns the stream of SIGTERM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). */ + terminate(): SignalStream { + return signal(Signal.SIGTERM); + }, + /** Returns the stream of SIGUSR1 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). */ + userDefined1(): SignalStream { + return signal(Signal.SIGUSR1); + }, + /** Returns the stream of SIGUSR2 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). */ + userDefined2(): SignalStream { + return signal(Signal.SIGUSR2); + }, + /** Returns the stream of SIGWINCH signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). */ + windowChange(): SignalStream { + return signal(Signal.SIGWINCH); + } +}; + +/** SignalStream represents the stream of signals, implements both + * AsyncIterator and PromiseLike */ +export class SignalStream implements AsyncIterator<void>, PromiseLike<void> { + private rid: number; + /** The promise of polling the signal, + * resolves with false when it receives signal, + * Resolves with true when the signal stream is disposed. */ + private pollingPromise: Promise<boolean> = Promise.resolve(false); + /** The flag, which is true when the stream is disposed. */ + private disposed = false; + constructor(signo: number) { + this.rid = sendSync(dispatch.OP_SIGNAL_BIND, { signo }).rid; + this.loop(); + } + + private async pollSignal(): Promise<boolean> { + return ( + await sendAsync(dispatch.OP_SIGNAL_POLL, { + rid: this.rid + }) + ).done; + } + + private async loop(): Promise<void> { + do { + this.pollingPromise = this.pollSignal(); + } while (!(await this.pollingPromise) && !this.disposed); + } + + then<T, S>( + f: (v: void) => T | Promise<T>, + g?: (v: Error) => S | Promise<S> + ): Promise<T | S> { + return this.pollingPromise.then((_): void => {}).then(f, g); + } + + async next(): Promise<IteratorResult<void>> { + return { done: await this.pollingPromise, value: undefined }; + } + + [Symbol.asyncIterator](): AsyncIterator<void> { + return this; + } + + dispose(): void { + if (this.disposed) { + throw new Error("The stream has already been disposed."); + } + this.disposed = true; + sendSync(dispatch.OP_SIGNAL_UNBIND, { rid: this.rid }); + } +} |