c_closures/
lib.rs

1//! # Purpose
2//!
3//! This crate is for producing Rust closures that can cross an FFI boundary with no generic types.
4//! It provides support for any single argument signature, along with any return type, assuming
5//! both have valid representations in C/C++ and Rust.
6//!
7//! [Here's an example.](https://github.com/Xaeroxe/c-closures-rs/tree/master/example)
8//!
9//! # Safety concerns
10//!
11//! Creating a `Closure` by itself can not cause undefined behavior, however the resulting
12//! structure is extremely dangerous. The C/C++ code may not validate arguments
13//! passed are of the correct type, which could lead to memory corruption and
14//! segfaulting. `Closure` should never be an argument to a safe function, nor should it be
15//! a public member of any structures passed into a safe function.
16//!
17//! # Usage in C/C++
18//!
19//! To use this with a C/C++ library you'll need to include the header provided in the repo,
20//! `rust_closures.h`, then link to the assembly produced by `rust_closures.c`. If the C/C++ code
21//! is being linked into a Rust binary depending on this crate, then you don't need to worry about
22//! linking to `rust_closures.c`. Then you can accept the `Closure` type anywhere that you need to
23//! accept arbitrary Rust code.
24//!
25//! # Limitations
26//!
27//! `Closure` can currently only accept a single argument, this can be worked around by making that argument
28//! a C/C++ class/struct containing multiple fields. Additionally it is strongly recommended that all types
29//! in the closure signature have a valid representation in C/C++ and Rust. Fat pointers are a common gotcha
30//! in this respect, remember slices and string slices are not a single pointer value.
31//!
32//! This cannot be used to transfer ownership across FFI boundaries, as this crate cannot reasonably guarantee
33//! both sides are using the same memory allocator, or dispose of the types in the same way. If such transfer
34//! is required, you should copy the data into a new allocation, on the side of the FFI boundary it needs to live
35//! on. The major exception to this is types with the `Copy` marker trait, which are trivially cloned and require
36//! no disposal instructions.
37//!
38//! In order to achieve this in such a general manner this crate leans heavily on heap allocations. Arguments,
39//! and return types are treated as data of arbitrary unknown length. If such heap allocations are unacceptable
40//! for your use case, consider authoring a similar structure with specific known types and involving no indirection.
41//!
42
43#![allow(non_snake_case)]
44
45use std::{ffi::c_void, mem::size_of, process::abort, ptr::null_mut};
46
47use backtrace::Backtrace;
48use log::error;
49
50include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
51
52// Send + Sync impl for Closure intentionally omitted. It's possible to create versions of these
53// for which such an impl is correct, and it may even prove to be desirable, but this version is
54// not provably Send + Sync as the closures it wraps can capture anything.
55
56impl Closure {
57    /// Transform an FnMut Rust closure into a structure you can pass into a C/C++ library.
58    ///
59    /// This structure currently assumes it will never be called in multiple threads
60    /// simultaneously. If that guarantee cannot be upheld, then you should instead use `fn_not_mut`.
61    ///
62    /// ```
63    /// # use c_closures::Closure;
64    /// let mut y = 5;
65    /// let _f = Closure::fn_mut(move |x: &i32| {
66    ///     y *= 2;
67    ///     *x * 2
68    /// });
69    /// ```
70    pub fn fn_mut<Arg, Return, Function>(f: Function) -> Self
71    where
72        Arg: FromClosureArgPointer,
73        Function: FnMut(Arg) -> Return + Send + 'static,
74    {
75        Self {
76            data: Box::into_raw(Box::new(f)) as *mut c_void,
77            function: Some(f_wrapper::<Arg, Return, Function>),
78            delete_data: Some(delete_me::<Function>),
79            delete_ret: Some(delete_me::<Return>),
80        }
81    }
82
83    /// Transform an Fn Rust closure into a structure you can pass into a C/C++ library.
84    ///
85    /// This structure is safe to use in multiple threads simultaneously. If your usage is single
86    /// threaded, consider `fn_mut` instead as it permits more robust closures.
87    ///
88    /// ```
89    /// # use c_closures::Closure;
90    /// let y = 5;
91    /// let _f = Closure::fn_not_mut(move |x: &i32| {
92    ///     *x * y
93    /// });
94    /// ```
95    pub fn fn_not_mut<Arg, Return, Function>(f: Function) -> Self
96    where
97        Arg: FromClosureArgPointer,
98        Function: Fn(Arg) -> Return + Send + 'static,
99    {
100        Self {
101            data: Box::into_raw(Box::new(f)) as *mut c_void,
102            function: Some(f_wrapper::<Arg, Return, Function>),
103            delete_data: Some(delete_me::<Function>),
104            delete_ret: Some(delete_me::<Return>),
105        }
106    }
107
108    /// Transform an FnOnce Rust closure into a structure you can pass into a C/C++ library.
109    ///
110    /// This structure assumes it will only ever be called once. If you attempt to call it more than once
111    /// the return value will be zeroed memory. If the return type does not consider zeroed memory to be a valid
112    /// representation, then usage of the return type in this instance may result in undefined behavior.
113    ///
114    /// ```
115    /// # use c_closures::Closure;
116    /// let values = vec![String::from("1"), String::from("2"), String::from("3")];
117    /// let _f = Closure::fn_once(move |_: ()| {
118    ///     for item in &values {
119    ///         println!("Item: {}", item);
120    ///     }
121    ///     // Probably not how this would actually be used, just to demonstrate that we can.
122    ///     std::mem::drop(values);
123    /// });
124    /// ```
125    pub fn fn_once<Arg, Return, Function>(f: Function) -> Self
126    where
127        Arg: FromClosureArgPointer,
128        Function: FnOnce(Arg) -> Return + Send + 'static,
129    {
130        let mut f = Some(f);
131        Self::fn_mut(move |arg| match f.take() {
132            Some(f) => f(arg),
133            None => {
134                error!("Function marked as single-use was called more than once, the closure will not be called as that would segfault. Aborting.");
135                abort()
136            }
137        })
138    }
139
140    /// Constructs a new instance of this class that when called does nothing. It provides all
141    /// possible signatures simultaneously, excluding those with a return value, because the
142    /// `Closure` machinery will do nothing with it.
143    pub fn new_noop() -> Self {
144        Self {
145            data: null_mut(),
146            function: None,
147            delete_data: None,
148            delete_ret: None,
149        }
150    }
151
152    /// Similar to the `rebind_closure` macro, except this operates on immutable references instead.
153    pub fn rebind_closure_ref<C: ClosureMarkerTrait>(&self) -> &C {
154        // size_of here is a const fn, so this branch will be optimized out of existence.
155        if size_of::<C>() != size_of::<Self>() {
156            panic!("rebind_closure_ref external definition is not the same size as internal definition. \
157            `ClosureMarkerTrait` is probably implemented incorrectly. This also might be a bug in c-closures.")
158        } else {
159            unsafe { &*(self as *const Self as *const C) }
160        }
161    }
162
163    /// Similar to the `rebind_closure` macro, except this operates on mutable references instead.
164    pub fn rebind_closure_mut<C: ClosureMarkerTrait>(&mut self) -> &mut C {
165        // size_of here is a const fn, so this branch will be optimized out of existence.
166        if size_of::<C>() != size_of::<Self>() {
167            panic!("rebind_closure_mut external definition is not the same size as internal definition. \
168            `ClosureMarkerTrait` is probably implemented incorrectly. This also might be a bug in c-closures.")
169        } else {
170            unsafe { &mut *(self as *mut Self as *mut C) }
171        }
172    }
173}
174
175// In the unlikely event a `Closure` is released while on the Rust side, we need to dispose of it correctly.
176impl Drop for Closure {
177    fn drop(&mut self) {
178        unsafe {
179            closure_release(self);
180        }
181    }
182}
183
184unsafe extern "C" fn f_wrapper<Arg, Return, Function>(f: *mut c_void, a: *mut c_void) -> *mut c_void
185where
186    Arg: FromClosureArgPointer,
187    Function: FnMut(Arg) -> Return + Send + 'static,
188{
189    let f = &mut *(f as *mut Function);
190    let arg = if a.is_null() && size_of::<Arg>() > 0 {
191        error!(
192        "Unexpected null argument received in Closure, the closure will not be called as that would segfault.\n{:?}",
193        Backtrace::new()
194      );
195        None
196    } else {
197        Some(Arg::from_arg_ptr(a))
198    };
199
200    arg.map(|arg| Box::into_raw(Box::new(f(arg))) as *mut c_void)
201        .unwrap_or(null_mut())
202}
203
204unsafe extern "C" fn delete_me<T>(t: *mut c_void) {
205    // The box takes back ownership, and is then dropped, preventing a leak of the closure data.
206    Box::from_raw(t as *mut T);
207}
208
209/// This trait identifies instances of the `Closure` type from `rust_closures.h`. In Rust land, there will be
210/// multiple instances of this type that we need to be able to cast from one to another. This trait helps us
211/// determine which of these casts are safe. To implement this use `BindgenBuilderExt::c_closures_enhancements`
212/// from `c-closures-build` on your `bindgen::Builder`.
213pub trait ClosureMarkerTrait {}
214
215impl ClosureMarkerTrait for Closure {}
216
217/// Provides a general purpose way to deref a structure from a C void pointer. Auto implemented for `Copy` types.
218pub trait FromClosureArgPointer {
219    /// # Safety
220    ///
221    /// Incorrect implementations of this trait may lead to undefined behavior. If you're trying to read out a
222    /// pointer type, then the pointer passed to this trait is a pointer to your pointer, not the pointer itself.
223    unsafe fn from_arg_ptr(ptr: *const c_void) -> Self;
224}
225
226impl<T: Copy> FromClosureArgPointer for T {
227    unsafe fn from_arg_ptr(ptr: *const c_void) -> Self {
228        *(ptr as *const T)
229    }
230}
231
232/// Rebinds a `Closure` from this crate to a `Closure` type defined externally.
233/// If you use bindgen to make bindings to C/C++ functions accepting this `Closure` type then the bindings won't
234/// be defined in terms of `c_closures`, instead your functions will want an instance of your own `Closure` definition.
235/// This macro provides a convenient way to rebind them.
236///
237/// ```
238/// use c_closures::{Closure, rebind_closure};
239/// mod ffi {
240///     // Import of bindgen generated closure here.
241///     // i.e. include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
242/// # #[repr(C)]
243/// # pub struct Closure {
244/// #  inner: [u8; std::mem::size_of::<c_closures::Closure>()],
245/// # }
246/// # impl c_closures::ClosureMarkerTrait for Closure {}
247/// }
248/// # fn main () {
249/// // elsewhere
250/// let c = rebind_closure!(ffi::Closure, Closure::fn_not_mut(|_: ()| 2 + 2));
251/// # }
252/// ```
253#[macro_export]
254macro_rules! rebind_closure {
255    ($external_name:ty, $closure:expr) => {
256        { // Additional scope added to prevent leaking of the fn definition.
257            fn is_closure_type<C: $crate::ClosureMarkerTrait>() {}
258
259            // size_of here is a const fn, so this branch will be optimized out of existence.
260            if ::std::mem::size_of::<$external_name>() != ::std::mem::size_of::<$crate::Closure>() {
261                panic!("rebind_ref! macro external definition is not the same size as internal definition. \
262                `ClosureMarkerTrait` is probably implemented incorrectly.")
263            } else {
264                is_closure_type::<$external_name>(); // Intentionally creates a compiler error if the marker trait isn't implemented.
265                unsafe {
266                    ::std::mem::transmute::<$crate::Closure, $external_name>($closure)
267                }
268            }
269        }
270    };
271}
272
273#[cfg(test)]
274mod tests {
275    use std::{
276        ffi::{CStr, CString},
277        sync::Arc,
278    };
279
280    use super::*;
281
282    #[test]
283    fn fn_not_mut() {
284        let y = 4;
285        let mut closure = Closure::fn_not_mut(move |x: i32| x + x + y);
286        unsafe {
287            let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
288            assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 8);
289            closure_release_return_value(&mut closure, ret);
290            closure_release(&mut closure);
291        }
292    }
293
294    #[test]
295    fn fn_mut() {
296        let mut y = 4;
297        let mut closure = Closure::fn_mut(move |x: i32| {
298            y *= 2;
299            x + x + y
300        });
301        unsafe {
302            let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
303            assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 12);
304            closure_release_return_value(&mut closure, ret);
305
306            let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
307            assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 20);
308            closure_release_return_value(&mut closure, ret);
309            closure_release(&mut closure);
310        }
311    }
312
313    #[test]
314    fn fn_once() {
315        let mut y = 4;
316        let mut closure = Closure::fn_once(move |x: i32| {
317            y *= 2;
318            x + x + y
319        });
320        unsafe {
321            let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
322            assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 12);
323            closure_release_return_value(&mut closure, ret);
324
325            // I'd love to verify that a subsequent call aborts, but it's non-trivial
326            // to put that into a test suite. We'll address this if it ever becomes a problem
327            // that this testing isn't done.
328            closure_release(&mut closure);
329        }
330    }
331
332    #[test]
333    fn fn_cstring() {
334        let mut closure = Closure::fn_not_mut(|name: &CStr| {
335            CString::new(format!("Hello {}", name.to_str().unwrap())).unwrap()
336        });
337        let my_name = CString::new("Jacob").unwrap();
338        unsafe {
339            let ret = closure_call(&mut closure, &mut my_name.as_c_str() as *mut &CStr as _);
340            assert_eq!(
341                (&mut *(ret as *mut CString)).clone().into_string().unwrap(),
342                "Hello Jacob"
343            );
344            closure_release_return_value(&mut closure, ret);
345            closure_release(&mut closure);
346        }
347    }
348
349    #[test]
350    fn fn_drop_test() {
351        let value = Arc::new(());
352        let value_clone = value.clone();
353        let mut closure = Closure::fn_not_mut(move |_: ()| value_clone.clone());
354        unsafe {
355            let ret = closure_call(&mut closure, &mut () as *mut () as _);
356            assert_eq!(Arc::strong_count(&value), 3);
357            closure_release_return_value(&mut closure, ret);
358            assert_eq!(Arc::strong_count(&value), 2);
359            closure_release(&mut closure);
360            assert_eq!(Arc::strong_count(&value), 1);
361        }
362    }
363
364    struct NotAClosure;
365
366    impl ClosureMarkerTrait for NotAClosure {}
367
368    #[test]
369    #[should_panic]
370    fn bad_ref_usage() {
371        let c = Closure::fn_not_mut(|_: ()| ());
372        c.rebind_closure_ref::<NotAClosure>();
373    }
374
375    #[test]
376    #[should_panic]
377    fn bad_mut_usage() {
378        let mut c = Closure::fn_not_mut(|_: ()| ());
379        c.rebind_closure_mut::<NotAClosure>();
380    }
381
382    // Validates that calling this macro doesn't result in leaky definitions. Items
383    // defined in the macro should not exist outside of it.
384    #[test]
385    fn two_in_scope() {
386        rebind_closure!(Closure, Closure::new_noop());
387        rebind_closure!(Closure, Closure::new_noop());
388    }
389}