1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use std::error::Error as StdError;

use libuv_sys2::{self as ffi, uv_async_t};

use crate::{Error, Handle};

type Callback = Box<dyn FnMut() -> Result<(), Box<dyn StdError>> + 'static>;

/// Binding to libuv's [Async handle][1] used to trigger the execution of a
/// callback in the Neovim thread.
///
/// [1]: http://docs.libuv.org/en/v1.x/async.html
#[derive(Clone)]
pub struct AsyncHandle {
    handle: Handle<uv_async_t, Callback>,
}

unsafe impl Send for AsyncHandle {}
unsafe impl Sync for AsyncHandle {}

impl AsyncHandle {
    /// Registers a new callback on the Neovim event loop, returning an
    /// [`AsyncHandle`] which can be used to execute the callback from any
    /// thread. The callback will always be executed on the main thread.
    pub fn new<E, Cb>(mut callback: Cb) -> Result<Self, Error>
    where
        E: StdError + 'static,
        Cb: FnMut() -> Result<(), E> + 'static,
    {
        let mut handle = Handle::new(|uv_loop, handle| unsafe {
            ffi::uv_async_init(
                uv_loop,
                handle.as_mut_ptr(),
                Some(async_cb as _),
            )
        })?;

        let callback: Callback = Box::new(move || {
            // Type erase the callback by boxing its error.
            callback().map_err(|err| Box::new(err) as Box<dyn StdError>)
        });

        unsafe { handle.set_data(callback) };

        Ok(Self { handle })
    }

    /// Wakes up the Neovim event loop and executes the callback associated to
    /// this handle. It is safe to call this function from any thread. The
    /// callback will be called on the main thread.
    ///
    /// NOTE: [libuv] will coalesce calls to [`AsyncHandle::send`], that is,
    /// not every call to it will yield an execution of the callback. For
    /// example: if [`AsyncHandle::send`] is called 5 times in a row before the
    /// callback is called, the callback will only be called once. If
    /// [`AsyncHandle::send`] is called again after the callback was called, it
    /// will be called again.
    ///
    /// [libuv]: https://libuv.org/
    pub fn send(&self) -> Result<(), Error> {
        let retv =
            unsafe { ffi::uv_async_send(self.handle.as_ptr() as *mut _) };

        if retv < 0 {
            return Err(Error::AsyncTrigger);
        }

        Ok(())
    }
}

extern "C" fn async_cb(ptr: *mut uv_async_t) {
    let handle: Handle<_, Callback> = unsafe { Handle::from_raw(ptr) };

    let callback = unsafe { handle.get_data() };

    if !callback.is_null() {
        let callback = unsafe { &mut *callback };

        if let Err(_err) = callback() {
            // TODO: what now?
        }
    }
}