From 25b8da7e70c91d8ea7e31de92f0ea092a459ee72 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Sat, 7 Dec 2019 19:50:37 +0900 Subject: [PATCH 01/12] feat: signal handler --- cli/js/deno.ts | 1 + cli/js/dispatch.ts | 3 + cli/js/lib.deno_runtime.d.ts | 100 ++++++++++++++++++ cli/js/process.ts | 2 +- cli/js/signal_test.ts | 191 ++++++++++++++++++++++++++++++++++ cli/js/signals.ts | 196 +++++++++++++++++++++++++++++++++++ cli/js/unit_tests.ts | 1 + cli/lib.rs | 2 +- cli/ops/mod.rs | 1 + cli/ops/signal.rs | 139 +++++++++++++++++++++++++ cli/signal.rs | 12 +++ cli/worker.rs | 1 + std/manual.md | 33 ++++++ 13 files changed, 680 insertions(+), 2 deletions(-) create mode 100644 cli/js/signal_test.ts create mode 100644 cli/js/signals.ts create mode 100644 cli/ops/signal.rs diff --git a/cli/js/deno.ts b/cli/js/deno.ts index e53f9a63afd747..2d20ae811419f7 100644 --- a/cli/js/deno.ts +++ b/cli/js/deno.ts @@ -101,6 +101,7 @@ export { } from "./process.ts"; export { transpileOnly, compile, bundle } from "./compiler_api.ts"; export { inspect } from "./console.ts"; +export { signal, signals, SignalStream } from "./signals.ts"; export { build, OperatingSystem, Arch } from "./build.ts"; export { version } from "./version.ts"; export const args: string[] = []; diff --git a/cli/js/dispatch.ts b/cli/js/dispatch.ts index f5049cca88f17a..42b03f7363e140 100644 --- a/cli/js/dispatch.ts +++ b/cli/js/dispatch.ts @@ -74,6 +74,9 @@ export let OP_HOSTNAME: number; export let OP_OPEN_PLUGIN: number; export let OP_COMPILE: number; export let OP_TRANSPILE: number; +export let OP_BIND_SIGNAL: number; +export let OP_UNBIND_SIGNAL: number; +export let OP_POLL_SIGNAL: number; /** **WARNING:** This is only available during the snapshotting process and is * unavailable at runtime. */ diff --git a/cli/js/lib.deno_runtime.d.ts b/cli/js/lib.deno_runtime.d.ts index efdf06347a7377..ff2c5e17d4b913 100644 --- a/cli/js/lib.deno_runtime.d.ts +++ b/cli/js/lib.deno_runtime.d.ts @@ -2130,6 +2130,106 @@ declare namespace Deno { */ export const args: string[]; + // @url js/signal.d.ts + export class SignalStream implements AsyncIterator, PromiseLike { + constructor(signal: typeof Deno.Signal); + then( + f: (v: void) => T | Promise, + g?: (v: void) => S | Promise + ): Promise; + next(): Promise>; + [Symbol.asyncIterator](): AsyncIterator; + dispose(): void; + } + /** + * 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; + export const signals: { + /** + * Returns the stream of SIGALRM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGALRM). + */ + alarm: () => SignalStream; + /** + * Returns the stream of SIGCHLD signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). + */ + child: () => SignalStream; + /** + * Returns the stream of SIGHUP signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). + */ + hungup: () => SignalStream; + /** + * Returns the stream of SIGINFO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). + */ + info: () => SignalStream; + /** + * Returns the stream of SIGINT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). + */ + interrupt: () => SignalStream; + /** + * Returns the stream of SIGIO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). + */ + io: () => SignalStream; + /** + * Returns the stream of SIGPIPE signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). + */ + pipe: () => SignalStream; + /** + * Returns the stream of SIGQUIT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). + */ + quit: () => SignalStream; + /** + * Returns the stream of SIGTERM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). + */ + terminate: () => SignalStream; + /** + * Returns the stream of SIGUSR1 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). + */ + userDefined1: () => SignalStream; + /** + * Returns the stream of SIGUSR2 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). + */ + userDefined2: () => SignalStream; + /** + * Returns the stream of SIGWINCH signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). + */ + windowChange: () => SignalStream; + }; + /** UNSTABLE: new API. Maybe move EOF here. * * Special Deno related symbols. diff --git a/cli/js/process.ts b/cli/js/process.ts index 8ad6384b7414b8..5267763c1e7417 100644 --- a/cli/js/process.ts +++ b/cli/js/process.ts @@ -296,7 +296,7 @@ enum MacOSSignal { /** Signals numbers. This is platform dependent. */ -export const Signal = {}; +export const Signal: { [key: string]: number } = {}; export function setSignals(): void { if (build.os === "mac") { diff --git a/cli/js/signal_test.ts b/cli/js/signal_test.ts new file mode 100644 index 00000000000000..7bc0745133c1df --- /dev/null +++ b/cli/js/signal_test.ts @@ -0,0 +1,191 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +import { testPerm, assert, assertEquals, assertThrows } from "./test_util.ts"; + +function defer(n: number): Promise { + return new Promise((resolve, _) => { + setTimeout(resolve, n); + }); +} + +if (Deno.build.os === "win") { + testPerm({ run: true }, async function signalsNotImplemented(): Promise< + void + > { + assertThrows( + () => { + Deno.signal(1); + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.alarm(); // for SIGALRM + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.child(); // for SIGCHLD + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.hungup(); // for SIGHUP + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.info(); // for SIGINFO + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.interrupt(); // for SIGINT + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.io(); // for SIGIO + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.pipe(); // for SIGPIPE + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.quit(); // for SIGQUIT + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.terminate(); // for SIGTERM + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.userDefined1(); // for SIGUSR1 + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.userDefined2(); // for SIGURS2 + }, + Error, + "not implemented" + ); + assertThrows( + () => { + Deno.signals.windowChange(); // for SIGWINCH + }, + Error, + "not implemented" + ); + }); +} else { + testPerm({ run: true, net: true }, async function signalStreamTest(): Promise< + void + > { + // This prevents the program from exiting. + const t = setInterval(() => {}, 1000); + + let c = 0; + const sig = Deno.signal(Deno.Signal.SIGUSR1); + + setTimeout(async () => { + await defer(20); + for (const _ of Array(3)) { + // Sends SIGUSR1 3 times. + Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); + await defer(20); + } + sig.dispose(); + }); + + for await (const _ of sig) { + c += 1; + } + + assertEquals(c, 3); + + clearTimeout(t); + }); + + testPerm( + { run: true, net: true }, + async function signalPromiseTest(): Promise { + // This prevents the program from exiting. + const t = setInterval(() => {}, 1000); + + const sig = Deno.signal(Deno.Signal.SIGUSR1); + setTimeout(() => { + Deno.kill(Deno.pid, Deno.Signal.SIGUSR1); + }, 20); + await sig; + sig.dispose(); + + clearTimeout(t); + } + ); + + testPerm({ run: true }, async function signalShorthandsTest(): Promise { + let s: Deno.SignalStream; + s = Deno.signals.alarm(); // for SIGALRM + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.child(); // for SIGCHLD + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.hungup(); // for SIGHUP + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.info(); // for SIGINFO + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.interrupt(); // for SIGINT + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.io(); // for SIGIO + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.pipe(); // for SIGPIPE + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.quit(); // for SIGQUIT + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.terminate(); // for SIGTERM + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.userDefined1(); // for SIGUSR1 + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.userDefined2(); // for SIGURS2 + assert(s instanceof Deno.SignalStream); + s.dispose(); + s = Deno.signals.windowChange(); // for SIGWINCH + assert(s instanceof Deno.SignalStream); + s.dispose(); + }); +} diff --git a/cli/js/signals.ts b/cli/js/signals.ts new file mode 100644 index 00000000000000..502ae48494a311 --- /dev/null +++ b/cli/js/signals.ts @@ -0,0 +1,196 @@ +// 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 { DenoError, ErrorKind } from "./errors.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 { + 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 createSignalStream(Signal.SIGALRM); + }, + /** + * Returns the stream of SIGCHLD signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). + */ + child(): SignalStream { + return createSignalStream(Signal.SIGCHLD); + }, + /** + * Returns the stream of SIGHUP signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). + */ + hungup(): SignalStream { + return createSignalStream(Signal.SIGHUP); + }, + /** + * Returns the stream of SIGINFO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). + */ + info(): SignalStream { + return createSignalStream(Signal.SIGINFO); + }, + /** + * Returns the stream of SIGINT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). + */ + interrupt(): SignalStream { + return createSignalStream(Signal.SIGINT); + }, + /** + * Returns the stream of SIGIO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). + */ + io(): SignalStream { + return createSignalStream(Signal.SIGIO); + }, + /** + * Returns the stream of SIGPIPE signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). + */ + pipe(): SignalStream { + return createSignalStream(Signal.SIGPIPE); + }, + /** + * Returns the stream of SIGQUIT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). + */ + quit(): SignalStream { + return createSignalStream(Signal.SIGQUIT); + }, + /** + * Returns the stream of SIGTERM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). + */ + terminate(): SignalStream { + return createSignalStream(Signal.SIGTERM); + }, + /** + * Returns the stream of SIGUSR1 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). + */ + userDefined1(): SignalStream { + return createSignalStream(Signal.SIGUSR1); + }, + /** + * Returns the stream of SIGUSR2 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). + */ + userDefined2(): SignalStream { + return createSignalStream(Signal.SIGUSR2); + }, + /** + * Returns the stream of SIGWINCH signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). + */ + windowChange(): SignalStream { + return createSignalStream(Signal.SIGWINCH); + } +}; + +const createSignalStream = (signal: number): SignalStream => { + if (build.os === "win") { + throw new Error("not implemented!"); + } + return new SignalStream(signal); +}; + +const STREAM_DISPOSED_MESSAGE = + "No signal is available because signal stream is disposed"; + +export class SignalStream implements AsyncIterator, PromiseLike { + private rid: number; + private currentPromise: Promise = Promise.resolve(); + private disposed = false; + constructor(signo: number) { + this.rid = sendSync(dispatch.OP_BIND_SIGNAL, { signo }).rid; + this.loop(); + } + + private async pollSignal(): Promise { + const { done } = await sendAsync(dispatch.OP_POLL_SIGNAL, { + rid: this.rid + }); + + if (done) { + throw new DenoError(ErrorKind.NotFound, STREAM_DISPOSED_MESSAGE); + } + } + + private async loop(): Promise { + while (!this.disposed) { + this.currentPromise = this.pollSignal(); + try { + await this.currentPromise; + } catch (e) { + if (e instanceof DenoError && e.kind === ErrorKind.NotFound) { + // If the stream is disposed, then returns silently. + return; + } + // If it's not StreamDisposed error, it's an unexpected error. + throw e; + } + } + } + + then( + f: (v: void) => T | Promise, + g?: (v: void) => S | Promise + ): Promise { + return this.currentPromise.then(f, g); + } + + async next(): Promise> { + try { + await this.currentPromise; + return { done: false, value: undefined }; + } catch (e) { + if (e instanceof DenoError && e.kind === ErrorKind.NotFound) { + return { done: true, value: undefined }; + } + throw e; + } + } + + [Symbol.asyncIterator](): AsyncIterator { + return this; + } + + dispose(): void { + this.disposed = true; + sendSync(dispatch.OP_UNBIND_SIGNAL, { rid: this.rid }); + } +} diff --git a/cli/js/unit_tests.ts b/cli/js/unit_tests.ts index 084661ab84dd4a..47ae06b19b060b 100644 --- a/cli/js/unit_tests.ts +++ b/cli/js/unit_tests.ts @@ -42,6 +42,7 @@ import "./read_link_test.ts"; import "./rename_test.ts"; import "./request_test.ts"; import "./resources_test.ts"; +import "./signal_test.ts"; import "./stat_test.ts"; import "./symbols_test.ts"; import "./symlink_test.ts"; diff --git a/cli/lib.rs b/cli/lib.rs index 53dac1ea9a9e3d..3a83106d63a649 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -44,7 +44,7 @@ mod progress; mod repl; pub mod resolve_addr; mod shell; -mod signal; +pub mod signal; pub mod source_maps; mod startup_data; pub mod state; diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 81f95ffb9c3e24..4306e25e24d964 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -22,6 +22,7 @@ pub mod random; pub mod repl; pub mod resources; pub mod runtime_compiler; +pub mod signal; pub mod timers; pub mod tls; pub mod web_worker; diff --git a/cli/ops/signal.rs b/cli/ops/signal.rs new file mode 100644 index 00000000000000..55b5add2aa0fbe --- /dev/null +++ b/cli/ops/signal.rs @@ -0,0 +1,139 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +#[cfg(unix)] +use super::dispatch_json::Deserialize; +use super::dispatch_json::{JsonOp, Value}; +#[cfg(unix)] +use crate::deno_error::bad_resource; +use crate::ops::json_op; +#[cfg(unix)] +use crate::signal::SignalStreamResource; +use crate::state::ThreadSafeState; +use deno_core::*; +#[cfg(unix)] +use futures::future::{poll_fn, FutureExt}; +#[cfg(unix)] +use serde_json; +#[cfg(unix)] +use tokio::signal::unix::{signal, SignalKind}; + +pub fn init(i: &mut Isolate, s: &ThreadSafeState) { + i.register_op( + "bind_signal", + s.core_op(json_op(s.stateful_op(op_bind_signal))), + ); + i.register_op( + "unbind_signal", + s.core_op(json_op(s.stateful_op(op_unbind_signal))), + ); + i.register_op( + "poll_signal", + s.core_op(json_op(s.stateful_op(op_poll_signal))), + ); +} + +#[cfg(unix)] +#[derive(Deserialize)] +struct BindSignalArgs { + signo: i32, +} + +#[cfg(unix)] +#[derive(Deserialize)] +struct UnbindSignalArgs { + rid: i32, +} + +#[cfg(unix)] +#[derive(Deserialize)] +struct PollSignalArgs { + rid: i32, +} + +#[cfg(unix)] +fn op_bind_signal( + state: &ThreadSafeState, + args: Value, + _zero_copy: Option, +) -> Result { + let args: BindSignalArgs = serde_json::from_value(args)?; + let mut table = state.lock_resource_table(); + let rid = table.add( + "signal", + Box::new(SignalStreamResource( + signal(SignalKind::from_raw(args.signo)).expect(""), + None, + )), + ); + Ok(JsonOp::Sync(json!({ + "rid": rid, + }))) +} + +#[cfg(unix)] +fn op_poll_signal( + state: &ThreadSafeState, + args: Value, + _zero_copy: Option, +) -> Result { + let args: PollSignalArgs = serde_json::from_value(args)?; + let rid = args.rid as u32; + let state_ = state.clone(); + + let future = poll_fn(move |cx| { + let mut table = state_.lock_resource_table(); + if let Some(mut signal) = table.get_mut::(rid) { + signal.1 = Some(cx.waker().clone()); + return signal.0.poll_recv(cx); + } + std::task::Poll::Ready(None) + }) + .then(|result| async move { Ok(json!({ "done": result.is_none() })) }); + + Ok(JsonOp::AsyncUnref(future.boxed())) +} + +#[cfg(unix)] +pub fn op_unbind_signal( + state: &ThreadSafeState, + args: Value, + _zero_copy: Option, +) -> Result { + let args: UnbindSignalArgs = serde_json::from_value(args)?; + let rid = args.rid as u32; + let mut table = state.lock_resource_table(); + let resource = table.get::(rid); + if let Some(signal) = resource { + if let Some(waker) = &signal.1 { + waker.clone().wake(); + } + } + table.close(rid).ok_or_else(bad_resource)?; + Ok(JsonOp::Sync(json!({}))) +} + +#[cfg(not(unix))] +pub fn op_bind_signal( + _state: &ThreadSafeState, + _args: Value, + _zero_copy: Option, +) -> Result { + unimplemented!(); +} + +#[cfg(not(unix))] +fn op_unbind_signal( + _state: &ThreadSafeState, + _args: Value, + _zero_copy: Option, +) -> Result { + unimplemented!(); +} + +#[cfg(not(unix))] +fn op_poll_signal( + _state: &ThreadSafeState, + _args: Value, + _zero_copy: Option, +) -> Result { + unimplemented!(); +} diff --git a/cli/signal.rs b/cli/signal.rs index 57f2d0d3ddf2b9..e577ad5cd9d9e6 100644 --- a/cli/signal.rs +++ b/cli/signal.rs @@ -1,4 +1,10 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use deno_core::ErrBox; +#[cfg(unix)] +use deno_core::Resource; + +#[cfg(unix)] +use tokio::signal::unix::Signal; #[cfg(unix)] pub fn kill(pid: i32, signo: i32) -> Result<(), ErrBox> { @@ -14,3 +20,9 @@ pub fn kill(_pid: i32, _signal: i32) -> Result<(), ErrBox> { // TODO: implement this for windows Ok(()) } + +#[cfg(unix)] +pub struct SignalStreamResource(pub Signal, pub Option); + +#[cfg(unix)] +impl Resource for SignalStreamResource {} diff --git a/cli/worker.rs b/cli/worker.rs index ef72602d41d9d5..9fd70eedc2effe 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -200,6 +200,7 @@ impl MainWorker { ops::random::init(&mut isolate, &state); ops::repl::init(&mut isolate, &state); ops::resources::init(&mut isolate, &state); + ops::signal::init(&mut isolate, &state); ops::timers::init(&mut isolate, &state); ops::worker_host::init(&mut isolate, &state); ops::web_worker::init(&mut isolate, &state); diff --git a/std/manual.md b/std/manual.md index ece5a755b9f866..204701566f17bc 100644 --- a/std/manual.md +++ b/std/manual.md @@ -428,6 +428,39 @@ Uncaught NotFound: No such file or directory (os error 2) at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17) ``` +### Handle OS Signals + +[API Reference](https://deno.land/typedoc/index.html#signal) + +You can use `Deno.signal()` function for handling OS signals. + +``` +for await (const _ of Deno.signal(Deno.Signal.SIGINT)) { + console.log("interrupted!"); +} +``` + +`Deno.signal()` also works as a promise. + +``` +await Deno.signal(Deno.Singal.SIGINT); +console.log("interrupted!"); +``` + +If you want to stop watching the signal, you can use `dispose()` method of the +signal object. + +``` +const sig = Deno.signal(Deno.Signal.SIGINT); +setTimeout(() => { sig.dispose(); }, 5000); + +for await (const _ of sig) { + console.log("interrupted"); +} +``` + +The above for-await loop exits after 5 seconds when sig.dispose() is called. + ### Linking to third party code In the above examples, we saw that Deno could execute scripts from URLs. Like From f7c26483634de28b8c56feeea9d73ce4f48ad32e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 23 Jan 2020 11:49:21 +0900 Subject: [PATCH 02/12] style: update styles --- cli/js/lib.deno_runtime.d.ts | 75 ++++++++++++----------------------- cli/js/signals.ts | 76 +++++++++++++----------------------- cli/ops/signal.rs | 21 +++++++--- cli/signal.rs | 11 ------ 4 files changed, 69 insertions(+), 114 deletions(-) diff --git a/cli/js/lib.deno_runtime.d.ts b/cli/js/lib.deno_runtime.d.ts index ff2c5e17d4b913..d3fe5a564140f0 100644 --- a/cli/js/lib.deno_runtime.d.ts +++ b/cli/js/lib.deno_runtime.d.ts @@ -2130,7 +2130,8 @@ declare namespace Deno { */ export const args: string[]; - // @url js/signal.d.ts + /** SignalStream represents the stream of signals, implements both + * AsyncIterator and PromiseLike */ export class SignalStream implements AsyncIterator, PromiseLike { constructor(signal: typeof Deno.Signal); then( @@ -2168,65 +2169,41 @@ declare namespace Deno { */ export function signal(signo: number): SignalStream; export const signals: { - /** - * Returns the stream of SIGALRM signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGALRM). - */ + /** Returns the stream of SIGALRM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGALRM). */ alarm: () => SignalStream; - /** - * Returns the stream of SIGCHLD signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). - */ + /** Returns the stream of SIGCHLD signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). */ child: () => SignalStream; - /** - * Returns the stream of SIGHUP signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). - */ + /** Returns the stream of SIGHUP signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). */ hungup: () => SignalStream; - /** - * Returns the stream of SIGINFO signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). - */ + /** Returns the stream of SIGINFO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). */ info: () => SignalStream; - /** - * Returns the stream of SIGINT signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). - */ + /** Returns the stream of SIGINT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). */ interrupt: () => SignalStream; - /** - * Returns the stream of SIGIO signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). - */ + /** Returns the stream of SIGIO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). */ io: () => SignalStream; - /** - * Returns the stream of SIGPIPE signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). - */ + /** Returns the stream of SIGPIPE signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). */ pipe: () => SignalStream; - /** - * Returns the stream of SIGQUIT signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). - */ + /** Returns the stream of SIGQUIT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). */ quit: () => SignalStream; - /** - * Returns the stream of SIGTERM signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). - */ + /** Returns the stream of SIGTERM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). */ terminate: () => SignalStream; - /** - * Returns the stream of SIGUSR1 signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). - */ + /** Returns the stream of SIGUSR1 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). */ userDefined1: () => SignalStream; - /** - * Returns the stream of SIGUSR2 signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). - */ + /** Returns the stream of SIGUSR2 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). */ userDefined2: () => SignalStream; - /** - * Returns the stream of SIGWINCH signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). - */ + /** Returns the stream of SIGWINCH signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). */ windowChange: () => SignalStream; }; diff --git a/cli/js/signals.ts b/cli/js/signals.ts index 502ae48494a311..d079b5419108a5 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -35,87 +35,63 @@ export function signal(signo: number): SignalStream { } export const signals = { - /** - * Returns the stream of SIGALRM signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGALRM). - */ + /** Returns the stream of SIGALRM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGALRM). */ alarm(): SignalStream { return createSignalStream(Signal.SIGALRM); }, - /** - * Returns the stream of SIGCHLD signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). - */ + /** Returns the stream of SIGCHLD signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). */ child(): SignalStream { return createSignalStream(Signal.SIGCHLD); }, - /** - * Returns the stream of SIGHUP signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). - */ + /** Returns the stream of SIGHUP signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). */ hungup(): SignalStream { return createSignalStream(Signal.SIGHUP); }, - /** - * Returns the stream of SIGINFO signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). - */ + /** Returns the stream of SIGINFO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). */ info(): SignalStream { return createSignalStream(Signal.SIGINFO); }, - /** - * Returns the stream of SIGINT signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). - */ + /** Returns the stream of SIGINT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). */ interrupt(): SignalStream { return createSignalStream(Signal.SIGINT); }, - /** - * Returns the stream of SIGIO signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). - */ + /** Returns the stream of SIGIO signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). */ io(): SignalStream { return createSignalStream(Signal.SIGIO); }, - /** - * Returns the stream of SIGPIPE signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). - */ + /** Returns the stream of SIGPIPE signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). */ pipe(): SignalStream { return createSignalStream(Signal.SIGPIPE); }, - /** - * Returns the stream of SIGQUIT signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). - */ + /** Returns the stream of SIGQUIT signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). */ quit(): SignalStream { return createSignalStream(Signal.SIGQUIT); }, - /** - * Returns the stream of SIGTERM signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). - */ + /** Returns the stream of SIGTERM signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). */ terminate(): SignalStream { return createSignalStream(Signal.SIGTERM); }, - /** - * Returns the stream of SIGUSR1 signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). - */ + /** Returns the stream of SIGUSR1 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). */ userDefined1(): SignalStream { return createSignalStream(Signal.SIGUSR1); }, - /** - * Returns the stream of SIGUSR2 signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). - */ + /** Returns the stream of SIGUSR2 signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). */ userDefined2(): SignalStream { return createSignalStream(Signal.SIGUSR2); }, - /** - * Returns the stream of SIGWINCH signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). - */ + /** Returns the stream of SIGWINCH signals. + * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). */ windowChange(): SignalStream { return createSignalStream(Signal.SIGWINCH); } @@ -131,9 +107,13 @@ const createSignalStream = (signal: number): SignalStream => { const STREAM_DISPOSED_MESSAGE = "No signal is available because signal stream is disposed"; +/** SignalStream represents the stream of signals, implements both + * AsyncIterator and PromiseLike */ export class SignalStream implements AsyncIterator, PromiseLike { private rid: number; + /** The promise of polling the signal. */ private currentPromise: Promise = Promise.resolve(); + /** The flag, which is true when the stream is disposed. */ private disposed = false; constructor(signo: number) { this.rid = sendSync(dispatch.OP_BIND_SIGNAL, { signo }).rid; diff --git a/cli/ops/signal.rs b/cli/ops/signal.rs index 55b5add2aa0fbe..e12a50c058a527 100644 --- a/cli/ops/signal.rs +++ b/cli/ops/signal.rs @@ -1,20 +1,23 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use super::dispatch_json::{JsonOp, Value}; +use crate::ops::json_op; +use crate::state::ThreadSafeState; +use deno_core::*; + #[cfg(unix)] use super::dispatch_json::Deserialize; -use super::dispatch_json::{JsonOp, Value}; #[cfg(unix)] use crate::deno_error::bad_resource; -use crate::ops::json_op; #[cfg(unix)] -use crate::signal::SignalStreamResource; -use crate::state::ThreadSafeState; -use deno_core::*; +use std::task::Waker; +#[cfg(unix)] +use deno_core::Resource; #[cfg(unix)] use futures::future::{poll_fn, FutureExt}; #[cfg(unix)] use serde_json; #[cfg(unix)] -use tokio::signal::unix::{signal, SignalKind}; +use tokio::signal::unix::{signal, Signal, SignalKind}; pub fn init(i: &mut Isolate, s: &ThreadSafeState) { i.register_op( @@ -31,6 +34,12 @@ pub fn init(i: &mut Isolate, s: &ThreadSafeState) { ); } +#[cfg(unix)] +pub struct SignalStreamResource(pub Signal, pub Option); + +#[cfg(unix)] +impl Resource for SignalStreamResource {} + #[cfg(unix)] #[derive(Deserialize)] struct BindSignalArgs { diff --git a/cli/signal.rs b/cli/signal.rs index e577ad5cd9d9e6..6f150aeab5636c 100644 --- a/cli/signal.rs +++ b/cli/signal.rs @@ -1,10 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use deno_core::ErrBox; -#[cfg(unix)] -use deno_core::Resource; - -#[cfg(unix)] -use tokio::signal::unix::Signal; #[cfg(unix)] pub fn kill(pid: i32, signo: i32) -> Result<(), ErrBox> { @@ -20,9 +15,3 @@ pub fn kill(_pid: i32, _signal: i32) -> Result<(), ErrBox> { // TODO: implement this for windows Ok(()) } - -#[cfg(unix)] -pub struct SignalStreamResource(pub Signal, pub Option); - -#[cfg(unix)] -impl Resource for SignalStreamResource {} From fe55b49c6c8d8c21811f1eeed0ddd8fa58fef04d Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 23 Jan 2020 15:39:27 +0900 Subject: [PATCH 03/12] docs: add document for the waker in SignalStreamResource --- cli/ops/signal.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cli/ops/signal.rs b/cli/ops/signal.rs index e12a50c058a527..333301e7ed78d0 100644 --- a/cli/ops/signal.rs +++ b/cli/ops/signal.rs @@ -9,14 +9,14 @@ use super::dispatch_json::Deserialize; #[cfg(unix)] use crate::deno_error::bad_resource; #[cfg(unix)] -use std::task::Waker; -#[cfg(unix)] use deno_core::Resource; #[cfg(unix)] use futures::future::{poll_fn, FutureExt}; #[cfg(unix)] use serde_json; #[cfg(unix)] +use std::task::Waker; +#[cfg(unix)] use tokio::signal::unix::{signal, Signal, SignalKind}; pub fn init(i: &mut Isolate, s: &ThreadSafeState) { @@ -35,6 +35,8 @@ pub fn init(i: &mut Isolate, s: &ThreadSafeState) { } #[cfg(unix)] +/// The resource for signal stream. +/// The second element is the waker of polling future. pub struct SignalStreamResource(pub Signal, pub Option); #[cfg(unix)] @@ -113,6 +115,8 @@ pub fn op_unbind_signal( let resource = table.get::(rid); if let Some(signal) = resource { if let Some(waker) = &signal.1 { + // Wakes up the pending poll if exists. + // This prevents the poll future from getting stuck forever. waker.clone().wake(); } } From 79f57625789ef1a10727cfe70a5d84de4bcd2e4e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 23 Jan 2020 18:42:27 +0900 Subject: [PATCH 04/12] fix: remove signals.info shorthand because linux doesn't have SIGINFO --- cli/js/lib.deno_runtime.d.ts | 3 --- cli/js/signal_test.ts | 10 ---------- cli/js/signals.ts | 5 ----- 3 files changed, 18 deletions(-) diff --git a/cli/js/lib.deno_runtime.d.ts b/cli/js/lib.deno_runtime.d.ts index d3fe5a564140f0..1dfa3209f8b0a1 100644 --- a/cli/js/lib.deno_runtime.d.ts +++ b/cli/js/lib.deno_runtime.d.ts @@ -2178,9 +2178,6 @@ declare namespace Deno { /** Returns the stream of SIGHUP signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). */ hungup: () => SignalStream; - /** Returns the stream of SIGINFO signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). */ - info: () => SignalStream; /** Returns the stream of SIGINT signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). */ interrupt: () => SignalStream; diff --git a/cli/js/signal_test.ts b/cli/js/signal_test.ts index 7bc0745133c1df..e3460fc2207410 100644 --- a/cli/js/signal_test.ts +++ b/cli/js/signal_test.ts @@ -39,13 +39,6 @@ if (Deno.build.os === "win") { Error, "not implemented" ); - assertThrows( - () => { - Deno.signals.info(); // for SIGINFO - }, - Error, - "not implemented" - ); assertThrows( () => { Deno.signals.interrupt(); // for SIGINT @@ -160,9 +153,6 @@ if (Deno.build.os === "win") { s = Deno.signals.hungup(); // for SIGHUP assert(s instanceof Deno.SignalStream); s.dispose(); - s = Deno.signals.info(); // for SIGINFO - assert(s instanceof Deno.SignalStream); - s.dispose(); s = Deno.signals.interrupt(); // for SIGINT assert(s instanceof Deno.SignalStream); s.dispose(); diff --git a/cli/js/signals.ts b/cli/js/signals.ts index d079b5419108a5..e691814b72136a 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -50,11 +50,6 @@ export const signals = { hungup(): SignalStream { return createSignalStream(Signal.SIGHUP); }, - /** Returns the stream of SIGINFO signals. - * This method is the shorthand for Deno.signal(Deno.Signal.SIGINFO). */ - info(): SignalStream { - return createSignalStream(Signal.SIGINFO); - }, /** Returns the stream of SIGINT signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). */ interrupt(): SignalStream { From 8e651c6f74a92c99dc723e3d8b74d8fed7c3cfa9 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 23 Jan 2020 19:40:36 +0900 Subject: [PATCH 05/12] fix(signal): fix bug in windows --- cli/js/signals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/js/signals.ts b/cli/js/signals.ts index e691814b72136a..d2ba0412b81d43 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -31,7 +31,7 @@ import { build } from "./build.ts"; * The above for-await loop exits after 5 seconds when sig.dispose() is called. */ export function signal(signo: number): SignalStream { - return new SignalStream(signo); + return createSignalStream(signo); } export const signals = { From 05d5168c521903e634328bce8fdff4297954ff92 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 16:06:11 +0900 Subject: [PATCH 06/12] refactor: unify signal arg structs. --- cli/ops/signal.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cli/ops/signal.rs b/cli/ops/signal.rs index 333301e7ed78d0..1008db7f52f2ba 100644 --- a/cli/ops/signal.rs +++ b/cli/ops/signal.rs @@ -50,13 +50,7 @@ struct BindSignalArgs { #[cfg(unix)] #[derive(Deserialize)] -struct UnbindSignalArgs { - rid: i32, -} - -#[cfg(unix)] -#[derive(Deserialize)] -struct PollSignalArgs { +struct SignalArgs { rid: i32, } @@ -86,7 +80,7 @@ fn op_poll_signal( args: Value, _zero_copy: Option, ) -> Result { - let args: PollSignalArgs = serde_json::from_value(args)?; + let args: SignalArgs = serde_json::from_value(args)?; let rid = args.rid as u32; let state_ = state.clone(); @@ -109,7 +103,7 @@ pub fn op_unbind_signal( args: Value, _zero_copy: Option, ) -> Result { - let args: UnbindSignalArgs = serde_json::from_value(args)?; + let args: SignalArgs = serde_json::from_value(args)?; let rid = args.rid as u32; let mut table = state.lock_resource_table(); let resource = table.get::(rid); From b0bdb49702a3e94b79e92db70b427c1a8a9e229f Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 16:11:15 +0900 Subject: [PATCH 07/12] refactor(signal): rename op_xyz_signal -> op_signal_xyz --- cli/js/dispatch.ts | 6 +++--- cli/js/signals.ts | 6 +++--- cli/ops/signal.rs | 24 ++++++++++++------------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cli/js/dispatch.ts b/cli/js/dispatch.ts index 42b03f7363e140..aa6696fa2c1f17 100644 --- a/cli/js/dispatch.ts +++ b/cli/js/dispatch.ts @@ -74,9 +74,9 @@ export let OP_HOSTNAME: number; export let OP_OPEN_PLUGIN: number; export let OP_COMPILE: number; export let OP_TRANSPILE: number; -export let OP_BIND_SIGNAL: number; -export let OP_UNBIND_SIGNAL: number; -export let OP_POLL_SIGNAL: number; +export let OP_SIGNAL_BIND: number; +export let OP_SIGNAL_UNBIND: number; +export let OP_SIGNAL_POLL: number; /** **WARNING:** This is only available during the snapshotting process and is * unavailable at runtime. */ diff --git a/cli/js/signals.ts b/cli/js/signals.ts index d2ba0412b81d43..58b692d6ab7741 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -111,12 +111,12 @@ export class SignalStream implements AsyncIterator, PromiseLike { /** The flag, which is true when the stream is disposed. */ private disposed = false; constructor(signo: number) { - this.rid = sendSync(dispatch.OP_BIND_SIGNAL, { signo }).rid; + this.rid = sendSync(dispatch.OP_SIGNAL_BIND, { signo }).rid; this.loop(); } private async pollSignal(): Promise { - const { done } = await sendAsync(dispatch.OP_POLL_SIGNAL, { + const { done } = await sendAsync(dispatch.OP_SIGNAL_POLL, { rid: this.rid }); @@ -166,6 +166,6 @@ export class SignalStream implements AsyncIterator, PromiseLike { dispose(): void { this.disposed = true; - sendSync(dispatch.OP_UNBIND_SIGNAL, { rid: this.rid }); + sendSync(dispatch.OP_SIGNAL_UNBIND, { rid: this.rid }); } } diff --git a/cli/ops/signal.rs b/cli/ops/signal.rs index 1008db7f52f2ba..5fef0201313b21 100644 --- a/cli/ops/signal.rs +++ b/cli/ops/signal.rs @@ -21,16 +21,16 @@ use tokio::signal::unix::{signal, Signal, SignalKind}; pub fn init(i: &mut Isolate, s: &ThreadSafeState) { i.register_op( - "bind_signal", - s.core_op(json_op(s.stateful_op(op_bind_signal))), + "signal_bind", + s.core_op(json_op(s.stateful_op(op_signal_bind))), ); i.register_op( - "unbind_signal", - s.core_op(json_op(s.stateful_op(op_unbind_signal))), + "signal_unbind", + s.core_op(json_op(s.stateful_op(op_signal_unbind))), ); i.register_op( - "poll_signal", - s.core_op(json_op(s.stateful_op(op_poll_signal))), + "signal_poll", + s.core_op(json_op(s.stateful_op(op_signal_poll))), ); } @@ -55,7 +55,7 @@ struct SignalArgs { } #[cfg(unix)] -fn op_bind_signal( +fn op_signal_bind( state: &ThreadSafeState, args: Value, _zero_copy: Option, @@ -75,7 +75,7 @@ fn op_bind_signal( } #[cfg(unix)] -fn op_poll_signal( +fn op_signal_poll( state: &ThreadSafeState, args: Value, _zero_copy: Option, @@ -98,7 +98,7 @@ fn op_poll_signal( } #[cfg(unix)] -pub fn op_unbind_signal( +pub fn op_signal_unbind( state: &ThreadSafeState, args: Value, _zero_copy: Option, @@ -119,7 +119,7 @@ pub fn op_unbind_signal( } #[cfg(not(unix))] -pub fn op_bind_signal( +pub fn op_signal_bind( _state: &ThreadSafeState, _args: Value, _zero_copy: Option, @@ -128,7 +128,7 @@ pub fn op_bind_signal( } #[cfg(not(unix))] -fn op_unbind_signal( +fn op_signal_unbind( _state: &ThreadSafeState, _args: Value, _zero_copy: Option, @@ -137,7 +137,7 @@ fn op_unbind_signal( } #[cfg(not(unix))] -fn op_poll_signal( +fn op_signal_poll( _state: &ThreadSafeState, _args: Value, _zero_copy: Option, From 749d7087670b3f40d8ff04c448586a8eae11b00d Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 16:14:38 +0900 Subject: [PATCH 08/12] refactor: remove redundant factory function --- cli/js/signals.ts | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/cli/js/signals.ts b/cli/js/signals.ts index 58b692d6ab7741..8ce87670d972f6 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -31,72 +31,68 @@ import { build } from "./build.ts"; * The above for-await loop exits after 5 seconds when sig.dispose() is called. */ export function signal(signo: number): SignalStream { - return createSignalStream(signo); + 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 createSignalStream(Signal.SIGALRM); + return signal(Signal.SIGALRM); }, /** Returns the stream of SIGCHLD signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGCHLD). */ child(): SignalStream { - return createSignalStream(Signal.SIGCHLD); + return signal(Signal.SIGCHLD); }, /** Returns the stream of SIGHUP signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGHUP). */ hungup(): SignalStream { - return createSignalStream(Signal.SIGHUP); + return signal(Signal.SIGHUP); }, /** Returns the stream of SIGINT signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGINT). */ interrupt(): SignalStream { - return createSignalStream(Signal.SIGINT); + return signal(Signal.SIGINT); }, /** Returns the stream of SIGIO signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGIO). */ io(): SignalStream { - return createSignalStream(Signal.SIGIO); + return signal(Signal.SIGIO); }, /** Returns the stream of SIGPIPE signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGPIPE). */ pipe(): SignalStream { - return createSignalStream(Signal.SIGPIPE); + return signal(Signal.SIGPIPE); }, /** Returns the stream of SIGQUIT signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGQUIT). */ quit(): SignalStream { - return createSignalStream(Signal.SIGQUIT); + return signal(Signal.SIGQUIT); }, /** Returns the stream of SIGTERM signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGTERM). */ terminate(): SignalStream { - return createSignalStream(Signal.SIGTERM); + return signal(Signal.SIGTERM); }, /** Returns the stream of SIGUSR1 signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR1). */ userDefined1(): SignalStream { - return createSignalStream(Signal.SIGUSR1); + return signal(Signal.SIGUSR1); }, /** Returns the stream of SIGUSR2 signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGUSR2). */ userDefined2(): SignalStream { - return createSignalStream(Signal.SIGUSR2); + return signal(Signal.SIGUSR2); }, /** Returns the stream of SIGWINCH signals. * This method is the shorthand for Deno.signal(Deno.Signal.SIGWINCH). */ windowChange(): SignalStream { - return createSignalStream(Signal.SIGWINCH); - } -}; - -const createSignalStream = (signal: number): SignalStream => { - if (build.os === "win") { - throw new Error("not implemented!"); + return signal(Signal.SIGWINCH); } - return new SignalStream(signal); }; const STREAM_DISPOSED_MESSAGE = From f6ae2ef183332221637cd50466f5a54811c9590d Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 18:07:28 +0900 Subject: [PATCH 09/12] refactor: remove redundant error throwing & handling --- cli/js/signals.ts | 50 ++++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/cli/js/signals.ts b/cli/js/signals.ts index 8ce87670d972f6..7c0de0646bcfa1 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -100,10 +100,12 @@ const STREAM_DISPOSED_MESSAGE = /** SignalStream represents the stream of signals, implements both * AsyncIterator and PromiseLike */ -export class SignalStream implements AsyncIterator, PromiseLike { +export class SignalStream implements AsyncIterator, PromiseLike { private rid: number; - /** The promise of polling the signal. */ - private currentPromise: Promise = Promise.resolve(); + /** 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 = Promise.resolve(false); /** The flag, which is true when the stream is disposed. */ private disposed = false; constructor(signo: number) { @@ -111,49 +113,27 @@ export class SignalStream implements AsyncIterator, PromiseLike { this.loop(); } - private async pollSignal(): Promise { - const { done } = await sendAsync(dispatch.OP_SIGNAL_POLL, { + private async pollSignal(): Promise { + return (await sendAsync(dispatch.OP_SIGNAL_POLL, { rid: this.rid - }); - - if (done) { - throw new DenoError(ErrorKind.NotFound, STREAM_DISPOSED_MESSAGE); - } + })).done; } private async loop(): Promise { - while (!this.disposed) { - this.currentPromise = this.pollSignal(); - try { - await this.currentPromise; - } catch (e) { - if (e instanceof DenoError && e.kind === ErrorKind.NotFound) { - // If the stream is disposed, then returns silently. - return; - } - // If it's not StreamDisposed error, it's an unexpected error. - throw e; - } - } + do { + this.pollingPromise = this.pollSignal(); + } while (!await this.pollingPromise && !this.disposed); } then( - f: (v: void) => T | Promise, - g?: (v: void) => S | Promise + f: (v: boolean) => T | Promise, + g?: (v: Error) => S | Promise ): Promise { - return this.currentPromise.then(f, g); + return this.pollingPromise.then(f, g); } async next(): Promise> { - try { - await this.currentPromise; - return { done: false, value: undefined }; - } catch (e) { - if (e instanceof DenoError && e.kind === ErrorKind.NotFound) { - return { done: true, value: undefined }; - } - throw e; - } + return { done: await this.pollingPromise, value: undefined }; } [Symbol.asyncIterator](): AsyncIterator { From 8fa9fc82ca2869534a81e62688cd3964cec99f5b Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 18:11:08 +0900 Subject: [PATCH 10/12] fix: remove unnecessary test perm --- cli/js/signal_test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cli/js/signal_test.ts b/cli/js/signal_test.ts index e3460fc2207410..06457314c89275 100644 --- a/cli/js/signal_test.ts +++ b/cli/js/signal_test.ts @@ -1,5 +1,11 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals, assertThrows } from "./test_util.ts"; +import { + test, + testPerm, + assert, + assertEquals, + assertThrows +} from "./test_util.ts"; function defer(n: number): Promise { return new Promise((resolve, _) => { @@ -8,9 +14,7 @@ function defer(n: number): Promise { } if (Deno.build.os === "win") { - testPerm({ run: true }, async function signalsNotImplemented(): Promise< - void - > { + test(async function signalsNotImplemented(): Promise { assertThrows( () => { Deno.signal(1); From dc985698f1fe4fe9f060dbcaa46150036b207fda Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 18:21:11 +0900 Subject: [PATCH 11/12] feat: add the guard against double dispose --- cli/js/signals.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cli/js/signals.ts b/cli/js/signals.ts index 7c0de0646bcfa1..a9a555c96615e5 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -2,7 +2,6 @@ import { Signal } from "./process.ts"; import * as dispatch from "./dispatch.ts"; import { sendSync, sendAsync } from "./dispatch_json.ts"; -import { DenoError, ErrorKind } from "./errors.ts"; import { build } from "./build.ts"; /** @@ -95,9 +94,6 @@ export const signals = { } }; -const STREAM_DISPOSED_MESSAGE = - "No signal is available because signal stream is disposed"; - /** SignalStream represents the stream of signals, implements both * AsyncIterator and PromiseLike */ export class SignalStream implements AsyncIterator, PromiseLike { @@ -114,15 +110,17 @@ export class SignalStream implements AsyncIterator, PromiseLike { } private async pollSignal(): Promise { - return (await sendAsync(dispatch.OP_SIGNAL_POLL, { - rid: this.rid - })).done; + return ( + await sendAsync(dispatch.OP_SIGNAL_POLL, { + rid: this.rid + }) + ).done; } private async loop(): Promise { do { this.pollingPromise = this.pollSignal(); - } while (!await this.pollingPromise && !this.disposed); + } while (!(await this.pollingPromise) && !this.disposed); } then( @@ -141,6 +139,9 @@ export class SignalStream implements AsyncIterator, PromiseLike { } 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 }); } From 87112aa294857ac58429ea31ab1903a7c468f60e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 24 Jan 2020 18:30:32 +0900 Subject: [PATCH 12/12] feat: SignalStream: PromiseLike -> PromiseLike --- cli/js/signals.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/js/signals.ts b/cli/js/signals.ts index a9a555c96615e5..02d52bc2f1c5aa 100644 --- a/cli/js/signals.ts +++ b/cli/js/signals.ts @@ -96,7 +96,7 @@ export const signals = { /** SignalStream represents the stream of signals, implements both * AsyncIterator and PromiseLike */ -export class SignalStream implements AsyncIterator, PromiseLike { +export class SignalStream implements AsyncIterator, PromiseLike { private rid: number; /** The promise of polling the signal, * resolves with false when it receives signal, @@ -124,10 +124,10 @@ export class SignalStream implements AsyncIterator, PromiseLike { } then( - f: (v: boolean) => T | Promise, + f: (v: void) => T | Promise, g?: (v: Error) => S | Promise ): Promise { - return this.pollingPromise.then(f, g); + return this.pollingPromise.then((_): void => {}).then(f, g); } async next(): Promise> {