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}