mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
Various cleanups:
* Drop nix/lazy_static * Use previously registered handlers * Handle some more errors
This commit is contained in:
+2
-6
@@ -11,19 +11,15 @@ An implementation of an asynchronous Unix signal handling backed futures.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio-core = "0.1"
|
tokio-core = "0.1.4"
|
||||||
futures = "0.1.7"
|
futures = "0.1.7"
|
||||||
lazy_static = "0.2"
|
|
||||||
nix = "0.7"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
mio = "0.6"
|
mio = "0.6"
|
||||||
|
mio-uds = "0.6"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = "0.2"
|
winapi = "0.2"
|
||||||
kernel32-sys = "0.2"
|
kernel32-sys = "0.2"
|
||||||
mio = "0.6"
|
mio = "0.6"
|
||||||
|
|
||||||
[replace]
|
|
||||||
"tokio-core:0.1.3" = { git = "https://github.com/tokio-rs/tokio-core" }
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use futures::stream::Stream;
|
use futures::stream::Stream;
|
||||||
|
|||||||
+142
-98
@@ -7,28 +7,28 @@
|
|||||||
|
|
||||||
pub extern crate libc;
|
pub extern crate libc;
|
||||||
extern crate mio;
|
extern crate mio;
|
||||||
extern crate nix;
|
extern crate mio_uds;
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::cell::UnsafeCell;
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::os::unix::io::RawFd;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::io::prelude::*;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::unix::prelude::*;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Mutex, Once, ONCE_INIT};
|
||||||
|
|
||||||
|
use futures::future;
|
||||||
|
use futures::sync::mpsc::{Receiver, Sender, channel};
|
||||||
|
use futures::{Async, AsyncSink, Future, IntoFuture};
|
||||||
|
use futures::{Sink, Stream, Poll};
|
||||||
use self::libc::c_int;
|
use self::libc::c_int;
|
||||||
use self::nix::sys::signal::{sigaction, SigAction, SigHandler, SigSet, SA_NOCLDSTOP, SA_RESTART};
|
|
||||||
use self::nix::sys::signal::Signal as NixSignal;
|
|
||||||
use self::nix::Error as NixError;
|
|
||||||
use self::nix::Errno;
|
|
||||||
use self::nix::sys::socket::{recv, send, socketpair, AddressFamily, SockType, SockFlag, MSG_DONTWAIT};
|
|
||||||
use self::mio::{Evented, Token, Ready, PollOpt};
|
|
||||||
use self::mio::Poll as MioPoll;
|
use self::mio::Poll as MioPoll;
|
||||||
use self::mio::unix::EventedFd;
|
use self::mio::unix::EventedFd;
|
||||||
use futures::{Async, AsyncSink, Future, IntoFuture};
|
use self::mio::{Evented, Token, Ready, PollOpt};
|
||||||
use futures::sync::mpsc::{Receiver, Sender, channel};
|
use self::mio_uds::UnixStream;
|
||||||
use futures::{Sink, Stream, Poll};
|
|
||||||
use tokio_core::reactor::{Handle, CoreId, PollEvented};
|
|
||||||
use tokio_core::io::IoFuture;
|
use tokio_core::io::IoFuture;
|
||||||
|
use tokio_core::reactor::{Handle, CoreId, PollEvented};
|
||||||
|
|
||||||
pub use self::libc::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2};
|
pub use self::libc::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2};
|
||||||
pub use self::libc::{SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGTRAP};
|
pub use self::libc::{SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGTRAP};
|
||||||
@@ -36,82 +36,133 @@ pub use self::libc::{SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGTRAP};
|
|||||||
// Number of different unix signals
|
// Number of different unix signals
|
||||||
const SIGNUM: usize = 32;
|
const SIGNUM: usize = 32;
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SignalInfo {
|
struct SignalInfo {
|
||||||
initialized: bool,
|
pending: AtomicBool,
|
||||||
// The ones interested in this signal
|
// The ones interested in this signal
|
||||||
recipients: Vec<Sender<c_int>>,
|
recipients: Mutex<Vec<Sender<c_int>>>,
|
||||||
// TODO: Other stuff, like the previous sigaction to call
|
|
||||||
|
init: Once,
|
||||||
|
initialized: UnsafeCell<bool>,
|
||||||
|
prev: UnsafeCell<libc::sigaction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Globals {
|
struct Globals {
|
||||||
pending: [AtomicBool; SIGNUM],
|
sender: UnixStream,
|
||||||
sender: RawFd,
|
receiver: UnixStream,
|
||||||
receiver: RawFd,
|
signals: [SignalInfo; SIGNUM],
|
||||||
signals: [Mutex<SignalInfo>; SIGNUM],
|
|
||||||
drivers: Mutex<HashSet<CoreId>>,
|
drivers: Mutex<HashSet<CoreId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Globals {
|
impl Default for SignalInfo {
|
||||||
fn new() -> Self {
|
fn default() -> SignalInfo {
|
||||||
// TODO: Better error handling
|
SignalInfo {
|
||||||
// We use socket pair instead of pipe, as it allows send() and recv().
|
pending: AtomicBool::new(false),
|
||||||
let (receiver, sender) = socketpair(AddressFamily::Unix, SockType::Stream, 0, SockFlag::empty()).unwrap();
|
init: ONCE_INIT,
|
||||||
Globals {
|
initialized: UnsafeCell::new(false),
|
||||||
// Bunch of false values
|
recipients: Mutex::new(Vec::new()),
|
||||||
pending: Default::default(),
|
prev: UnsafeCell::new(unsafe { mem::zeroed() }),
|
||||||
sender: sender,
|
|
||||||
receiver: receiver,
|
|
||||||
signals: Default::default(),
|
|
||||||
drivers: Mutex::new(HashSet::new()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
static mut GLOBALS: *mut Globals = 0 as *mut Globals;
|
||||||
// TODO: Get rid of lazy_static once the prototype is done ‒ get rid of the dependency as well
|
|
||||||
// as the possible lock in there, which *might* be problematic in signals
|
fn globals() -> &'static Globals {
|
||||||
static ref GLOBALS: Globals = Globals::new();
|
static INIT: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
INIT.call_once(|| {
|
||||||
|
let (receiver, sender) = UnixStream::pair().unwrap();
|
||||||
|
let globals = Globals {
|
||||||
|
sender: sender,
|
||||||
|
receiver: receiver,
|
||||||
|
signals: Default::default(),
|
||||||
|
drivers: Mutex::new(HashSet::new()),
|
||||||
|
};
|
||||||
|
GLOBALS = Box::into_raw(Box::new(globals));
|
||||||
|
});
|
||||||
|
&*GLOBALS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag the relevant signal and wake up through a self-pipe
|
// Flag the relevant signal and wake up through a self-pipe
|
||||||
extern "C" fn pipe_wakeup(signal: c_int) {
|
extern fn handler(signum: c_int,
|
||||||
let index = signal as usize;
|
info: *mut libc::siginfo_t,
|
||||||
// TODO: Handle the old signal handler
|
ptr: *mut libc::c_void) {
|
||||||
// It might be good enough to use some lesser ordering than this, but how to prove it?
|
type FnSigaction = extern fn(c_int, *mut libc::siginfo_t, *mut libc::c_void);
|
||||||
GLOBALS.pending[index].store(true, Ordering::SeqCst);
|
type FnHandler = extern fn(c_int);
|
||||||
// Send a wakeup, ignore any errors (anything reasonably possible is full pipe and then it will
|
unsafe {
|
||||||
// wake up anyway).
|
let slot = match (*GLOBALS).signals.get(signum as usize) {
|
||||||
let _ = send(GLOBALS.sender, &[0u8], MSG_DONTWAIT);
|
Some(slot) => slot,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
slot.pending.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
// Send a wakeup, ignore any errors (anything reasonably possible is
|
||||||
|
// full pipe and then it will wake up anyway).
|
||||||
|
drop((*GLOBALS).sender.write(&[1]));
|
||||||
|
|
||||||
|
let fnptr = (*slot.prev.get()).sa_sigaction;
|
||||||
|
if fnptr == 0 || fnptr == libc::SIG_DFL || fnptr == libc::SIG_IGN {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (*slot.prev.get()).sa_flags & libc::SA_SIGINFO == 0 {
|
||||||
|
let action = mem::transmute::<usize, FnHandler>(fnptr);
|
||||||
|
action(signum)
|
||||||
|
} else {
|
||||||
|
let action = mem::transmute::<usize, FnSigaction>(fnptr);
|
||||||
|
action(signum, info, ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we listen to the given signal and provide the recipient end of the self-pipe
|
// Make sure we listen to the given signal and provide the recipient end of the
|
||||||
fn signal_enable(signal: c_int) {
|
// self-pipe
|
||||||
let index = signal as usize;
|
fn signal_enable(signal: c_int) -> io::Result<()> {
|
||||||
let mut siginfo = GLOBALS.signals[index].lock().unwrap();
|
let siginfo = &globals().signals[signal as usize];
|
||||||
if !siginfo.initialized {
|
unsafe {
|
||||||
let action = SigAction::new(SigHandler::Handler(pipe_wakeup), SA_NOCLDSTOP | SA_RESTART, SigSet::empty());
|
let mut err = None;
|
||||||
unsafe { sigaction(NixSignal::from_c_int(signal).unwrap(), &action).unwrap() };
|
siginfo.init.call_once(|| {
|
||||||
// TODO: Handle the old signal handler
|
let mut new: libc::sigaction = mem::zeroed();
|
||||||
siginfo.initialized = true;
|
new.sa_sigaction = handler as usize;
|
||||||
}
|
new.sa_flags = libc::SA_RESTART |
|
||||||
|
libc::SA_SIGINFO |
|
||||||
|
libc::SA_NOCLDSTOP;
|
||||||
|
if libc::sigaction(signal, &new, &mut *siginfo.prev.get()) != 0 {
|
||||||
|
err = Some(io::Error::last_os_error());
|
||||||
|
} else {
|
||||||
|
*siginfo.initialized.get() = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some(err) = err {
|
||||||
|
return Err(err)
|
||||||
|
}
|
||||||
|
if *siginfo.initialized.get() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other,
|
||||||
|
"failed to register signal handler"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventedReceiver;
|
struct EventedReceiver;
|
||||||
|
|
||||||
impl Evented for EventedReceiver {
|
impl Evented for EventedReceiver {
|
||||||
fn register(&self, poll: &MioPoll, token: Token, events: Ready, opts: PollOpt) -> io::Result<()> {
|
fn register(&self, poll: &MioPoll, token: Token, events: Ready, opts: PollOpt) -> io::Result<()> {
|
||||||
EventedFd(&GLOBALS.receiver).register(poll, token, events, opts)
|
let fd = globals().receiver.as_raw_fd();
|
||||||
|
EventedFd(&fd).register(poll, token, events, opts)
|
||||||
}
|
}
|
||||||
fn reregister(&self, poll: &MioPoll, token: Token, events: Ready, opts: PollOpt) -> io::Result<()> {
|
fn reregister(&self, poll: &MioPoll, token: Token, events: Ready, opts: PollOpt) -> io::Result<()> {
|
||||||
EventedFd(&GLOBALS.receiver).reregister(poll, token, events, opts)
|
let fd = globals().receiver.as_raw_fd();
|
||||||
|
EventedFd(&fd).reregister(poll, token, events, opts)
|
||||||
}
|
}
|
||||||
fn deregister(&self, poll: &MioPoll) -> io::Result<()> {
|
fn deregister(&self, poll: &MioPoll) -> io::Result<()> {
|
||||||
EventedFd(&GLOBALS.receiver).deregister(poll)
|
let fd = globals().receiver.as_raw_fd();
|
||||||
|
EventedFd(&fd).deregister(poll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// There'll be stuff inside
|
|
||||||
struct Driver {
|
struct Driver {
|
||||||
id: CoreId,
|
id: CoreId,
|
||||||
wakeup: PollEvented<EventedReceiver>,
|
wakeup: PollEvented<EventedReceiver>,
|
||||||
@@ -134,7 +185,7 @@ impl Future for Driver {
|
|||||||
|
|
||||||
impl Drop for Driver {
|
impl Drop for Driver {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut drivers = GLOBALS.drivers.lock().unwrap();
|
let mut drivers = globals().drivers.lock().unwrap();
|
||||||
drivers.remove(&self.id);
|
drivers.remove(&self.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,13 +207,11 @@ impl Driver {
|
|||||||
}
|
}
|
||||||
// Read all available data (until EAGAIN)
|
// Read all available data (until EAGAIN)
|
||||||
let mut received = false;
|
let mut received = false;
|
||||||
let mut buffer = [0; 1024];
|
|
||||||
loop {
|
loop {
|
||||||
match recv(GLOBALS.receiver, &mut buffer, MSG_DONTWAIT) {
|
match (&globals().receiver).read(&mut [0; 128]) {
|
||||||
Ok(0) => panic!("EOF on self-pipe"),
|
Ok(0) => panic!("EOF on self-pipe"),
|
||||||
Ok(_) => received = true,
|
Ok(_) => received = true,
|
||||||
Err(NixError::Sys(Errno::EAGAIN)) => break,
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
|
||||||
Err(NixError::Sys(Errno::EINTR)) => (),
|
|
||||||
Err(e) => panic!("Bad read on self-pipe: {}", e),
|
Err(e) => panic!("Bad read on self-pipe: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,30 +223,26 @@ impl Driver {
|
|||||||
}
|
}
|
||||||
// Go through all the signals and broadcast everything
|
// Go through all the signals and broadcast everything
|
||||||
fn broadcast(&self) {
|
fn broadcast(&self) {
|
||||||
for (sig, value) in GLOBALS.pending.iter().enumerate() {
|
for (sig, slot) in globals().signals.iter().enumerate() {
|
||||||
// Any signal of this kind arrived since we checked last?
|
// Any signal of this kind arrived since we checked last?
|
||||||
if value.swap(false, Ordering::SeqCst) {
|
if !slot.pending.swap(false, Ordering::SeqCst) {
|
||||||
let signum = sig as c_int;
|
continue
|
||||||
let mut siginfo = GLOBALS.signals[sig].lock().unwrap();
|
}
|
||||||
// It doesn't seem to be possible to do this through the iterators for now.
|
|
||||||
// This trick is copied from https://github.com/rust-lang/rfcs/pull/1353.
|
let signum = sig as c_int;
|
||||||
for i in (0 .. siginfo.recipients.len()).rev() {
|
let mut recipients = slot.recipients.lock().unwrap();
|
||||||
// TODO: This thing probably generates unnecessary wakups of this task.
|
|
||||||
// But let's optimise it later on, when we know this works.
|
// Notify all waiters on this signal that the signal has been
|
||||||
match siginfo.recipients[i].start_send(signum) {
|
// received. If we can't push a message into the queue then we don't
|
||||||
// We don't care if it was full or not ‒ we just want to wake up the other
|
// worry about it as everything is coalesced anyway.
|
||||||
// side.
|
for i in (0..recipients.len()).rev() {
|
||||||
Ok(AsyncSink::Ready) => {
|
// TODO: This thing probably generates unnecessary wakups of
|
||||||
// We are required to call this if we push something inside
|
// this task. But let's optimise it later on, when we
|
||||||
let _ = siginfo.recipients[i].poll_complete();
|
// know this works.
|
||||||
},
|
match recipients[i].start_send(signum) {
|
||||||
// The channel is full -> it'll get woken up anyway
|
Ok(AsyncSink::Ready) => {}
|
||||||
Ok(AsyncSink::NotReady(_)) => (),
|
Ok(AsyncSink::NotReady(_)) => {}
|
||||||
// The other side disappeared, drop this end.
|
Err(_) => { recipients.swap_remove(i); }
|
||||||
Err(_) => {
|
|
||||||
siginfo.recipients.swap_remove(i);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,25 +313,24 @@ impl Signal {
|
|||||||
/// multiple times. When a signal is received then all the associated
|
/// multiple times. When a signal is received then all the associated
|
||||||
/// channels will receive the signal notification.
|
/// channels will receive the signal notification.
|
||||||
pub fn new(signal: c_int, handle: &Handle) -> IoFuture<Signal> {
|
pub fn new(signal: c_int, handle: &Handle) -> IoFuture<Signal> {
|
||||||
let index = signal as usize;
|
|
||||||
// One wakeup in a queue is enough
|
|
||||||
let (sender, receiver) = channel(1);
|
|
||||||
{
|
|
||||||
let mut siginfo = GLOBALS.signals[index].lock().unwrap();
|
|
||||||
siginfo.recipients.push(sender);
|
|
||||||
}
|
|
||||||
// Turn the signal delivery on once we are ready for it
|
// Turn the signal delivery on once we are ready for it
|
||||||
signal_enable(signal);
|
if let Err(e) = signal_enable(signal) {
|
||||||
|
return future::err(e).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
// One wakeup in a queue is enough
|
||||||
|
let (tx, rx) = channel(1);
|
||||||
|
globals().signals[signal as usize].recipients.lock().unwrap().push(tx);
|
||||||
let id = handle.id();
|
let id = handle.id();
|
||||||
{
|
{
|
||||||
let mut drivers = GLOBALS.drivers.lock().unwrap();
|
let mut drivers = globals().drivers.lock().unwrap();
|
||||||
if !drivers.contains(&id) {
|
if !drivers.contains(&id) {
|
||||||
handle.spawn(Driver::new(handle));
|
handle.spawn(Driver::new(handle));
|
||||||
drivers.insert(id);
|
drivers.insert(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Init the driving task for this handle
|
// TODO: Init the driving task for this handle
|
||||||
Ok(Signal(receiver)).into_future().boxed()
|
Ok(Signal(rx)).into_future().boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user