glib/
closure.rs

1// Copyright 2013-2017, The Gtk-rs Project Developers.
2// See the COPYRIGHT file at the top-level directory of this distribution.
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5// TODO: support marshaller.
6
7use std::mem;
8use std::ptr;
9use std::slice;
10
11use libc::{c_uint, c_void};
12
13use gobject_sys;
14use translate::{from_glib_none, mut_override, ToGlibPtr, ToGlibPtrMut, Uninitialized};
15use types::Type;
16use ToValue;
17use Value;
18
19glib_wrapper! {
20    #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
21    pub struct Closure(Shared<gobject_sys::GClosure>);
22
23    match fn {
24        ref => |ptr| {
25            gobject_sys::g_closure_ref(ptr);
26            gobject_sys::g_closure_sink(ptr);
27        },
28        unref => |ptr| gobject_sys::g_closure_unref(ptr),
29        get_type => || gobject_sys::g_closure_get_type(),
30    }
31}
32
33impl Closure {
34    pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
35        unsafe { Closure::new_unsafe(callback) }
36    }
37
38    pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
39        unsafe extern "C" fn marshal<F>(
40            _closure: *mut gobject_sys::GClosure,
41            return_value: *mut gobject_sys::GValue,
42            n_param_values: c_uint,
43            param_values: *const gobject_sys::GValue,
44            _invocation_hint: *mut c_void,
45            marshal_data: *mut c_void,
46        ) where
47            F: Fn(&[Value]) -> Option<Value>,
48        {
49            let values = slice::from_raw_parts(param_values as *const _, n_param_values as usize);
50            let callback: Box<F> = Box::from_raw(marshal_data as *mut _);
51            let result = callback(values);
52            if !return_value.is_null() {
53                match result {
54                    Some(result) => *return_value = result.into_raw(),
55                    None => {
56                        let result = Value::uninitialized();
57                        *return_value = result.into_raw();
58                    }
59                }
60            }
61            mem::forget(callback);
62        }
63
64        unsafe extern "C" fn finalize<F>(
65            notify_data: *mut c_void,
66            _closure: *mut gobject_sys::GClosure,
67        ) where
68            F: Fn(&[Value]) -> Option<Value>,
69        {
70            let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
71            // callback is dropped here.
72        }
73
74        // Due to bitfields we have to do our own calculations here for the size of the GClosure:
75        // - 4: 32 bits in guint bitfields at the beginning
76        // - padding due to alignment needed for the following pointer
77        // - 3 * size_of<*mut c_void>: 3 pointers
78        // We don't store any custom data ourselves in the GClosure
79        let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
80            + 3 * mem::size_of::<*mut c_void>() as u32;
81        let closure = gobject_sys::g_closure_new_simple(size, ptr::null_mut());
82        assert_ne!(closure, ptr::null_mut());
83        let callback = Box::new(callback);
84        let ptr: *mut F = Box::into_raw(callback);
85        let ptr: *mut c_void = ptr as *mut _;
86        gobject_sys::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
87        gobject_sys::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
88        from_glib_none(closure)
89    }
90
91    #[allow(clippy::redundant_closure)]
92    pub fn invoke(&self, values: &[&dyn ToValue]) -> Option<Value> {
93        let mut result = unsafe { Value::uninitialized() };
94
95        let v_args: Vec<Value>;
96        let mut s_args: [Value; 10] = unsafe { mem::zeroed() };
97        let values = if values.len() <= 10 {
98            for (i, arg) in values.iter().enumerate() {
99                s_args[i] = arg.to_value();
100            }
101            &s_args[0..values.len()]
102        } else {
103            v_args = values.iter().map(|v| v.to_value()).collect();
104            v_args.as_slice()
105        };
106
107        unsafe {
108            gobject_sys::g_closure_invoke(
109                self.to_glib_none().0 as *mut _,
110                result.to_glib_none_mut().0,
111                values.len() as u32,
112                mut_override(values.as_ptr()) as *mut gobject_sys::GValue,
113                ptr::null_mut(),
114            );
115        }
116        if result.type_() == Type::Invalid {
117            None
118        } else {
119            Some(result)
120        }
121    }
122}
123
124unsafe impl Send for Closure {}
125unsafe impl Sync for Closure {}
126
127#[cfg(test)]
128mod tests {
129    use std::sync::atomic::{AtomicUsize, Ordering};
130    use std::sync::Arc;
131
132    use super::Closure;
133    use ToValue;
134    use Value;
135
136    fn closure_fn(values: &[Value]) -> Option<Value> {
137        assert_eq!(values.len(), 2);
138        let string: Option<String> = values[0].get();
139        assert_eq!(string, Some("test".to_string()));
140        let int: Option<i32> = values[1].get();
141        assert_eq!(int, Some(42));
142        Some(24.to_value())
143    }
144
145    #[test]
146    fn test_closure() {
147        let call_count = Arc::new(AtomicUsize::new(0));
148
149        let count = call_count.clone();
150        let closure = Closure::new(move |values| {
151            count.fetch_add(1, Ordering::Relaxed);
152            assert_eq!(values.len(), 2);
153            let string: Option<String> = values[0].get();
154            assert_eq!(string, Some("test".to_string()));
155            let int: Option<i32> = values[1].get();
156            assert_eq!(int, Some(42));
157            None
158        });
159        let result = closure.invoke(&[&"test".to_string(), &42]);
160        assert!(result.is_none());
161        assert_eq!(call_count.load(Ordering::Relaxed), 1);
162
163        let result = closure.invoke(&[&"test".to_string(), &42]);
164        assert!(result.is_none());
165        assert_eq!(call_count.load(Ordering::Relaxed), 2);
166
167        let closure = Closure::new(closure_fn);
168        let result = closure.invoke(&[&"test".to_string(), &42]);
169        let int: Option<i32> = result.and_then(|result| result.get());
170        assert_eq!(int, Some(24));
171    }
172}