nvim_oxi_libuv/
async.rs

1use std::error::Error as StdError;
2
3use crate::{Error, Handle, IntoResult, ffi};
4
5type Callback = Box<dyn FnMut() -> Result<(), Box<dyn StdError>> + 'static>;
6
7/// Binding to libuv's [Async handle][1] used to trigger the execution of a
8/// callback in the Neovim thread.
9///
10/// [1]: http://docs.libuv.org/en/v1.x/async.html
11#[derive(Clone)]
12pub struct AsyncHandle {
13    handle: Handle<ffi::uv_async_t, Callback>,
14}
15
16unsafe impl Send for AsyncHandle {}
17unsafe impl Sync for AsyncHandle {}
18
19impl AsyncHandle {
20    /// Registers a new callback on the Neovim event loop, returning an
21    /// [`AsyncHandle`] which can be used to execute the callback from any
22    /// thread. The callback will always be executed on the main thread.
23    pub fn new<Cb, R>(mut callback: Cb) -> Result<Self, Error>
24    where
25        Cb: FnMut() -> R + 'static,
26        R: IntoResult<()>,
27        R::Error: StdError + 'static,
28    {
29        let mut handle = Handle::new(|uv_loop, handle| unsafe {
30            ffi::uv_async_init(
31                uv_loop,
32                handle.as_mut_ptr(),
33                Some(async_cb as _),
34            )
35        })?;
36
37        let callback: Callback = Box::new(move || {
38            // Type erase the callback by boxing its error.
39            callback()
40                .into_result()
41                .map_err(|err| Box::new(err) as Box<dyn StdError>)
42        });
43
44        unsafe { handle.set_data(callback) };
45
46        Ok(Self { handle })
47    }
48
49    /// Wakes up the Neovim event loop and executes the callback associated to
50    /// this handle. It is safe to call this function from any thread. The
51    /// callback will be called on the main thread.
52    ///
53    /// NOTE: [libuv] will coalesce calls to [`AsyncHandle::send`], that is,
54    /// not every call to it will yield an execution of the callback. For
55    /// example: if [`AsyncHandle::send`] is called 5 times in a row before the
56    /// callback is called, the callback will only be called once. If
57    /// [`AsyncHandle::send`] is called again after the callback was called, it
58    /// will be called again.
59    ///
60    /// [libuv]: https://libuv.org/
61    pub fn send(&self) -> Result<(), Error> {
62        let retv =
63            unsafe { ffi::uv_async_send(self.handle.as_ptr() as *mut _) };
64
65        if retv < 0 {
66            return Err(Error::AsyncTrigger);
67        }
68
69        Ok(())
70    }
71}
72
73extern "C" fn async_cb(ptr: *mut ffi::uv_async_t) {
74    let handle: Handle<_, Callback> = unsafe { Handle::from_raw(ptr) };
75
76    let callback = unsafe { handle.get_data() };
77
78    if !callback.is_null() {
79        let callback = unsafe { &mut *callback };
80
81        if let Err(_err) = callback() {
82            // TODO: what now?
83        }
84    }
85}