burntsushi / chan-signal Goto Github PK
View Code? Open in Web Editor NEWRespond to OS signals with channels.
License: The Unlicense
Respond to OS signals with channels.
License: The Unlicense
docs.rs reports "The requested crate does not exist" on the documentation page.
It would be nice if there was an API for waiting on any signal (or just some straight forward way of getting a complete slice of the signal enum without manually typing them all out).
I've got a small application that's already got a signal mask set in libc land, and just need to receive any of the signals it can get.
The chan-signal crate is currently tied to the chan
crate. If I want to deliver signals to, for example, a mio event loop, I need to write a proxy that first receives on the chan receiver and then forwards to the event loop. This is just one case, I can imagine some folks might want to use mpsc channels or some other notification mechanism.
I propose that the notify
API accept a generic Notify
(name probably needs bikeshedding) parameter. That might look something like this:
/// Thing that can receive signal notifications
pub trait Notify {
/// Called when a signal arrives
fn notify(Signal);
}
and the notify
function might look like this:
pub fn notify<N: Notify>(notifier: N, signals: &[Signal]) { /* ... */ }
A Notify
based on chan::Sender
could be added trivially with no overhead compared with the current implementation.
pub struct ChanNotify(chan::Sender<Signal>);
impl Notify for ChanNotify {
#[inline]
fn notify(signal: Signal) {
self.0.send(signal);
}
}
An implementation based on mpsc::Sender
, mio::Sender
, or whatever else a consumer might want could be added easily in their application.
I realize that this crate is chan-signal
, so it's kind of tied to the chan
crate. If these changes make sense, maybe it would be worth refactoring the core signal functionality into a generalized crate like signal-base
or something with a much better name ;).
Not sure this is a chan-signal issue or a cargo/std::process issue.
Given the following code:
extern crate chan_signal;
use std::io::{stdin, stdout};
use std::io::Write;
use std::thread;
use std::sync::atomic::*;
pub static INTR: AtomicBool = ATOMIC_BOOL_INIT;
fn main() {
let hchan = chan_signal::notify(&[chan_signal::Signal::INT]);
thread::spawn(move || {
if let Some(_) = hchan.recv() {
INTR.store(true, Ordering::Relaxed);
}
});
loop {
if INTR.load(Ordering::Relaxed) {
break;
}
}
write!(stdout(), ">>> ");
stdout().flush();
let mut line = String::new();
let input = stdin().read_line(&mut line);
println!("Input: {:?} Result: {:?}", line, input);
}
it works fine when running the compiled binary on its own: loop happens, press Ctrl-C, prompt appears, line is read from stdin. When run with cargo run
, reading the line fails with EIO
.
Consider this program:
#[macro_use]
extern crate chan;
extern crate chan_signal;
use chan::Receiver;
use chan_signal::Signal;
fn signal_received(rec: &Receiver<Signal>) -> bool
{
chan_select! {
default => { return false },
rec.recv() -> _ => { return true }
}
}
fn main() {
// Signal gets a value when the OS sent a INT or TERM signal.
let signal = chan_signal::notify(&[Signal::ILL]);
// Wait for a signal or for work to be done.
loop {
let ret = signal_received(&signal);
if ret {
println!("illegal instruction");
break
}
}
}
When I run this it only responds to SIGILL
and SIGKILL
. It effectively ignores all other signals. This came as a surprise since only SIGILL
is given to notify
. I would have expected the other signals to work as normal.
I'm trying to mix this chan module together with libc::fork()
but somehow it doesn't work.
This is probably due to some difference between thread::spawn()
and fork()
but I just don't understand why it should not work.
Here's the example from the README adapted to fork(). Interestingly the INT
signal works but the TERM
not even though sdone
is really moved into run
(this issue moved from here)
Windows does not have the signal handling mechanism of *nix. Instead it has two things:
SetConsoleCtrlHandler
which catches things like ctrl + C
in the command line.AddVectoredExceptionHandler
.sigwait
can't catch ignored signals at least in macOS (e.g., SIGIO
or SIGWINCH
).
man page of sigwait
notes:
Processes which call sigwait() on ignored signals will wait indefinitely. Ignored signals are dropped immediately by the system, before delivery to a waiting process.
chan-signal
mechanism is spawning watcher thread, wait with sigwait
, and send Signal
to chan
.
However, sigwait
ignore SIGWINCH
, and it will never be sent to chan
.
I'm using macOS, and I'm not sure this is also problem in linux.
The easiest way to resolve this is set the empty signal handler for ignored signals.
But this might cause other problems.
Is there any other way to resolve this?
Enumerable values are nice, but hard to use when I come to kill a child process with the trapped signal. Is there any chance that Signal::as_sig to be exported or am I missing something? Great library, thanks.
I have a variable that I want to be properly destroyed after a SIGINT. Therefor I have implemented the Drop trait for the type of the variable, initialize it before starting the runner thread and give the thread a reference.
Unfortunately the drop() function will never be called.
Here is an example to illustrate the problem:
#[macro_use]
extern crate chan;
extern crate chan_signal;
use std::thread;
use chan_signal::Signal;
struct Thing{
a_thing: u8
}
impl Drop for Thing {
fn drop(&mut self){
println!("Thing #{:?} dropped", self.a_thing);
}
}
fn main() {
let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);
let (sdone, rdone) = chan::sync(0);
{
let thing1 = Thing{a_thing: 1};
let thing2 = Thing{a_thing: 2};
thread::spawn(move || run( thing2, sdone));
chan_select! {
signal.recv() -> signal => {
println!("received signal: {:?}", signal)
},
rdone.recv() => {
println!("Program completed normally.");
}
}
}
}
fn run(thing: Thing, _sdone: chan::Sender<()>) {
let thing3 = Thing{a_thing: 3};
loop {
}
}
running this example will output:
received signal: Some(INT)
Thing #1 dropped
Expected would be:
received signal: Some(INT)
Thing #1 dropped
Thing #2 dropped
I would expect thing3.drop() not to be called, since its thread is terminated violently.
My rustc is at 1.19.0 and I tested the code on Kubuntu 16.04
I assume rust can't call the destructor safely because the inner state of the object might be inconsistent (e.g. maybe the runner thread already started to call the destructor and didn't finished entirely). So I don't assume that this issue will be fixed anytime soon, but I thought it might be of interest.
INT and TERM are ambiguous. They have their own meanings. So you may want to rename them to INTERRUPT and TERMINATE, because we do have the possibility to spell them in full in the 21th century. If you look to the Apple API or Java API, this is really common and helps newcomers to better understand the code.
Proceed with other constants the same way like SIGALRM
.
Consider to do a breaking change in order to have just one right way.
The way i imagine this is somewhat hard due to some design choices, and in most cases this would probably be a breaking change. My proposel would be the following
// Handle SIGTERM. channel is the writing end of the channel
chan_signal::handle([Signal::TERM], Handle(channel));
// We don't care about SIGINT
chan_signal::handle([Signal::INT], Ignore);
// Someone else has messed with SIGSEGV - lets teach them a lesson
chan_signal::handle([Signal::SEGV], Default);
Implementing this would also fix #2
Example code:
extern crate chan;
extern crate chan_signal;
use chan_signal::{Signal, notify};
fn main() {
let signal = notify(&[Signal::INT]);
println!("Signal: {:?}", signal.recv());
}
Here's what I've got from ps
on 10.9.5:
fauna:~ edwinamsler$ ps
PID TTY TIME CMD
301 ttys000 0:00.19 -bash
1560 ttys000 0:00.00 target/debug/signals
1575 ttys000 0:00.00 target/debug/signals
1601 ttys000 0:00.00 target/debug/signals
1629 ttys000 0:00.00 target/debug/signals
1645 ttys000 0:00.00 target/debug/signals
1650 ttys000 0:00.00 target/debug/signals
1678 ttys000 0:00.00 target/debug/signals
1691 ttys000 0:00.00 target/debug/signals
1696 ttys000 0:00.00 target/debug/signals
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.