cancellable_timer/
lib.rs

1#![deny(missing_docs)]
2
3//! Crate that implements a timer with a `sleep` method that can be cancelled.
4//!
5//! # Example
6//!
7//! ```
8//! use std::time::Duration;
9//! use cancellable_timer::*;
10//!
11//! fn main() {
12//!     let (mut timer, canceller) = Timer::new2().unwrap();
13//!
14//!     // Spawn a thread that will cancel the timer after 2s.
15//!     std::thread::spawn(move || {
16//!         std::thread::sleep(Duration::from_secs(2));
17//!         println!("Stop the timer.");
18//!         canceller.cancel();
19//!     });
20//!
21//!     println!("Wait 10s");
22//!     let r = timer.sleep(Duration::from_secs(10));
23//!     println!("Done: {:?}", r);
24//! }
25//! ```
26
27extern crate mio;
28
29use std::io;
30use std::time::Duration;
31
32use mio::*;
33
34/// A timer object that can be used to put the current thread to sleep
35/// or to start a callback after a given amount of time.
36pub struct Timer {
37    poll: Poll,
38    token: Token,
39    _registration: Registration,
40    events: Events,
41}
42
43/// An object that allows cancelling the associated [Timer](struct.Timer.html).
44#[derive(Clone)]
45pub struct Canceller {
46    set_readiness: SetReadiness,
47}
48
49impl Timer {
50    /// Create a [Timer](struct.Timer.html) and its associated [Canceller](struct.Canceller.html).
51    pub fn new2() -> io::Result<(Self, Canceller)> {
52        let poll = Poll::new()?;
53
54        let token = Token(0);
55        let (registration, set_readiness) = Registration::new2();
56        poll.register(&registration, token, Ready::readable(), PollOpt::edge())?;
57
58        Ok((
59            Timer {
60                poll,
61                token,
62                _registration: registration,
63                events: Events::with_capacity(4),
64            },
65            Canceller { set_readiness },
66        ))
67    }
68
69    /// Put the current thread to sleep until the given time has
70    /// elapsed or the timer is cancelled.
71    ///
72    /// Returns:
73    /// * Ok(()) if the given time has elapsed.
74    /// * An [Error](https://docs.rust-lang.org/std/io/struct.Error.html)
75    /// of kind [ErrorKind::Interrupted](https://docs.rust-lang.org/std/io/enum.ErrorKind.html)
76    /// if the timer has been cancelled.
77    /// * Some other [Error](https://docs.rust-lang.org/std/io/struct.Error.html)
78    /// if something goes wrong.
79    pub fn sleep(&mut self, duration: Duration) -> io::Result<()> {
80        self.poll.poll(&mut self.events, Some(duration))?;
81        for event in self.events.iter() {
82            if event.token() == self.token {
83                return Err(io::Error::new(
84                    io::ErrorKind::Interrupted,
85                    "timer cancelled",
86                ));
87            }
88        }
89        Ok(())
90    }
91
92    /// Run a callback on a new thread after a specified amount of time.
93    /// The callback is not run if `after` returns an error.
94    ///
95    /// Otherwise, the callback is given:
96    /// * Ok(()) if the amount of time has elapsed.
97    /// * An [Error](https://docs.rust-lang.org/std/io/struct.Error.html)
98    /// of kind [ErrorKind::Interrupted](https://docs.rust-lang.org/std/io/enum.ErrorKind.html)
99    /// if the timer has been cancelled.
100    /// * Some other [Error](https://docs.rust-lang.org/std/io/struct.Error.html)
101    /// if something goes wrong.
102    pub fn after<F>(wait: Duration, callback: F) -> io::Result<Canceller>
103    where
104        F: FnOnce(io::Result<()>),
105        F: Send + 'static,
106    {
107        let (mut timer, canceller) = Timer::new2()?;
108        std::thread::Builder::new().spawn(move || {
109            callback(timer.sleep(wait));
110        })?;
111        Ok(canceller)
112    }
113}
114
115impl Canceller {
116    /// Cancel the associated [Timer](struct.Timer.html).
117    pub fn cancel(&self) -> io::Result<()> {
118        self.set_readiness.set_readiness(Ready::readable())
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use std::thread;
126
127    #[test]
128    fn uninterrupted_sleep() {
129        let (mut timer, _) = Timer::new2().unwrap();
130        let r = timer.sleep(Duration::from_secs(1));
131        assert!(r.is_ok());
132    }
133
134    #[test]
135    fn cancel_before_sleep() {
136        let (mut timer, canceller) = Timer::new2().unwrap();
137        canceller.cancel().unwrap();
138        let r = timer.sleep(Duration::from_secs(1));
139        assert_eq!(r.unwrap_err().kind(), io::ErrorKind::Interrupted);
140    }
141
142    #[test]
143    fn cancel_during_sleep() {
144        let (mut timer, canceller) = Timer::new2().unwrap();
145        thread::spawn(move || {
146            thread::sleep(Duration::from_secs(2));
147            canceller.cancel().unwrap();
148        });
149        let r = timer.sleep(Duration::from_secs(10));
150        assert_eq!(r.unwrap_err().kind(), io::ErrorKind::Interrupted);
151    }
152}