rdbc_rs/driver/
callback.rs

1//! Callback A [`BoxedCallback<Output>`] is a handle for waking up driver
2//! caller by notifying asynchronous task is completed.
3//!
4//! This handle encapsulates a [`FnOnce(Result<Output>)`] instance,
5//! which defines the caller-specific wakeup behavior.
6
7use anyhow::Result;
8use std::ptr::NonNull;
9
10/// A virtual function pointer table (vtable) that specifies the
11/// behavior of a [`FnOnce(Result<Output>)`](FnOnce).
12#[repr(C)]
13pub struct CallbackVTable<Output> {
14    invoke: unsafe fn(NonNull<CallbackVTable<Output>>, result: Result<Output>),
15
16    drop: unsafe fn(NonNull<CallbackVTable<Output>>),
17}
18
19impl<Output> CallbackVTable<Output> {
20    fn new<F>() -> Self
21    where
22        F: FnOnce(Result<Output>),
23    {
24        CallbackVTable {
25            drop: drop::<F, Output>,
26            invoke: invoke::<F, Output>,
27        }
28    }
29}
30
31unsafe fn drop<F, Output>(_: NonNull<CallbackVTable<Output>>)
32where
33    F: FnOnce(Result<Output>),
34{
35}
36
37unsafe fn invoke<F, Output>(vtable: NonNull<CallbackVTable<Output>>, result: Result<Output>)
38where
39    F: FnOnce(Result<Output>),
40{
41    let mut raw = vtable.cast::<Callback<F, Output>>();
42
43    let f = raw.as_mut().f.take();
44
45    f.unwrap()(result);
46}
47
48#[repr(C)]
49struct Callback<F, Output>
50where
51    F: FnOnce(Result<Output>),
52{
53    vtable: CallbackVTable<Output>,
54    f: Option<F>,
55}
56
57/// Type erased Callback wrapper struct
58pub struct BoxedCallback<Output> {
59    vtable: NonNull<CallbackVTable<Output>>,
60}
61
62impl<Output> BoxedCallback<Output> {
63    pub fn new<F>(f: F) -> Self
64    where
65        F: FnOnce(Result<Output>),
66    {
67        let boxed = Box::new(Callback::<F, Output> {
68            vtable: CallbackVTable::<Output>::new::<F>(),
69            f: Some(f),
70        });
71
72        let ptr =
73            unsafe { NonNull::new_unchecked(Box::into_raw(boxed) as *mut CallbackVTable<Output>) };
74
75        Self { vtable: ptr }
76    }
77
78    /// Call the callback function and pass the return value to the caller
79    pub fn invoke(&self, result: Result<Output>) {
80        unsafe {
81            let invoke = self.vtable.as_ref().invoke;
82
83            invoke(self.vtable, result)
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::BoxedCallback;
91
92    #[test]
93    fn test_boxed_callback() {
94        let boxed: BoxedCallback<usize> = BoxedCallback::new(|v| {
95            assert_eq!(v.unwrap(), 1);
96        });
97
98        boxed.invoke(Ok(1));
99    }
100}