nvim_oxi_libuv/
timer.rs

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