glib/
closure.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// TODO: support marshaller.
4
5use std::{mem, ptr, slice};
6
7use libc::{c_uint, c_void};
8
9use crate::{gobject_ffi, prelude::*, translate::*, Type, Value};
10
11wrapper! {
12    #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
13    #[doc(alias = "GClosure")]
14    pub struct Closure(Shared<gobject_ffi::GClosure>);
15
16    match fn {
17        ref => |ptr| {
18            gobject_ffi::g_closure_ref(ptr);
19            gobject_ffi::g_closure_sink(ptr);
20        },
21        unref => |ptr| gobject_ffi::g_closure_unref(ptr),
22        type_ => || gobject_ffi::g_closure_get_type(),
23    }
24}
25
26#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
27pub struct RustClosure(Closure);
28
29impl RustClosure {
30    // rustdoc-stripper-ignore-next
31    /// Creates a new closure around a Rust closure.
32    ///
33    /// See [`glib::closure!`](macro@crate::closure) for a way to create a closure with concrete
34    /// types.
35    ///
36    /// # Panics
37    ///
38    /// Invoking the closure with wrong argument types or returning the wrong return value type
39    /// will panic.
40    ///
41    /// # Example
42    ///
43    /// ```
44    /// use glib::prelude::*;
45    ///
46    /// let closure = glib::RustClosure::new(|values| {
47    ///     let x = values[0].get::<i32>().unwrap();
48    ///     Some((x + 1).to_value())
49    /// });
50    ///
51    /// assert_eq!(
52    ///     closure.invoke::<i32>(&[&1i32]),
53    ///     2,
54    /// );
55    /// ```
56    #[doc(alias = "g_closure_new")]
57    pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
58        Self(Closure::new(callback))
59    }
60
61    // rustdoc-stripper-ignore-next
62    /// Creates a new closure around a Rust closure.
63    ///
64    /// See [`glib::closure_local!`](crate::closure_local) for a way to create a closure with
65    /// concrete types.
66    ///
67    /// # Panics
68    ///
69    /// Invoking the closure with wrong argument types or returning the wrong return value type
70    /// will panic.
71    ///
72    /// Invoking the closure from a different thread than this one will panic.
73    #[doc(alias = "g_closure_new")]
74    pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
75        Self(Closure::new_local(callback))
76    }
77
78    // rustdoc-stripper-ignore-next
79    /// Invokes the closure with the given arguments.
80    ///
81    /// For invalidated closures this returns the "default" value of the return type. For nullable
82    /// types this is `None`, which means that e.g. requesting `R = String` will panic will `R =
83    /// Option<String>` will return `None`.
84    ///
85    /// # Panics
86    ///
87    /// The argument types and return value type must match the ones expected by the closure or
88    /// otherwise this function panics.
89    #[doc(alias = "g_closure_invoke")]
90    pub fn invoke<R: TryFromClosureReturnValue>(&self, values: &[&dyn ToValue]) -> R {
91        let values = values
92            .iter()
93            .copied()
94            .map(ToValue::to_value)
95            .collect::<smallvec::SmallVec<[_; 10]>>();
96
97        R::try_from_closure_return_value(self.invoke_with_values(R::static_type(), &values))
98            .expect("Invalid return value")
99    }
100
101    // rustdoc-stripper-ignore-next
102    /// Invokes the closure with the given arguments.
103    ///
104    /// For invalidated closures this returns the "default" value of the return type.
105    ///
106    /// # Panics
107    ///
108    /// The argument types and return value type must match the ones expected by the closure or
109    /// otherwise this function panics.
110    #[doc(alias = "g_closure_invoke")]
111    pub fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
112        unsafe { self.0.invoke_with_values(return_type, values) }
113    }
114
115    // rustdoc-stripper-ignore-next
116    /// Invalidates the closure.
117    ///
118    /// Invoking an invalidated closure has no effect.
119    #[doc(alias = "g_closure_invalidate")]
120    pub fn invalidate(&self) {
121        self.0.invalidate();
122    }
123}
124
125impl From<RustClosure> for Closure {
126    #[inline]
127    fn from(c: RustClosure) -> Self {
128        c.0
129    }
130}
131
132impl AsRef<Closure> for RustClosure {
133    #[inline]
134    fn as_ref(&self) -> &Closure {
135        &self.0
136    }
137}
138
139impl AsRef<Closure> for Closure {
140    #[inline]
141    fn as_ref(&self) -> &Closure {
142        self
143    }
144}
145
146impl Closure {
147    // rustdoc-stripper-ignore-next
148    /// Creates a new closure around a Rust closure.
149    ///
150    /// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
151    /// closures. This type mostly exists for FFI interop.
152    ///
153    /// # Panics
154    ///
155    /// Invoking the closure with wrong argument types or returning the wrong return value type
156    /// will panic.
157    ///
158    ///
159    /// # Example
160    ///
161    /// ```
162    /// use glib::prelude::*;
163    ///
164    /// let closure = glib::Closure::new(|values| {
165    ///     let x = values[0].get::<i32>().unwrap();
166    ///     Some((x + 1).to_value())
167    /// });
168    ///
169    /// // Invoking non-Rust closures is unsafe because of possibly missing
170    /// // argument and return value type checks.
171    /// let res = unsafe {
172    ///     closure
173    ///         .invoke_with_values(glib::Type::I32, &[1i32.to_value()])
174    ///         .and_then(|v| v.get::<i32>().ok())
175    ///         .expect("Invalid return value")
176    /// };
177    ///
178    /// assert_eq!(res, 2);
179    /// ```
180    #[doc(alias = "g_closure_new")]
181    pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
182        unsafe { Self::new_unsafe(callback) }
183    }
184
185    // rustdoc-stripper-ignore-next
186    /// Creates a new closure around a Rust closure.
187    ///
188    /// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
189    /// closures. This type mostly exists for FFI interop.
190    ///
191    /// # Panics
192    ///
193    /// Invoking the closure with wrong argument types or returning the wrong return value type
194    /// will panic.
195    ///
196    /// Invoking the closure from a different thread than this one will panic.
197    #[doc(alias = "g_closure_new")]
198    pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
199        let callback = crate::thread_guard::ThreadGuard::new(callback);
200
201        unsafe { Self::new_unsafe(move |values| (callback.get_ref())(values)) }
202    }
203
204    // rustdoc-stripper-ignore-next
205    /// Creates a new closure around a Rust closure.
206    ///
207    /// # Safety
208    ///
209    /// The captured variables of the closure must stay valid as long as the return value of this
210    /// constructor does, and it must be valid to call the closure from any thread that is used by
211    /// callers.
212    #[doc(alias = "g_closure_new")]
213    pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
214        unsafe extern "C" fn marshal<F>(
215            _closure: *mut gobject_ffi::GClosure,
216            return_value: *mut gobject_ffi::GValue,
217            n_param_values: c_uint,
218            param_values: *const gobject_ffi::GValue,
219            _invocation_hint: *mut c_void,
220            marshal_data: *mut c_void,
221        ) where
222            F: Fn(&[Value]) -> Option<Value>,
223        {
224            let values = if n_param_values == 0 {
225                &[]
226            } else {
227                slice::from_raw_parts(param_values as *const _, n_param_values as usize)
228            };
229            let callback: &F = &*(marshal_data as *mut _);
230            let result = callback(values);
231
232            if return_value.is_null() {
233                assert!(
234                    result.is_none(),
235                    "Closure returned a return value but the caller did not expect one"
236                );
237            } else {
238                let return_value = &mut *(return_value as *mut Value);
239                match result {
240                    Some(result) => {
241                        assert!(
242                            result.type_().is_a(return_value.type_()),
243                            "Closure returned a value of type {} but caller expected {}",
244                            result.type_(),
245                            return_value.type_()
246                        );
247                        *return_value = result;
248                    }
249                    None if return_value.type_() == Type::INVALID => (),
250                    None => {
251                        panic!(
252                            "Closure returned no value but the caller expected a value of type {}",
253                            return_value.type_()
254                        );
255                    }
256                }
257            }
258        }
259
260        unsafe extern "C" fn finalize<F>(
261            notify_data: *mut c_void,
262            _closure: *mut gobject_ffi::GClosure,
263        ) where
264            F: Fn(&[Value]) -> Option<Value>,
265        {
266            let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
267            // callback is dropped here.
268        }
269
270        // Due to bitfields we have to do our own calculations here for the size of the GClosure:
271        // - 4: 32 bits in guint bitfields at the beginning
272        // - padding due to alignment needed for the following pointer
273        // - 3 * size_of<*mut c_void>: 3 pointers
274        // We don't store any custom data ourselves in the GClosure
275        let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
276            + 3 * mem::size_of::<*mut c_void>() as u32;
277        let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
278        let callback = Box::new(callback);
279        let ptr: *mut F = Box::into_raw(callback);
280        let ptr: *mut c_void = ptr as *mut _;
281        gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
282        gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
283        from_glib_none(closure)
284    }
285
286    // rustdoc-stripper-ignore-next
287    /// Invokes the closure with the given arguments.
288    ///
289    /// For invalidated closures this returns the "default" value of the return type.
290    ///
291    /// # Safety
292    ///
293    /// The argument types and return value type must match the ones expected by the closure or
294    /// otherwise the behaviour is undefined.
295    ///
296    /// Closures created from Rust via e.g. [`Closure::new`] will panic on type mismatches but
297    /// this is not guaranteed for closures created from other languages.
298    #[doc(alias = "g_closure_invoke")]
299    pub unsafe fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
300        let mut result = if return_type == Type::UNIT {
301            Value::uninitialized()
302        } else {
303            Value::from_type(return_type)
304        };
305        let result_ptr = if return_type == Type::UNIT {
306            ptr::null_mut()
307        } else {
308            result.to_glib_none_mut().0
309        };
310
311        gobject_ffi::g_closure_invoke(
312            self.to_glib_none().0,
313            result_ptr,
314            values.len() as u32,
315            mut_override(values.as_ptr()) as *mut gobject_ffi::GValue,
316            ptr::null_mut(),
317        );
318
319        if return_type == Type::UNIT {
320            None
321        } else {
322            Some(result)
323        }
324    }
325
326    // rustdoc-stripper-ignore-next
327    /// Invalidates the closure.
328    ///
329    /// Invoking an invalidated closure has no effect.
330    #[doc(alias = "g_closure_invalidate")]
331    pub fn invalidate(&self) {
332        unsafe {
333            gobject_ffi::g_closure_invalidate(self.to_glib_none().0);
334        }
335    }
336}
337
338pub trait IntoClosureReturnValue {
339    fn into_closure_return_value(self) -> Option<Value>;
340}
341
342impl IntoClosureReturnValue for () {
343    #[inline]
344    fn into_closure_return_value(self) -> Option<Value> {
345        None
346    }
347}
348
349impl<T: Into<Value>> IntoClosureReturnValue for T {
350    #[inline]
351    fn into_closure_return_value(self) -> Option<Value> {
352        Some(self.into())
353    }
354}
355
356pub trait TryFromClosureReturnValue: StaticType + Sized + 'static {
357    fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError>;
358}
359
360impl TryFromClosureReturnValue for () {
361    #[inline]
362    fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
363        match v {
364            None => Ok(()),
365            Some(v) => Err(bool_error!(
366                "Invalid return value: expected (), got {}",
367                v.type_()
368            )),
369        }
370    }
371}
372
373impl<T: for<'a> crate::value::FromValue<'a> + StaticType + 'static> TryFromClosureReturnValue
374    for T
375{
376    #[inline]
377    fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
378        v.ok_or_else(|| {
379            bool_error!(
380                "Invalid return value: expected {}, got ()",
381                T::static_type()
382            )
383        })
384        .and_then(|v| {
385            v.get_owned::<T>().map_err(|_| {
386                bool_error!(
387                    "Invalid return value: expected {}, got {}",
388                    T::static_type(),
389                    v.type_()
390                )
391            })
392        })
393    }
394}
395
396unsafe impl Send for Closure {}
397unsafe impl Sync for Closure {}
398
399#[cfg(test)]
400mod tests {
401    use std::sync::{
402        atomic::{AtomicUsize, Ordering},
403        Arc,
404    };
405
406    use super::*;
407
408    #[allow(clippy::unnecessary_wraps)]
409    fn closure_fn(values: &[Value]) -> Option<Value> {
410        assert_eq!(values.len(), 2);
411        let string_arg = values[0].get::<&str>();
412        assert_eq!(string_arg, Ok("test"));
413        let int_arg = values[1].get::<i32>();
414        assert_eq!(int_arg, Ok(42));
415        Some(24.to_value())
416    }
417
418    #[test]
419    fn test_closure() {
420        let call_count = Arc::new(AtomicUsize::new(0));
421
422        let count = call_count.clone();
423        let closure = RustClosure::new(move |values| {
424            count.fetch_add(1, Ordering::Relaxed);
425            assert_eq!(values.len(), 2);
426            let string_arg = values[0].get::<&str>();
427            assert_eq!(string_arg, Ok("test"));
428            let int_arg = values[1].get::<i32>();
429            assert_eq!(int_arg, Ok(42));
430            None
431        });
432        closure.invoke::<()>(&[&"test", &42]);
433        assert_eq!(call_count.load(Ordering::Relaxed), 1);
434
435        closure.invoke::<()>(&[&"test", &42]);
436        assert_eq!(call_count.load(Ordering::Relaxed), 2);
437
438        closure.invalidate();
439        closure.invoke::<()>(&[&"test", &42]);
440        assert_eq!(call_count.load(Ordering::Relaxed), 2);
441
442        let closure = RustClosure::new(closure_fn);
443        let result = closure.invoke::<i32>(&[&"test", &42]);
444        assert_eq!(result, 24);
445        closure.invalidate();
446        let result = closure.invoke::<i32>(&[&"test", &42]);
447        assert_eq!(result, 0);
448    }
449}