tokio-signal-0.2.7/Cargo.toml.orig010064400007650000024000000021701337536364400152440ustar0000000000000000[package] name = "tokio-signal" # When releasing to crates.io: # - Update html_root_url. # - Update CHANGELOG.md. # - Update doc URL. # - Create "v0.2.x" git tag. version = "0.2.7" authors = ["Alex Crichton "] license = "MIT" repository = "https://github.com/tokio-rs/tokio" homepage = "https://github.com/tokio-rs/tokio" documentation = "https://docs.rs/tokio-signal/0.2.7/tokio_signal" description = """ An implementation of an asynchronous Unix signal handling backed futures. """ categories = ["asynchronous"] [badges] travis-ci = { repository = "tokio-rs/tokio" } appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" } [dependencies] futures = "0.1.11" mio = "0.6.14" tokio-reactor = { version = "0.1.0", path = "../tokio-reactor" } tokio-executor = { version = "0.1.0", path = "../tokio-executor" } tokio-io = { version = "0.1", path = "../tokio-io" } [target.'cfg(unix)'.dependencies] libc = "0.2" mio-uds = "0.6" signal-hook = "0.1" [dev-dependencies] tokio = { version = "0.1.8", path = ".." } [target.'cfg(windows)'.dependencies.winapi] version = "0.3" features = ["minwindef", "wincon"] tokio-signal-0.2.7/Cargo.toml0000644000000030600000000000000114760ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "tokio-signal" version = "0.2.7" authors = ["Alex Crichton "] description = "An implementation of an asynchronous Unix signal handling backed futures.\n" homepage = "https://github.com/tokio-rs/tokio" documentation = "https://docs.rs/tokio-signal/0.2.7/tokio_signal" categories = ["asynchronous"] license = "MIT" repository = "https://github.com/tokio-rs/tokio" [dependencies.futures] version = "0.1.11" [dependencies.mio] version = "0.6.14" [dependencies.tokio-executor] version = "0.1.0" [dependencies.tokio-io] version = "0.1" [dependencies.tokio-reactor] version = "0.1.0" [dev-dependencies.tokio] version = "0.1.8" [target."cfg(unix)".dependencies.libc] version = "0.2" [target."cfg(unix)".dependencies.mio-uds] version = "0.6" [target."cfg(unix)".dependencies.signal-hook] version = "0.1" [target."cfg(windows)".dependencies.winapi] version = "0.3" features = ["minwindef", "wincon"] [badges.appveyor] id = "s83yxhy9qeb58va7" repository = "carllerche/tokio" [badges.travis-ci] repository = "tokio-rs/tokio" tokio-signal-0.2.7/CHANGELOG.md010064400007650000024000000020301337536400600141520ustar0000000000000000## 0.2.7 - (November 21, 2018) ### Changed * `unix::Signal` now implements `Sync` * minimize allocations ### Fixes * `unix::Signal` now avoids extraneous wakeups generated as a result of dropping other instances ## 0.2.6 - (October 26, 2018) ### Changed * Use the `signal-hook` crate for managing signal registrations ## 0.2.5 - (September 29, 2018) ### Fixes * Fix a possible starvation when polling multiple `Signal` instances outside of a tokio reactor (e.g. by using `Future::wait`) ## 0.2.4 - (August 25, 2018) ### Fixes * Actually make `unix::bsd` public ## 0.2.3 - (August 25, 2018) ### Features * Exposes `SIGINFO` on BSD-based operating systems. ## 0.2.2 - (August 14, 2018) ### Fixes * Fix starvation of `Signal`s whenever a `Signal` instance is dropped * Fix starvation of individual `Signal`s based on their creation order ## 0.2.1 - (May 27, 2018) ### Fixes * Bump minimum supported version of `mio` to 0.6.14 ## 0.2.0 - (May 7, 2018) #### Features * Uses `tokio` instead of `tokio_core` * Supports all 33 signals on FreeBSD tokio-signal-0.2.7/examples/ctrl-c.rs010064400007650000024000000045151337504616300157240ustar0000000000000000extern crate futures; extern crate tokio; extern crate tokio_signal; use futures::{Future, Stream}; /// how many signals to handle before exiting const STOP_AFTER: u64 = 10; fn main() -> Result<(), Box> { // tokio_signal provides a convenience builder for Ctrl+C // this even works cross-platform: linux and windows! // // `fn ctrl_c()` produces a `Future` of the actual stream-initialisation // the `flatten_stream()` convenience method lazily defers that // initialisation, allowing us to use it 'as if' it is already the // stream we want, reducing boilerplate Future-handling. let endless_stream = tokio_signal::ctrl_c().flatten_stream(); // don't keep going forever: convert the endless stream to a bounded one. let limited_stream = endless_stream.take(STOP_AFTER); // how many Ctrl+C have we received so far? let mut counter = 0; println!( "This program is now waiting for you to press Ctrl+C {0} times. * If running via `cargo run --example ctrl-c`, Ctrl+C also kills it, \ due to https://github.com/rust-lang-nursery/rustup.rs/issues/806 * If running the binary directly, the Ctrl+C is properly trapped. Terminate by repeating Ctrl+C {0} times, or ahead of time by \ opening a second terminal and issuing `pkill -sigkil ctrl-c`", STOP_AFTER ); // Stream::for_each is a powerful primitive provided by the Futures crate. // It turns a Stream into a Future that completes after all stream-items // have been completed, or the first time the closure returns an error let future = limited_stream.for_each(|()| { // Note how we manipulate the counter without any fancy synchronisation. // The borrowchecker realises there can't be any conflicts, so the closure // can just capture it. counter += 1; println!( "Ctrl+C received {} times! {} more before exit", counter, STOP_AFTER - counter ); // return Ok-result to continue handling the stream Ok(()) }); // Up until now, we haven't really DONE anything, just prepared // now it's time to actually schedule, and thus execute, the stream // on our event loop tokio::runtime::current_thread::block_on_all(future)?; println!("Stream ended, quiting the program."); Ok(()) } tokio-signal-0.2.7/examples/multiple.rs010064400007650000024000000031471337504616300163730ustar0000000000000000//! A small example of how to listen for two signals at the same time extern crate futures; extern crate tokio; extern crate tokio_signal; // A trick to not fail build on non-unix platforms when using unix-specific features. #[cfg(unix)] mod platform { use futures::{Future, Stream}; use tokio_signal::unix::{Signal, SIGINT, SIGTERM}; pub fn main() -> Result<(), Box<::std::error::Error>> { // Create a stream for each of the signals we'd like to handle. let sigint = Signal::new(SIGINT).flatten_stream(); let sigterm = Signal::new(SIGTERM).flatten_stream(); // Use the `select` combinator to merge these two streams into one let stream = sigint.select(sigterm); // Wait for a signal to arrive println!("Waiting for SIGINT or SIGTERM"); println!( " TIP: use `pkill -sigint multiple` from a second terminal \ to send a SIGINT to all processes named 'multiple' \ (i.e. this binary)" ); let (item, _rest) = ::tokio::runtime::current_thread::block_on_all(stream.into_future()) .map_err(|_| "failed to wait for signals")?; // Figure out which signal we received let item = item.ok_or("received no signal")?; if item == SIGINT { println!("received SIGINT"); } else { assert_eq!(item, SIGTERM); println!("received SIGTERM"); } Ok(()) } } #[cfg(not(unix))] mod platform { pub fn main() -> Result<(), Box<::std::error::Error>> {Ok(())} } fn main() -> Result<(), Box> { platform::main() } tokio-signal-0.2.7/examples/sighup-example.rs010064400007650000024000000031351337504616300174650ustar0000000000000000extern crate futures; extern crate tokio; extern crate tokio_signal; // A trick to not fail build on non-unix platforms when using unix-specific features. #[cfg(unix)] mod platform { use futures::{Future, Stream}; use tokio_signal::unix::{Signal, SIGHUP}; pub fn main() -> Result<(), Box<::std::error::Error>> { // on Unix, we can listen to whatever signal we want, in this case: SIGHUP let stream = Signal::new(SIGHUP).flatten_stream(); println!("Waiting for SIGHUPS (Ctrl+C to quit)"); println!( " TIP: use `pkill -sighup sighup-example` from a second terminal \ to send a SIGHUP to all processes named 'sighup-example' \ (i.e. this binary)" ); // for_each is a powerful primitive provided by the Futures crate // it turns a Stream into a Future that completes after all stream-items // have been completed. let future = stream.for_each(|the_signal| { println!( "*Got signal {:#x}* I should probably reload my config \ or something", the_signal ); Ok(()) }); // Up until now, we haven't really DONE anything, just prepared // now it's time to actually schedule, and thus execute, the stream // on our event loop, and loop forever ::tokio::runtime::current_thread::block_on_all(future)?; Ok(()) } } #[cfg(not(unix))] mod platform { pub fn main() -> Result<(), Box<::std::error::Error>> {Ok(())} } fn main() -> Result<(), Box> { platform::main() } tokio-signal-0.2.7/LICENSE010064400007650000024000000020531337472307400133560ustar0000000000000000Copyright (c) 2016-2018 Tokio contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tokio-signal-0.2.7/README.md010064400007650000024000000030151337472307400136270ustar0000000000000000# tokio-signal An implementation of Unix signal handling for Tokio [![Travis Build Status][travis-badge]][travis-url] [![Appveyor Build Status][appveyor-badge]][appveyor-url] [travis-badge]: https://travis-ci.org/tokio-rs/tokio.svg?branch=master [travis-url]: https://travis-ci.org/tokio-rs/tokio [appveyor-badge]: https://ci.appveyor.com/api/projects/status/s83yxhy9qeb58va7/branch/master?svg=true [appveyor-url]: https://ci.appveyor.com/project/carllerche/tokio/branch/master [Documentation](https://docs.rs/tokio-signal) ## Usage First, add this to your `Cargo.toml`: ```toml [dependencies] tokio-signal = "0.2" ``` Next you can use this in conjunction with the `tokio` and `futures` crates: ```rust,no_run extern crate futures; extern crate tokio; extern crate tokio_signal; use futures::{Future, Stream}; fn main() { // Create an infinite stream of "Ctrl+C" notifications. Each item received // on this stream may represent multiple ctrl-c signals. let ctrl_c = tokio_signal::ctrl_c().flatten_stream(); // Process each ctrl-c as it comes in let prog = ctrl_c.for_each(|()| { println!("ctrl-c received!"); Ok(()) }); tokio::run(prog.map_err(|err| panic!("{}", err))); } ``` # License This project is licensed the MIT license ([LICENSE](LICENSE) or http://opensource.org/licenses/MIT). ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tokio by you, shall be licensed as MIT, without any additional terms or conditions. tokio-signal-0.2.7/src/lib.rs010064400007650000024000000114151337536365700142660ustar0000000000000000//! Asynchronous signal handling for Tokio //! //! This crate implements asynchronous signal handling for Tokio, an //! asynchronous I/O framework in Rust. The primary type exported from this //! crate, `unix::Signal`, allows listening for arbitrary signals on Unix //! platforms, receiving them in an asynchronous fashion. //! //! Note that signal handling is in general a very tricky topic and should be //! used with great care. This crate attempts to implement 'best practice' for //! signal handling, but it should be evaluated for your own applications' needs //! to see if it's suitable. //! //! The are some fundamental limitations of this crate documented on the //! `Signal` structure as well. //! //! # Examples //! //! Print out all ctrl-C notifications received //! //! ```rust,no_run //! extern crate futures; //! extern crate tokio; //! extern crate tokio_signal; //! //! use futures::{Future, Stream}; //! //! fn main() { //! // Create an infinite stream of "Ctrl+C" notifications. Each item received //! // on this stream may represent multiple ctrl-c signals. //! let ctrl_c = tokio_signal::ctrl_c().flatten_stream(); //! //! // Process each ctrl-c as it comes in //! let prog = ctrl_c.for_each(|()| { //! println!("ctrl-c received!"); //! Ok(()) //! }); //! //! tokio::runtime::current_thread::block_on_all(prog).unwrap(); //! } //! ``` //! //! Wait for SIGHUP on Unix //! //! ```rust,no_run //! # extern crate futures; //! # extern crate tokio; //! # extern crate tokio_signal; //! # #[cfg(unix)] //! # mod foo { //! # //! extern crate futures; //! extern crate tokio; //! extern crate tokio_signal; //! //! use futures::{Future, Stream}; //! use tokio_signal::unix::{Signal, SIGHUP}; //! //! fn main() { //! // Like the previous example, this is an infinite stream of signals //! // being received, and signals may be coalesced while pending. //! let stream = Signal::new(SIGHUP).flatten_stream(); //! //! // Convert out stream into a future and block the program //! tokio::runtime::current_thread::block_on_all(stream.into_future()).ok().unwrap(); //! } //! # } //! # fn main() {} //! ``` #![doc(html_root_url = "https://docs.rs/tokio-signal/0.2.7")] #![deny(missing_docs)] extern crate futures; extern crate mio; extern crate tokio_executor; extern crate tokio_io; extern crate tokio_reactor; use std::io; use futures::stream::Stream; use futures::{future, Future}; use tokio_reactor::Handle; pub mod unix; pub mod windows; /// A future whose error is `io::Error` pub type IoFuture = Box + Send>; /// A stream whose error is `io::Error` pub type IoStream = Box + Send>; /// Creates a stream which receives "ctrl-c" notifications sent to a process. /// /// In general signals are handled very differently across Unix and Windows, but /// this is somewhat cross platform in terms of how it can be handled. A ctrl-c /// event to a console process can be represented as a stream for both Windows /// and Unix. /// /// This function binds to the default event loop. Note that /// there are a number of caveats listening for signals, and you may wish to /// read up on the documentation in the `unix` or `windows` module to take a /// peek. pub fn ctrl_c() -> IoFuture> { ctrl_c_handle(&Handle::current()) } /// Creates a stream which receives "ctrl-c" notifications sent to a process. /// /// In general signals are handled very differently across Unix and Windows, but /// this is somewhat cross platform in terms of how it can be handled. A ctrl-c /// event to a console process can be represented as a stream for both Windows /// and Unix. /// /// This function receives a `Handle` to an event loop and returns a future /// which when resolves yields a stream receiving all signal events. Note that /// there are a number of caveats listening for signals, and you may wish to /// read up on the documentation in the `unix` or `windows` module to take a /// peek. pub fn ctrl_c_handle(handle: &Handle) -> IoFuture> { return ctrl_c_imp(handle); #[cfg(unix)] fn ctrl_c_imp(handle: &Handle) -> IoFuture> { let handle = handle.clone(); Box::new(future::lazy(move || { unix::Signal::with_handle(unix::libc::SIGINT, &handle) .map(|x| Box::new(x.map(|_| ())) as Box + Send>) })) } #[cfg(windows)] fn ctrl_c_imp(handle: &Handle) -> IoFuture> { let handle = handle.clone(); // Use lazy to ensure that `ctrl_c` gets called while on an event loop Box::new(future::lazy(move || { windows::Event::ctrl_c_handle(&handle) .map(|x| Box::new(x) as Box + Send>) })) } } tokio-signal-0.2.7/src/unix.rs010064400007650000024000000373261337472307400145040ustar0000000000000000//! Unix-specific types for signal handling. //! //! This module is only defined on Unix platforms and contains the primary //! `Signal` type for receiving notifications of signals. #![cfg(unix)] pub extern crate libc; extern crate mio; extern crate mio_uds; extern crate signal_hook; use std::io::{self, Error, ErrorKind}; use std::io::prelude::*; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Mutex, Once, ONCE_INIT}; use self::libc::c_int; use self::mio_uds::UnixStream; use futures::future; use futures::sync::mpsc::{channel, Receiver, Sender}; use futures::{Async, Future}; use futures::{Poll, Stream}; use tokio_reactor::{Handle, PollEvented}; use tokio_io::IoFuture; pub use self::libc::{SIGUSR1, SIGUSR2, SIGINT, SIGTERM}; pub use self::libc::{SIGALRM, SIGHUP, SIGPIPE, SIGQUIT, SIGTRAP}; /// BSD-specific definitions #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] pub mod bsd { #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] pub use super::libc::SIGINFO; } // Number of different unix signals // (FreeBSD has 33) const SIGNUM: usize = 33; type SignalSender = Sender; struct SignalInfo { pending: AtomicBool, // The ones interested in this signal recipients: Mutex>>, init: Once, initialized: AtomicBool, } struct Globals { sender: UnixStream, receiver: UnixStream, signals: Vec, } impl Globals { /// Register a new `Signal` instance's channel sender. /// Returns a `SignalId` which should be later used for deregistering /// this sender. fn register_signal_sender(signal: c_int, tx: SignalSender) -> SignalId { let tx = Box::new(tx); let id = SignalId::from(&tx); let idx = signal as usize; globals().signals[idx].recipients.lock().unwrap().push(tx); id } /// Deregister a `Signal` instance's channel sender because the `Signal` /// is no longer interested in receiving events (e.g. dropped). fn deregister_signal_receiver(signal: c_int, id: SignalId) { let idx = signal as usize; let mut list = globals().signals[idx].recipients.lock().unwrap(); list.retain(|sender| SignalId::from(sender) != id); } } /// A newtype which represents a unique identifier for each `Signal` instance. /// The id is derived by boxing the channel `Sender` associated with this instance /// and using its address in memory. #[derive(Debug, Copy, Clone, PartialEq, Eq)] struct SignalId(usize); impl<'a> From<&'a Box> for SignalId { fn from(tx: &'a Box) -> Self { SignalId(&**tx as *const _ as usize) } } impl Default for SignalInfo { fn default() -> SignalInfo { SignalInfo { pending: AtomicBool::new(false), init: ONCE_INIT, initialized: AtomicBool::new(false), recipients: Mutex::new(Vec::new()), } } } static mut GLOBALS: *mut Globals = 0 as *mut Globals; fn globals() -> &'static Globals { static INIT: Once = ONCE_INIT; unsafe { INIT.call_once(|| { let (receiver, sender) = UnixStream::pair().unwrap(); let globals = Globals { sender: sender, receiver: receiver, signals: (0..SIGNUM).map(|_| Default::default()).collect(), }; GLOBALS = Box::into_raw(Box::new(globals)); }); &*GLOBALS } } /// Our global signal handler for all signals registered by this module. /// /// The purpose of this signal handler is to primarily: /// /// 1. Flag that our specific signal was received (e.g. store an atomic flag) /// 2. Wake up driver tasks by writing a byte to a pipe /// /// Those two operations shoudl both be async-signal safe. fn action(slot: &SignalInfo, mut sender: &UnixStream) { 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(sender.write(&[1])); } /// Enable this module to receive signal notifications for the `signal` /// provided. /// /// This will register the signal handler if it hasn't already been registered, /// returning any error along the way if that fails. fn signal_enable(signal: c_int) -> io::Result<()> { if signal_hook::FORBIDDEN.contains(&signal) { return Err(Error::new(ErrorKind::Other, format!("Refusing to register signal {}", signal))); } let globals = globals(); let siginfo = match globals.signals.get(signal as usize) { Some(slot) => slot, None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")), }; let mut registered = Ok(()); siginfo.init.call_once(|| { registered = unsafe { signal_hook::register(signal, move || action(siginfo, &globals.sender)).map(|_| ()) }; if registered.is_ok() { siginfo.initialized.store(true, Ordering::Relaxed); } }); registered?; // If the call_once failed, it won't be retried on the next attempt to register the signal. In // such case it is not run, registered is still `Ok(())`, initialized is still false. if siginfo.initialized.load(Ordering::Relaxed) { Ok(()) } else { Err(Error::new(ErrorKind::Other, "Failed to register signal handler")) } } struct Driver { wakeup: PollEvented, } impl Future for Driver { type Item = (); type Error = (); fn poll(&mut self) -> Poll<(), ()> { // Drain the data from the pipe and maintain interest in getting more self.drain(); // Broadcast any signals which were received self.broadcast(); // This task just lives until the end of the event loop Ok(Async::NotReady) } } impl Driver { fn new(handle: &Handle) -> io::Result { // NB: We give each driver a "fresh" reciever file descriptor to avoid // the issues described in alexcrichton/tokio-process#42. // // In the past we would reuse the actual receiver file descriptor and // swallow any errors around double registration of the same descriptor. // I'm not sure if the second (failed) registration simply doesn't end up // receiving wake up notifications, or there could be some race condition // when consuming readiness events, but having distinct descriptors for // distinct PollEvented instances appears to mitigate this. // // Unfortunately we cannot just use a single global PollEvented instance // either, since we can't compare Handles or assume they will always // point to the exact same reactor. let stream = globals().receiver.try_clone()?; let wakeup = PollEvented::new_with_handle(stream, handle)?; Ok(Driver { wakeup: wakeup, }) } /// Drain all data in the global receiver, ensuring we'll get woken up when /// there is a write on the other end. /// /// We do *NOT* use the existence of any read bytes as evidence a sigal was /// received since the `pending` flags would have already been set if that /// was the case. See #38 for more info. fn drain(&mut self) { loop { match self.wakeup.read(&mut [0; 128]) { Ok(0) => panic!("EOF on self-pipe"), Ok(_) => {}, Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break, Err(e) => panic!("Bad read on self-pipe: {}", e), } } } /// Go through all the signals and broadcast everything. /// /// Driver tasks wake up for *any* signal and simply process all globally /// registered signal streams, so each task is sort of cooperatively working /// for all the rest as well. fn broadcast(&self) { for (sig, slot) in globals().signals.iter().enumerate() { // Any signal of this kind arrived since we checked last? if !slot.pending.swap(false, Ordering::SeqCst) { continue; } let signum = sig as c_int; let mut recipients = slot.recipients.lock().unwrap(); // Notify all waiters on this signal that the signal has been // received. If we can't push a message into the queue then we don't // worry about it as everything is coalesced anyway. If the channel // has gone away then we can remove that slot. for i in (0..recipients.len()).rev() { match recipients[i].try_send(signum) { Ok(()) => {}, Err(ref e) if e.is_disconnected() => { recipients.swap_remove(i); }, // Channel is full, ignore the error since the // receiver has already been woken up Err(e) => { // Sanity check in case this error type ever gets // additional variants we have not considered. debug_assert!(e.is_full()); }, } } } } } /// An implementation of `Stream` for receiving a particular type of signal. /// /// This structure implements the `Stream` trait and represents notifications /// of the current process receiving a particular signal. The signal being /// listened for is passed to `Signal::new`, and the same signal number is then /// yielded as each element for the stream. /// /// In general signal handling on Unix is a pretty tricky topic, and this /// structure is no exception! There are some important limitations to keep in /// mind when using `Signal` streams: /// /// * Signals handling in Unix already necessitates coalescing signals /// together sometimes. This `Signal` stream is also no exception here in /// that it will also coalesce signals. That is, even if the signal handler /// for this process runs multiple times, the `Signal` stream may only return /// one signal notification. Specifically, before `poll` is called, all /// signal notifications are coalesced into one item returned from `poll`. /// Once `poll` has been called, however, a further signal is guaranteed to /// be yielded as an item. /// /// Put another way, any element pulled off the returned stream corresponds to /// *at least one* signal, but possibly more. /// /// * Signal handling in general is relatively inefficient. Although some /// improvements are possible in this crate, it's recommended to not plan on /// having millions of signal channels open. /// /// * Currently the "driver task" to process incoming signals never exits. This /// driver task runs in the background of the event loop provided, and /// in general you shouldn't need to worry about it. /// /// If you've got any questions about this feel free to open an issue on the /// repo, though, as I'd love to chat about this! In other words, I'd love to /// alleviate some of these limitations if possible! pub struct Signal { driver: Driver, signal: c_int, id: SignalId, rx: Receiver, } impl Signal { /// Creates a new stream which will receive notifications when the current /// process receives the signal `signal`. /// /// This function will create a new stream which binds to the default event /// loop. This function returns a future which will /// then resolve to the signal stream, if successful. /// /// The `Signal` stream is an infinite stream which will receive /// notifications whenever a signal is received. More documentation can be /// found on `Signal` itself, but to reiterate: /// /// * Signals may be coalesced beyond what the kernel already does. /// * Once a signal handler is registered with the process the underlying /// libc signal handler is never unregistered. /// /// A `Signal` stream can be created for a particular signal number /// multiple times. When a signal is received then all the associated /// channels will receive the signal notification. /// /// # Errors /// /// * If the lower-level C functions fail for some reason. /// * If the previous initialization of this specific signal failed. /// * If the signal is one of /// [`signal_hook::FORBIDDEN`](https://docs.rs/signal-hook/*/signal_hook/fn.register.html#panics) pub fn new(signal: c_int) -> IoFuture { Signal::with_handle(signal, &Handle::current()) } /// Creates a new stream which will receive notifications when the current /// process receives the signal `signal`. /// /// This function will create a new stream which may be based on the /// event loop handle provided. This function returns a future which will /// then resolve to the signal stream, if successful. /// /// The `Signal` stream is an infinite stream which will receive /// notifications whenever a signal is received. More documentation can be /// found on `Signal` itself, but to reiterate: /// /// * Signals may be coalesced beyond what the kernel already does. /// * Once a signal handler is registered with the process the underlying /// libc signal handler is never unregistered. /// /// A `Signal` stream can be created for a particular signal number /// multiple times. When a signal is received then all the associated /// channels will receive the signal notification. pub fn with_handle(signal: c_int, handle: &Handle) -> IoFuture { let handle = handle.clone(); Box::new(future::lazy(move || { let result = (|| { // Turn the signal delivery on once we are ready for it try!(signal_enable(signal)); // Ensure there's a driver for our associated event loop processing // signals. let driver = try!(Driver::new(&handle)); // One wakeup in a queue is enough, no need for us to buffer up any // more. NB: channels always guarantee at least one slot per sender, // so we don't need additional slots let (tx, rx) = channel(0); let id = Globals::register_signal_sender(signal, tx); Ok(Signal { driver: driver, rx: rx, id: id, signal: signal, }) })(); future::result(result) })) } } impl Stream for Signal { type Item = c_int; type Error = io::Error; fn poll(&mut self) -> Poll, io::Error> { self.driver.poll().unwrap(); // receivers don't generate errors self.rx.poll().map_err(|_| panic!()) } } impl Drop for Signal { fn drop(&mut self) { Globals::deregister_signal_receiver(self.signal, self.id); } } #[cfg(test)] mod tests { extern crate tokio; use super::*; #[test] fn dropped_signal_senders_are_cleaned_up() { let mut rt = self::tokio::runtime::current_thread::Runtime::new() .expect("failed to init runtime"); let signum = libc::SIGUSR1; let signal = rt.block_on(Signal::new(signum)) .expect("failed to create signal"); { let recipients = globals().signals[signum as usize].recipients.lock().unwrap(); assert!(!recipients.is_empty()); } drop(signal); unsafe { assert_eq!(libc::kill(libc::getpid(), signum), 0); } { let recipients = globals().signals[signum as usize].recipients.lock().unwrap(); assert!(recipients.is_empty()); } } } tokio-signal-0.2.7/src/windows.rs010064400007650000024000000245261337472307400152110ustar0000000000000000//! Windows-specific types for signal handling. //! //! This module is only defined on Windows and contains the primary `Event` type //! for receiving notifications of events. These events are listened for via the //! `SetConsoleCtrlHandler` function which receives events of the type //! `CTRL_C_EVENT` and `CTRL_BREAK_EVENT` #![cfg(windows)] extern crate mio; extern crate winapi; use std::cell::RefCell; use std::io; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Once, ONCE_INIT}; use futures::future; use futures::stream::Fuse; use futures::sync::mpsc; use futures::sync::oneshot; use futures::{Async, Future, IntoFuture, Poll, Stream}; use tokio_reactor::{Handle, PollEvented}; use mio::Ready; use self::winapi::shared::minwindef::*; use self::winapi::um::wincon::*; use IoFuture; extern "system" { fn SetConsoleCtrlHandler(HandlerRoutine: usize, Add: BOOL) -> BOOL; } static INIT: Once = ONCE_INIT; static mut GLOBAL_STATE: *mut GlobalState = 0 as *mut _; /// Stream of events discovered via `SetConsoleCtrlHandler`. /// /// This structure can be used to listen for events of the type `CTRL_C_EVENT` /// and `CTRL_BREAK_EVENT`. The `Stream` trait is implemented for this struct /// and will resolve for each notification received by the process. Note that /// there are few limitations with this as well: /// /// * A notification to this process notifies *all* `Event` streams for that /// event type. /// * Notifications to an `Event` stream **are coalesced** if they aren't /// processed quickly enough. This means that if two notifications are /// received back-to-back, then the stream may only receive one item about the /// two notifications. pub struct Event { reg: PollEvented, _finished: oneshot::Sender<()>, } struct GlobalState { ready: mio::SetReadiness, tx: mpsc::UnboundedSender, ctrl_c: GlobalEventState, ctrl_break: GlobalEventState, } struct GlobalEventState { ready: AtomicBool, } enum Message { NewEvent(DWORD, oneshot::Sender>), } struct DriverTask { handle: Handle, reg: PollEvented, rx: Fuse>, ctrl_c: EventState, ctrl_break: EventState, } struct EventState { tasks: Vec<(RefCell>, mio::SetReadiness)>, } impl Event { /// Creates a new stream listening for the `CTRL_C_EVENT` events. /// /// This function will register a handler via `SetConsoleCtrlHandler` and /// deliver notifications to the returned stream. pub fn ctrl_c() -> IoFuture { Event::ctrl_c_handle(&Handle::current()) } /// Creates a new stream listening for the `CTRL_C_EVENT` events. /// /// This function will register a handler via `SetConsoleCtrlHandler` and /// deliver notifications to the returned stream. pub fn ctrl_c_handle(handle: &Handle) -> IoFuture { Event::new(CTRL_C_EVENT, handle) } /// Creates a new stream listening for the `CTRL_BREAK_EVENT` events. /// /// This function will register a handler via `SetConsoleCtrlHandler` and /// deliver notifications to the returned stream. pub fn ctrl_break() -> IoFuture { Event::ctrl_break_handle(&Handle::current()) } /// Creates a new stream listening for the `CTRL_BREAK_EVENT` events. /// /// This function will register a handler via `SetConsoleCtrlHandler` and /// deliver notifications to the returned stream. pub fn ctrl_break_handle(handle: &Handle) -> IoFuture { Event::new(CTRL_BREAK_EVENT, handle) } fn new(signum: DWORD, handle: &Handle) -> IoFuture { let mut init = None; INIT.call_once(|| { init = Some(global_init(handle)); }); let new_signal = future::lazy(move || { let (tx, rx) = oneshot::channel(); let msg = Message::NewEvent(signum, tx); let res = unsafe { (*GLOBAL_STATE).tx.clone().unbounded_send(msg) }; res.expect( "failed to request a new signal stream, did the \ first event loop go away?", ); rx.then(|r| r.unwrap()) }); match init { Some(init) => Box::new(init.into_future().and_then(|()| new_signal)), None => Box::new(new_signal), } } } impl Stream for Event { type Item = (); type Error = io::Error; fn poll(&mut self) -> Poll, io::Error> { if !self.reg.poll_read_ready(Ready::readable())?.is_ready() { return Ok(Async::NotReady); } self.reg.clear_read_ready(Ready::readable())?; self.reg .get_ref() .inner .borrow() .as_ref() .unwrap() .1 .set_readiness(mio::Ready::empty()) .expect("failed to set readiness"); Ok(Async::Ready(Some(()))) } } fn global_init(handle: &Handle) -> io::Result<()> { let (tx, rx) = mpsc::unbounded(); let reg = MyRegistration { inner: RefCell::new(None), }; let reg = try!(PollEvented::new_with_handle(reg, handle)); let ready = reg.get_ref().inner.borrow().as_ref().unwrap().1.clone(); unsafe { let state = Box::new(GlobalState { ready: ready, ctrl_c: GlobalEventState { ready: AtomicBool::new(false), }, ctrl_break: GlobalEventState { ready: AtomicBool::new(false), }, tx: tx, }); GLOBAL_STATE = Box::into_raw(state); let rc = SetConsoleCtrlHandler(handler as usize, TRUE); if rc == 0 { Box::from_raw(GLOBAL_STATE); GLOBAL_STATE = 0 as *mut _; return Err(io::Error::last_os_error()); } ::tokio_executor::spawn(Box::new(DriverTask { handle: handle.clone(), rx: rx.fuse(), reg: reg, ctrl_c: EventState { tasks: Vec::new() }, ctrl_break: EventState { tasks: Vec::new() }, })); Ok(()) } } impl Future for DriverTask { type Item = (); type Error = (); fn poll(&mut self) -> Poll<(), ()> { self.check_event_drops(); self.check_messages(); self.check_events().unwrap(); // TODO: when to finish this task? Ok(Async::NotReady) } } impl DriverTask { fn check_event_drops(&mut self) { self.ctrl_c .tasks .retain(|task| !task.0.borrow_mut().poll().is_err()); self.ctrl_break .tasks .retain(|task| !task.0.borrow_mut().poll().is_err()); } fn check_messages(&mut self) { loop { // Acquire the next message let message = match self.rx.poll().unwrap() { Async::Ready(Some(e)) => e, Async::Ready(None) | Async::NotReady => break, }; let (sig, complete) = match message { Message::NewEvent(sig, complete) => (sig, complete), }; let event = if sig == CTRL_C_EVENT { &mut self.ctrl_c } else { &mut self.ctrl_break }; // Acquire the (registration, set_readiness) pair by... assuming // we're on the event loop (true because of the spawn above). let reg = MyRegistration { inner: RefCell::new(None), }; let reg = match PollEvented::new_with_handle(reg, &self.handle) { Ok(reg) => reg, Err(e) => { drop(complete.send(Err(e))); continue; } }; // Create the `Event` to pass back and then also keep a handle to // the `SetReadiness` for ourselves internally. let (tx, rx) = oneshot::channel(); let ready = reg.get_ref().inner.borrow_mut().as_mut().unwrap().1.clone(); drop(complete.send(Ok(Event { reg: reg, _finished: tx, }))); event.tasks.push((RefCell::new(rx), ready)); } } fn check_events(&mut self) -> io::Result<()> { if self.reg.poll_read_ready(Ready::readable())?.is_not_ready() { return Ok(()); } self.reg.clear_read_ready(Ready::readable())?; self.reg .get_ref() .inner .borrow() .as_ref() .unwrap() .1 .set_readiness(mio::Ready::empty()) .unwrap(); if unsafe { (*GLOBAL_STATE).ctrl_c.ready.swap(false, Ordering::SeqCst) } { for task in self.ctrl_c.tasks.iter() { task.1.set_readiness(mio::Ready::readable()).unwrap(); } } if unsafe { (*GLOBAL_STATE) .ctrl_break .ready .swap(false, Ordering::SeqCst) } { for task in self.ctrl_break.tasks.iter() { task.1.set_readiness(mio::Ready::readable()).unwrap(); } } Ok(()) } } unsafe extern "system" fn handler(ty: DWORD) -> BOOL { let event = match ty { CTRL_C_EVENT => &(*GLOBAL_STATE).ctrl_c, CTRL_BREAK_EVENT => &(*GLOBAL_STATE).ctrl_break, _ => return FALSE, }; if event.ready.swap(true, Ordering::SeqCst) { FALSE } else { drop((*GLOBAL_STATE).ready.set_readiness(mio::Ready::readable())); // TODO: this will report that we handled a CTRL_BREAK_EVENT when in // fact we may not have any streams actually created for that // event. TRUE } } struct MyRegistration { inner: RefCell>, } impl mio::Evented for MyRegistration { fn register( &self, poll: &mio::Poll, token: mio::Token, events: mio::Ready, opts: mio::PollOpt, ) -> io::Result<()> { let reg = mio::Registration::new2(); reg.0.register(poll, token, events, opts)?; *self.inner.borrow_mut() = Some(reg); Ok(()) } fn reregister( &self, _poll: &mio::Poll, _token: mio::Token, _events: mio::Ready, _opts: mio::PollOpt, ) -> io::Result<()> { Ok(()) } fn deregister(&self, _poll: &mio::Poll) -> io::Result<()> { Ok(()) } } tokio-signal-0.2.7/tests/drop_multi_loop.rs010064400007650000024000000017521337472307400172750ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; const TEST_SIGNAL: libc::c_int = libc::SIGUSR1; #[test] fn dropping_loops_does_not_cause_starvation() { let (mut rt, signal) = { let mut first_rt = CurrentThreadRuntime::new() .expect("failed to init first runtime"); let first_signal = run_with_timeout(&mut first_rt, Signal::new(TEST_SIGNAL)) .expect("failed to register first signal"); let mut second_rt = CurrentThreadRuntime::new() .expect("failed to init second runtime"); let second_signal = run_with_timeout(&mut second_rt, Signal::new(TEST_SIGNAL)) .expect("failed to register second signal"); drop(first_rt); drop(first_signal); (second_rt, second_signal) }; send_signal(TEST_SIGNAL); let signal_future = signal.into_future() .map_err(|(e, _)| e); run_with_timeout(&mut rt, signal_future) .expect("failed to get signal"); } tokio-signal-0.2.7/tests/drop_then_get_a_signal.rs010064400007650000024000000011411337472307400205340ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; #[test] fn drop_then_get_a_signal() { let mut rt = CurrentThreadRuntime::new().unwrap(); let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)) .expect("failed to create first signal"); drop(signal); send_signal(libc::SIGUSR1); let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)) .expect("failed to create signal") .into_future() .map(|_| ()) .map_err(|(e, _)| panic!("{}", e)); run_with_timeout(&mut rt, signal).expect("failed to get signal"); } tokio-signal-0.2.7/tests/dropping_does_not_deregister_other_instances.rs010064400007650000024000000023051337472307400252620ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; const TEST_SIGNAL: libc::c_int = libc::SIGUSR1; #[test] fn dropping_signal_does_not_deregister_any_other_instances() { // NB: Deadline requires a timer registration which is provided by // tokio's `current_thread::Runtime`, but isn't available by just using // tokio's default CurrentThread executor which powers `current_thread::block_on_all`. let mut rt = CurrentThreadRuntime::new() .expect("failed to init runtime"); // NB: Testing for issue #38: signals should not starve based on ordering let first_duplicate_signal = run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL)) .expect("failed to register first duplicate signal"); let signal = run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL)) .expect("failed to register signal"); let second_duplicate_signal = run_with_timeout(&mut rt, Signal::new(TEST_SIGNAL)) .expect("failed to register second duplicate signal"); drop(first_duplicate_signal); drop(second_duplicate_signal); send_signal(TEST_SIGNAL); run_with_timeout(&mut rt, signal.into_future().map_err(|(e, _)| e)) .expect("failed to get signal"); } tokio-signal-0.2.7/tests/multi_loop.rs010064400007650000024000000022611337472307400162450ustar0000000000000000#![cfg(unix)] extern crate libc; use std::sync::mpsc::channel; use std::thread; pub mod support; use support::*; #[test] fn multi_loop() { // An "ordinary" (non-future) channel let (sender, receiver) = channel(); // Run multiple times, to make sure there are no race conditions for _ in 0..10 { // Run multiple event loops, each one in its own thread let threads: Vec<_> = (0..4) .map(|_| { let sender = sender.clone(); thread::spawn(move || { let mut rt = CurrentThreadRuntime::new().unwrap(); let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGHUP)).unwrap(); sender.send(()).unwrap(); run_with_timeout(&mut rt, signal.into_future()).ok().unwrap(); }) }) .collect(); // Wait for them to declare they're ready for &_ in threads.iter() { receiver.recv().unwrap(); } // Send a signal send_signal(libc::SIGHUP); // Make sure the threads terminated correctly for t in threads { t.join().unwrap(); } } } tokio-signal-0.2.7/tests/notify_both.rs010064400007650000024000000010521337472307400164030ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; #[test] fn notify_both() { let mut rt = CurrentThreadRuntime::new().unwrap(); let signal1 = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR2)) .expect("failed to create signal1"); let signal2 = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR2)) .expect("failed to create signal2"); send_signal(libc::SIGUSR2); run_with_timeout(&mut rt, signal1.into_future().join(signal2.into_future())) .ok() .expect("failed to receive"); } tokio-signal-0.2.7/tests/signal.rs010064400007650000024000000007151337472307400153410ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; #[test] fn tokio_simple() { let signal_future = Signal::new(libc::SIGUSR1) .and_then(|signal| { send_signal(libc::SIGUSR1); signal.into_future().map(|_| ()).map_err(|(err, _)| err) }); let mut rt = CurrentThreadRuntime::new() .expect("failed to init runtime"); run_with_timeout(&mut rt, signal_future) .expect("failed"); } tokio-signal-0.2.7/tests/simple.rs010064400007650000024000000006241337472307400153540ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; #[test] fn simple() { let mut rt = CurrentThreadRuntime::new().unwrap(); let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)) .expect("failed to create signal"); send_signal(libc::SIGUSR1); run_with_timeout(&mut rt, signal.into_future()) .ok() .expect("failed to get signal"); } tokio-signal-0.2.7/tests/support.rs010064400007650000024000000020131337472307400155710ustar0000000000000000#![cfg(unix)] extern crate libc; extern crate futures; extern crate tokio; extern crate tokio_signal; use self::libc::{c_int, getpid, kill}; use std::time::Duration; use self::tokio::timer::Timeout; pub use self::futures::{Future, Stream}; pub use self::tokio::runtime::current_thread::{self, Runtime as CurrentThreadRuntime}; pub use self::tokio_signal::unix::Signal; pub fn with_timeout(future: F) -> impl Future { Timeout::new(future, Duration::from_secs(1)) .map_err(|e| if e.is_timer() { panic!("failed to register timer"); } else if e.is_elapsed() { panic!("timed out") } else { e.into_inner().expect("missing inner error") }) } pub fn run_with_timeout(rt: &mut CurrentThreadRuntime, future: F) -> Result where F: Future { rt.block_on(with_timeout(future)) } #[cfg(unix)] pub fn send_signal(signal: c_int) { unsafe { assert_eq!(kill(getpid(), signal), 0); } } tokio-signal-0.2.7/tests/twice.rs010064400007650000024000000007521337472307400152000ustar0000000000000000#![cfg(unix)] extern crate libc; pub mod support; use support::*; #[test] fn twice() { let mut rt = CurrentThreadRuntime::new().unwrap(); let signal = run_with_timeout(&mut rt, Signal::new(libc::SIGUSR1)).unwrap(); send_signal(libc::SIGUSR1); let (num, signal) = run_with_timeout(&mut rt, signal.into_future()).ok().unwrap(); assert_eq!(num, Some(libc::SIGUSR1)); send_signal(libc::SIGUSR1); run_with_timeout(&mut rt, signal.into_future()).ok().unwrap(); } tokio-signal-0.2.7/.cargo_vcs_info.json0000644000000001120000000000000134730ustar00{ "git": { "sha1": "b117fc1d65e72fa25eb0a921f77e5e2e0a8a49bb" } }