mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 08:39:09 -05:00
Add signal handlers (#3757)
This commit is contained in:
parent
a6a7253df9
commit
bc89f04cbf
13 changed files with 596 additions and 2 deletions
|
@ -101,6 +101,7 @@ export {
|
||||||
} from "./process.ts";
|
} from "./process.ts";
|
||||||
export { transpileOnly, compile, bundle } from "./compiler_api.ts";
|
export { transpileOnly, compile, bundle } from "./compiler_api.ts";
|
||||||
export { inspect } from "./console.ts";
|
export { inspect } from "./console.ts";
|
||||||
|
export { signal, signals, SignalStream } from "./signals.ts";
|
||||||
export { build, OperatingSystem, Arch } from "./build.ts";
|
export { build, OperatingSystem, Arch } from "./build.ts";
|
||||||
export { version } from "./version.ts";
|
export { version } from "./version.ts";
|
||||||
export const args: string[] = [];
|
export const args: string[] = [];
|
||||||
|
|
|
@ -74,6 +74,9 @@ export let OP_HOSTNAME: number;
|
||||||
export let OP_OPEN_PLUGIN: number;
|
export let OP_OPEN_PLUGIN: number;
|
||||||
export let OP_COMPILE: number;
|
export let OP_COMPILE: number;
|
||||||
export let OP_TRANSPILE: number;
|
export let OP_TRANSPILE: 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
|
/** **WARNING:** This is only available during the snapshotting process and is
|
||||||
* unavailable at runtime. */
|
* unavailable at runtime. */
|
||||||
|
|
74
cli/js/lib.deno_runtime.d.ts
vendored
74
cli/js/lib.deno_runtime.d.ts
vendored
|
@ -2130,6 +2130,80 @@ declare namespace Deno {
|
||||||
*/
|
*/
|
||||||
export const args: string[];
|
export const args: string[];
|
||||||
|
|
||||||
|
/** SignalStream represents the stream of signals, implements both
|
||||||
|
* AsyncIterator and PromiseLike */
|
||||||
|
export class SignalStream implements AsyncIterator<void>, PromiseLike<void> {
|
||||||
|
constructor(signal: typeof Deno.Signal);
|
||||||
|
then<T, S>(
|
||||||
|
f: (v: void) => T | Promise<T>,
|
||||||
|
g?: (v: void) => S | Promise<S>
|
||||||
|
): Promise<T | S>;
|
||||||
|
next(): Promise<IteratorResult<void>>;
|
||||||
|
[Symbol.asyncIterator](): AsyncIterator<void>;
|
||||||
|
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 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.
|
/** UNSTABLE: new API. Maybe move EOF here.
|
||||||
*
|
*
|
||||||
* Special Deno related symbols.
|
* Special Deno related symbols.
|
||||||
|
|
|
@ -296,7 +296,7 @@ enum MacOSSignal {
|
||||||
|
|
||||||
/** Signals numbers. This is platform dependent.
|
/** Signals numbers. This is platform dependent.
|
||||||
*/
|
*/
|
||||||
export const Signal = {};
|
export const Signal: { [key: string]: number } = {};
|
||||||
|
|
||||||
export function setSignals(): void {
|
export function setSignals(): void {
|
||||||
if (build.os === "mac") {
|
if (build.os === "mac") {
|
||||||
|
|
185
cli/js/signal_test.ts
Normal file
185
cli/js/signal_test.ts
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import {
|
||||||
|
test,
|
||||||
|
testPerm,
|
||||||
|
assert,
|
||||||
|
assertEquals,
|
||||||
|
assertThrows
|
||||||
|
} from "./test_util.ts";
|
||||||
|
|
||||||
|
function defer(n: number): Promise<void> {
|
||||||
|
return new Promise((resolve, _) => {
|
||||||
|
setTimeout(resolve, n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Deno.build.os === "win") {
|
||||||
|
test(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.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<void> {
|
||||||
|
// 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<void> {
|
||||||
|
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.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();
|
||||||
|
});
|
||||||
|
}
|
148
cli/js/signals.ts
Normal file
148
cli/js/signals.ts
Normal file
|
@ -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 });
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ import "./read_link_test.ts";
|
||||||
import "./rename_test.ts";
|
import "./rename_test.ts";
|
||||||
import "./request_test.ts";
|
import "./request_test.ts";
|
||||||
import "./resources_test.ts";
|
import "./resources_test.ts";
|
||||||
|
import "./signal_test.ts";
|
||||||
import "./stat_test.ts";
|
import "./stat_test.ts";
|
||||||
import "./symbols_test.ts";
|
import "./symbols_test.ts";
|
||||||
import "./symlink_test.ts";
|
import "./symlink_test.ts";
|
||||||
|
|
|
@ -44,7 +44,7 @@ mod progress;
|
||||||
mod repl;
|
mod repl;
|
||||||
pub mod resolve_addr;
|
pub mod resolve_addr;
|
||||||
mod shell;
|
mod shell;
|
||||||
mod signal;
|
pub mod signal;
|
||||||
pub mod source_maps;
|
pub mod source_maps;
|
||||||
mod startup_data;
|
mod startup_data;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub mod random;
|
||||||
pub mod repl;
|
pub mod repl;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
pub mod runtime_compiler;
|
pub mod runtime_compiler;
|
||||||
|
pub mod signal;
|
||||||
pub mod timers;
|
pub mod timers;
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
pub mod web_worker;
|
pub mod web_worker;
|
||||||
|
|
146
cli/ops/signal.rs
Normal file
146
cli/ops/signal.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
// 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;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use crate::deno_error::bad_resource;
|
||||||
|
#[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) {
|
||||||
|
i.register_op(
|
||||||
|
"signal_bind",
|
||||||
|
s.core_op(json_op(s.stateful_op(op_signal_bind))),
|
||||||
|
);
|
||||||
|
i.register_op(
|
||||||
|
"signal_unbind",
|
||||||
|
s.core_op(json_op(s.stateful_op(op_signal_unbind))),
|
||||||
|
);
|
||||||
|
i.register_op(
|
||||||
|
"signal_poll",
|
||||||
|
s.core_op(json_op(s.stateful_op(op_signal_poll))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
/// The resource for signal stream.
|
||||||
|
/// The second element is the waker of polling future.
|
||||||
|
pub struct SignalStreamResource(pub Signal, pub Option<Waker>);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
impl Resource for SignalStreamResource {}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct BindSignalArgs {
|
||||||
|
signo: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct SignalArgs {
|
||||||
|
rid: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn op_signal_bind(
|
||||||
|
state: &ThreadSafeState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
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_signal_poll(
|
||||||
|
state: &ThreadSafeState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
let args: SignalArgs = 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::<SignalStreamResource>(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_signal_unbind(
|
||||||
|
state: &ThreadSafeState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
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::<SignalStreamResource>(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.close(rid).ok_or_else(bad_resource)?;
|
||||||
|
Ok(JsonOp::Sync(json!({})))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn op_signal_bind(
|
||||||
|
_state: &ThreadSafeState,
|
||||||
|
_args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn op_signal_unbind(
|
||||||
|
_state: &ThreadSafeState,
|
||||||
|
_args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn op_signal_poll(
|
||||||
|
_state: &ThreadSafeState,
|
||||||
|
_args: Value,
|
||||||
|
_zero_copy: Option<PinnedBuf>,
|
||||||
|
) -> Result<JsonOp, ErrBox> {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
use deno_core::ErrBox;
|
use deno_core::ErrBox;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
|
@ -200,6 +200,7 @@ impl MainWorker {
|
||||||
ops::random::init(&mut isolate, &state);
|
ops::random::init(&mut isolate, &state);
|
||||||
ops::repl::init(&mut isolate, &state);
|
ops::repl::init(&mut isolate, &state);
|
||||||
ops::resources::init(&mut isolate, &state);
|
ops::resources::init(&mut isolate, &state);
|
||||||
|
ops::signal::init(&mut isolate, &state);
|
||||||
ops::timers::init(&mut isolate, &state);
|
ops::timers::init(&mut isolate, &state);
|
||||||
ops::worker_host::init(&mut isolate, &state);
|
ops::worker_host::init(&mut isolate, &state);
|
||||||
ops::web_worker::init(&mut isolate, &state);
|
ops::web_worker::init(&mut isolate, &state);
|
||||||
|
|
|
@ -428,6 +428,39 @@ Uncaught NotFound: No such file or directory (os error 2)
|
||||||
at handleAsyncMsgFromRust (deno/js/dispatch.ts:27:17)
|
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
|
### Linking to third party code
|
||||||
|
|
||||||
In the above examples, we saw that Deno could execute scripts from URLs. Like
|
In the above examples, we saw that Deno could execute scripts from URLs. Like
|
||||||
|
|
Loading…
Reference in a new issue