libuv_bindings/
timer.rs

1use std::error::Error as StdError;
2use std::time::Duration;
3
4use libuv_sys2::{self as ffi, uv_timer_t};
5
6use crate::{Error, Handle};
7
8pub(crate) type Callback = Box<
9    dyn FnMut(&mut TimerHandle) -> Result<(), Box<dyn StdError>> + 'static,
10>;
11
12/// Binding to libuv's [Timer handle][1] used to schedule callbacks to be
13/// called in the future.
14///
15/// [1]: http://docs.libuv.org/en/v1.x/timer.html
16pub struct TimerHandle {
17    handle: Handle<uv_timer_t, Callback>,
18}
19
20impl TimerHandle {
21    fn new() -> Result<Self, Error> {
22        let handle = Handle::new(|uv_loop, handle| unsafe {
23            ffi::uv_timer_init(uv_loop, handle.as_mut_ptr())
24        })?;
25
26        Ok(Self { handle })
27    }
28
29    /// Executes a callback every `repeat` interval starting after `timeout`.
30    ///
31    /// If the timeout is zero the callback will fire on the next event loop
32    /// iteration.
33    pub fn start<Cb, E>(
34        timeout: Duration,
35        repeat: Duration,
36        mut callback: Cb,
37    ) -> Result<Self, Error>
38    where
39        Cb: FnMut(&mut Self) -> Result<(), E> + 'static,
40        E: StdError + 'static,
41    {
42        let mut timer = Self::new()?;
43
44        let callback: Callback = Box::new(move |timer| {
45            // Type erase the callback by boxing its error.
46            callback(timer).map_err(|err| Box::new(err) as Box<dyn StdError>)
47        });
48
49        unsafe { timer.handle.set_data(callback) };
50
51        let retv = unsafe {
52            ffi::uv_timer_start(
53                timer.handle.as_mut_ptr(),
54                Some(timer_cb as _),
55                timeout.as_millis() as u64,
56                repeat.as_millis() as u64,
57            )
58        };
59
60        if retv < 0 {
61            return Err(Error::TimerStart);
62        }
63
64        Ok(timer)
65    }
66
67    /// Same as [`start`](TimerHandle::start) but accepts an `FnOnce` closure
68    /// which will only be called once before being automatically stopped.
69    pub fn once<Cb, E>(timeout: Duration, callback: Cb) -> Result<Self, Error>
70    where
71        Cb: FnOnce() -> Result<(), E> + 'static,
72        E: StdError + 'static,
73    {
74        let mut callback = Some(callback);
75
76        Self::start(timeout, Duration::from_millis(0), move |timer| {
77            let res = callback.take().unwrap()();
78            timer.stop().unwrap();
79            res
80        })
81    }
82
83    /// Stops the timer.
84    pub fn stop(&mut self) -> Result<(), Error> {
85        let retv = unsafe { ffi::uv_timer_stop(self.handle.as_mut_ptr()) };
86
87        if retv < 0 {
88            return Err(Error::TimerStop);
89        }
90
91        Ok(())
92    }
93}
94
95extern "C" fn timer_cb(ptr: *mut uv_timer_t) {
96    let handle: Handle<_, Callback> = unsafe { Handle::from_raw(ptr) };
97
98    let callback = unsafe { handle.get_data() };
99
100    if !callback.is_null() {
101        let mut handle = TimerHandle { handle };
102        let callback = unsafe { &mut *callback };
103
104        if let Err(_err) = callback(&mut handle) {
105            // TODO: what now?
106        }
107    }
108}