Standard Library
namespace signal
Operating-system signals delivered to the program as streams.
since 0.1.0-alpha.1linux
Overview
A signal never interrupts your code. Instead signal::on hands you an InStream<int> that
receives the signal number each time the signal arrives, and you read it like any other stream:
with for, subscribe or pullOrNone. Several subscribers to one signal each get every
delivery. A signal that arrives while nobody is subscribed is dropped. Closing the stream
ends the subscription, and when the last subscriber to a signal leaves, the signal goes back to
its default behaviour. SIGKILL and SIGSTOP cannot be caught, so there are no constants
for them and subscribing to them throws.
Description
A signal is the operating system's way of tapping a process on the shoulder (a terminal was resized, someone asked it to terminate). Leviathan never runs your code inside a signal handler. Instead a signal is a stream: you subscribe to it, and each delivery appears in the stream as the signal number.
signal::on(sig) returns an InStream<int> that receives one value each time the signal is
delivered. The numbers are available as constants: signal::HUP (1), signal::INT (2),
signal::QUIT (3), signal::USR1 (10), signal::TERM (15) and signal::WINCH (28, the
terminal was resized). SIGKILL and SIGSTOP cannot be caught and are not offered.
The stream follows all the usual stream rules (see lang.streams): it can be pulled, subscribed
or iterated, it has a single consumer, and it is IDisposable.
Because a subscription holds an operating-system resource, release it. Declaring it with using
releases it on every way out of the block. When the last subscriber of a signal goes away, the
signal returns to its default behavior and the event loop no longer waits for it.
Two subscribers, released together
console.writeln("HUP=${signal::HUP} INT=${signal::INT} QUIT=${signal::QUIT}");
console.writeln("USR1=${signal::USR1} TERM=${signal::TERM} WINCH=${signal::WINCH}");
void listen() {
using InStream<int> first = signal::on(signal::TERM);
using InStream<int> second = signal::on(signal::TERM);
console.writeln("two subscribers to one signal");
}
listen();
console.writeln("both released");
HUP=1 INT=2 QUIT=3
USR1=10 TERM=15 WINCH=28
two subscribers to one signal
both released
Rules
- Each subscription is its own stream. Two calls to
signal::onfor the same signal give two streams, and every delivery is pushed into both. - Nothing is buffered before you subscribe. A signal delivered before the first subscription is dropped.
- Deliveries can coalesce. Several resizes in quick succession may arrive as fewer deliveries; the stream carries at least one value after a change.
- Closing releases.
close()(or leaving ausingblock) stops the subscription, is idempotent, and never throws. When it was the last subscriber, the signal is reset to its default action and the loop stops waiting for it. - A program that leaves a subscription open keeps running while the event loop waits for the signal, exactly like a pending timer. A program that subscribes and releases exits on its own.
- In raw mode the interrupt key is not a signal. While
term::enableRaw()is active,Ctrl-Cdoes not producesignal::INT; only an externalkilldoes. - None of the
signalfunctions can run incomptimecode.
Examples
A full-screen program reacts to terminal resizes. The loop runs for as long as the subscription is open:
using InStream<int> resizes = signal::on(signal::WINCH);
for (int s in resizes) {
term::WinSize size = term::size();
console.writeln("now ${size.cols} x ${size.rows}");
}
Notes
The constants are the Linux signal numbers.
Examples
void watch() {
using InStream<int> usr1 = signal::on(signal::USR1);
console.writeln(usr1.hasData());
}
watch();
console.writeln(signal::USR1);
console.writeln(signal::TERM);
false
10
15
Constants and globals
HUP
const int HUP = 1The signal number of SIGHUP on Linux, 1: the controlling terminal was closed.
INT
const int INT = 2The signal number of SIGINT on Linux, 2: an interrupt, usually from Ctrl-C.
QUIT
const int QUIT = 3The signal number of SIGQUIT on Linux, 3: a quit request, usually from Ctrl-backslash.
TERM
const int TERM = 15The signal number of SIGTERM on Linux, 15: a polite request to terminate.
USR1
const int USR1 = 10The signal number of SIGUSR1 on Linux, 10: a signal reserved for the application's own use.
WINCH
const int WINCH = 28The signal number of SIGWINCH on Linux, 28: the terminal window changed size.
Functions
on
on(int sig) -> InStream<int>Subscribe to a signal and get a stream of its deliveries.
Each time the signal arrives, its number is pushed into the returned stream. A signal that
arrived before the subscription is not replayed. Closing the stream, directly or with
using, ends the subscription. While the terminal is in raw mode, signal::INT is delivered
only when another process sends it, because Ctrl-C no longer generates the signal.
Parameters
- sig
- The signal number, such as
signal::TERM.
Returns
A stream that receives sig once per delivery.
Throws
RuntimeException- when the signal cannot be watched, as for
SIGKILL(9).
Examples
try {
signal::on(9);
} catch (RuntimeException e) {
console.writeln(e.message);
}
InStream<int> s = signal::on(signal::USR1);
console.writeln(s.pullOrNone() == None);
s.close();
signal: cannot watch signal 9
true
Shutting down cleanly on a termination request
InStream<int> requests = signal::on(signal::TERM);
requests.subscribe((n) => {
console.writeln("received signal ${n}, shutting down");
env::exit(0);
});
See also: InStream
See also
- InStream — The reading end of a stream: a queue of values of type
Tthat something else produces. - IDisposable — The interface of an object that must be cleaned up when its work is finished.
- term — Control of the terminal the program runs in: raw input mode and the window size.