Skip to content

Commit

Permalink
Allow unblocking of non-subscribed signals
Browse files Browse the repository at this point in the history
- It is possible to block only those signals that are passed as parameters to
  notify/notify_on in the main thread and all threads spawned later.
- In order not to change the default behavior, all subscribable signals
  are still blocked by default. The new behavior can be opted into by
  calling the new function unblock_signals_by_default before
  notify/notify_on.
  • Loading branch information
ftilde authored and BurntSushi committed Jul 19, 2017
1 parent af64d8a commit 8207219
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
22 changes: 22 additions & 0 deletions examples/test_block_specific.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#[macro_use]
extern crate chan;
extern crate chan_signal;

use chan_signal::{Signal, kill_this};

fn main() {
chan_signal::unblock_signals_by_default();
let r_usr1 = chan_signal::notify(&[Signal::USR1]);
kill_this(Signal::USR1);
assert_eq!(r_usr1.recv(), Some(Signal::USR1));

let (s, r_usr2) = chan::sync(1);
chan_signal::notify_on(&s, Signal::USR2);
kill_this(Signal::USR2);
assert_eq!(r_usr2.recv(), Some(Signal::USR2));

// The following will terminate the process, as it is NOT blocked
// by the main thread.
kill_this(Signal::TERM);
unreachable!();
}
39 changes: 37 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ use libc::{
SIGWINCH,

SIG_SETMASK,
SIG_BLOCK,
};
use libc::kill;
use libc::getpid;
Expand Down Expand Up @@ -202,11 +203,38 @@ pub fn notify_on(chan: &Sender<Signal>, signal: Signal) {
let mut sigs = BitSet::new();
sigs.insert(signal.as_sig() as usize);
subs.insert((*chan).clone(), sigs);

// Make sure that the signal that we want notifications on is blocked
let mut sig_set = SigSet::empty();
sig_set.add(signal.as_sig()).unwrap();
sig_set.thread_block_signals().unwrap();
}
}

/// Unblock all subscribable signals that are blocked by default.
///
/// As noted elsewhere, by default chan_signal blocks all subscribable signals and enables
/// notification upon arrival of particular signals using `notify`/`notify_on`.
/// Calling this function will change the default behavior and cause only the signals that are
/// passed to `notify`/`notify_on` to be blocked.
///
/// This CANNOT be called after any call to `notify`/`notify_on` and (as with `notify`/`notify_on`)
/// a call to this function has to be made before spawning other threads.
pub fn unblock_signals_by_default() {
{
let subs = HANDLERS.lock().unwrap();
if !subs.is_empty() {
panic!("Called unblock_signals_by_default after setting up notifications.");
}
}

SigSet::empty().thread_set_signal_mask().unwrap();
}

fn init() {
SigSet::subscribable().thread_block_signals().unwrap();
// Block all subscribable signals by default
// This can be undone by calling unblock_signals_by_default
SigSet::subscribable().thread_set_signal_mask().unwrap();
thread::spawn(move || {
let mut listen = SigSet::subscribable();
loop {
Expand Down Expand Up @@ -401,12 +429,19 @@ impl SigSet {
ok_errno(sig, errno)
}

fn thread_block_signals(&self) -> io::Result<()> {
fn thread_set_signal_mask(&self) -> io::Result<()> {
let ecode = unsafe {
pthread_sigmask(SIG_SETMASK, &self.0, ptr::null_mut())
};
ok_errno((), ecode)
}

fn thread_block_signals(&self) -> io::Result<()> {
let ecode = unsafe {
pthread_sigmask(SIG_BLOCK, &self.0, ptr::null_mut())
};
ok_errno((), ecode)
}
}

fn ok_errno<T>(ok: T, ecode: libc::c_int) -> io::Result<T> {
Expand Down

0 comments on commit 8207219

Please sign in to comment.