glib/subclass/
shared.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Module for registering shared types for Rust types.
5
6use crate::{ffi, gobject_ffi, prelude::*, translate::*};
7
8pub unsafe trait RefCounted: Clone + Sized + 'static {
9    // rustdoc-stripper-ignore-next
10    /// The inner type
11    type InnerType;
12
13    // rustdoc-stripper-ignore-next
14    /// The function used to increment the inner type refcount
15    unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType;
16
17    // rustdoc-stripper-ignore-next
18    /// Provides access to a raw pointer to InnerType
19    fn as_ptr(&self) -> *const Self::InnerType;
20
21    // rustdoc-stripper-ignore-next
22    /// Converts the RefCounted object to a raw pointer to InnerType
23    unsafe fn into_raw(self) -> *const Self::InnerType;
24
25    // rustdoc-stripper-ignore-next
26    /// Converts a raw pointer to InnerType to a RefCounted object
27    unsafe fn from_raw(this: *const Self::InnerType) -> Self;
28}
29
30unsafe impl<T> RefCounted for std::sync::Arc<T>
31where
32    T: 'static,
33{
34    type InnerType = T;
35
36    #[inline]
37    unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
38        std::sync::Arc::increment_strong_count(this);
39        this
40    }
41
42    #[inline]
43    fn as_ptr(&self) -> *const Self::InnerType {
44        std::sync::Arc::as_ptr(self)
45    }
46
47    #[inline]
48    unsafe fn into_raw(self) -> *const Self::InnerType {
49        std::sync::Arc::into_raw(self)
50    }
51
52    #[inline]
53    unsafe fn from_raw(this: *const Self::InnerType) -> Self {
54        std::sync::Arc::from_raw(this)
55    }
56}
57
58unsafe impl<T> RefCounted for std::rc::Rc<T>
59where
60    T: 'static,
61{
62    type InnerType = T;
63
64    #[inline]
65    unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
66        use std::mem::ManuallyDrop;
67        let this_rc = ManuallyDrop::new(std::rc::Rc::from_raw(this));
68        std::rc::Rc::into_raw(ManuallyDrop::take(&mut this_rc.clone()))
69    }
70
71    #[inline]
72    fn as_ptr(&self) -> *const Self::InnerType {
73        std::rc::Rc::as_ptr(self)
74    }
75
76    #[inline]
77    unsafe fn into_raw(self) -> *const Self::InnerType {
78        std::rc::Rc::into_raw(self)
79    }
80
81    #[inline]
82    unsafe fn from_raw(this: *const Self::InnerType) -> Self {
83        std::rc::Rc::from_raw(this)
84    }
85}
86
87// rustdoc-stripper-ignore-next
88/// Trait for defining shared types.
89///
90/// Links together the type name with the type itself.
91///
92/// See [`register_shared_type`] for registering an implementation of this trait
93/// with the type system.
94///
95/// [`register_shared_type`]: fn.register_shared_type.html
96pub trait SharedType: StaticType + Clone + Sized + 'static {
97    // rustdoc-stripper-ignore-next
98    /// Shared type name.
99    ///
100    /// This must be unique in the whole process.
101    const NAME: &'static str;
102
103    // rustdoc-stripper-ignore-next
104    /// Allow name conflicts for this boxed type.
105    ///
106    /// By default, trying to register a type with a name that was registered before will panic. If
107    /// this is set to `true` then a new name will be selected by appending a counter.
108    ///
109    /// This is useful for defining new types in Rust library crates that might be linked multiple
110    /// times in the same process.
111    ///
112    /// A consequence of setting this to `true` is that it's not guaranteed that
113    /// `glib::Type::from_name(Self::NAME).unwrap() == Self::static_type()`.
114    ///
115    /// Optional.
116    const ALLOW_NAME_CONFLICT: bool = false;
117
118    // rustdoc-stripper-ignore-next
119    /// The inner refcounted type
120    type RefCountedType: RefCounted;
121
122    // rustdoc-stripper-ignore-next
123    /// Converts the SharedType into its inner RefCountedType
124    fn into_refcounted(self) -> Self::RefCountedType;
125
126    // rustdoc-stripper-ignore-next
127    /// Constructs a SharedType from a RefCountedType
128    fn from_refcounted(this: Self::RefCountedType) -> Self;
129}
130
131// rustdoc-stripper-ignore-next
132/// Register a boxed `glib::Type` ID for `T`.
133///
134/// This must be called only once and will panic on a second call.
135///
136/// See [`Shared!`] for defining a function that ensures that
137/// this is only called once and returns the type id.
138///
139/// [`Shared!`]: ../../derive.Shared.html
140pub fn register_shared_type<T: SharedType>() -> crate::Type {
141    unsafe {
142        use std::ffi::CString;
143        unsafe extern "C" fn shared_ref<T: SharedType>(v: ffi::gpointer) -> ffi::gpointer {
144            T::RefCountedType::ref_(v as *const <T::RefCountedType as RefCounted>::InnerType)
145                as ffi::gpointer
146        }
147        unsafe extern "C" fn shared_unref<T: SharedType>(v: ffi::gpointer) {
148            let _ = T::RefCountedType::from_raw(
149                v as *const <T::RefCountedType as RefCounted>::InnerType,
150            );
151        }
152
153        let type_name = if T::ALLOW_NAME_CONFLICT {
154            let mut i = 0;
155            loop {
156                let type_name = CString::new(if i == 0 {
157                    T::NAME.to_string()
158                } else {
159                    format!("{}-{}", T::NAME, i)
160                })
161                .unwrap();
162                if gobject_ffi::g_type_from_name(type_name.as_ptr()) == gobject_ffi::G_TYPE_INVALID
163                {
164                    break type_name;
165                }
166                i += 1;
167            }
168        } else {
169            let type_name = CString::new(T::NAME).unwrap();
170            assert_eq!(
171                gobject_ffi::g_type_from_name(type_name.as_ptr()),
172                gobject_ffi::G_TYPE_INVALID,
173                "Type {} has already been registered",
174                type_name.to_str().unwrap()
175            );
176
177            type_name
178        };
179
180        let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
181            type_name.as_ptr(),
182            Some(shared_ref::<T>),
183            Some(shared_unref::<T>),
184        ));
185        assert!(type_.is_valid());
186
187        type_
188    }
189}
190
191#[cfg(test)]
192mod test {
193    use super::*;
194    // We rename the current crate as glib, since the macros in glib-macros
195    // generate the glib namespace through the crate_ident_new utility,
196    // and that returns `glib` (and not `crate`) when called inside the glib crate
197    use crate as glib;
198
199    #[derive(Clone, Debug, PartialEq, Eq)]
200    struct MySharedInner {
201        foo: String,
202    }
203
204    #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
205    #[shared_boxed_type(name = "MySharedArc")]
206    struct MySharedArc(std::sync::Arc<MySharedInner>);
207
208    #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
209    #[shared_boxed_type(name = "MySharedRc")]
210    struct MySharedRc(std::rc::Rc<MySharedInner>);
211
212    #[test]
213    fn test_register() {
214        assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
215        assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
216    }
217
218    #[test]
219    fn test_value_arc() {
220        assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
221
222        let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
223            foo: String::from("abc"),
224        }));
225        let v = b.to_value();
226        let b2 = v.get::<MySharedArc>().unwrap();
227        assert!(std::sync::Arc::ptr_eq(&b.0, &b2.0));
228    }
229
230    #[test]
231    fn test_value_rc() {
232        assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
233
234        let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
235            foo: String::from("abc"),
236        }));
237        let v = b.to_value();
238        let b2 = v.get::<MySharedRc>().unwrap();
239        assert!(std::rc::Rc::ptr_eq(&b.0, &b2.0));
240    }
241
242    #[test]
243    fn same_ffi_pointer_arc() {
244        assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
245
246        let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
247            foo: String::from("abc"),
248        }));
249
250        let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
251
252        assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
253
254        let inner_raw_ptr_clone =
255            unsafe { <MySharedArc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
256
257        assert_eq!(std::sync::Arc::strong_count(&b.0), 3);
258        assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
259
260        let _ = unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
261        let _ =
262            unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
263        assert_eq!(std::sync::Arc::strong_count(&b.0), 1);
264    }
265
266    #[test]
267    fn same_ffi_pointer_rc() {
268        assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
269
270        let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
271            foo: String::from("abc"),
272        }));
273
274        let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
275
276        assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
277
278        let inner_raw_ptr_clone =
279            unsafe { <MySharedRc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
280
281        assert_eq!(std::rc::Rc::strong_count(&b.0), 3);
282        assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
283
284        let _ = unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
285        let _ =
286            unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
287        assert_eq!(std::rc::Rc::strong_count(&b.0), 1);
288    }
289
290    #[test]
291    fn from_glib_borrow_arc() {
292        assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
293
294        let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
295            foo: String::from("abc"),
296        }));
297
298        let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
299
300        assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
301
302        unsafe {
303            let _ = MySharedArc::from_glib_borrow(inner_raw_ptr);
304            assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
305        }
306
307        assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
308        unsafe {
309            let _ = std::sync::Arc::from_raw(inner_raw_ptr);
310        }
311        assert_eq!(std::sync::Arc::strong_count(&b.0), 1);
312    }
313
314    #[test]
315    fn from_glib_borrow_rc() {
316        assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
317
318        let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
319            foo: String::from("abc"),
320        }));
321
322        let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
323
324        assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
325
326        unsafe {
327            let _ = MySharedRc::from_glib_borrow(inner_raw_ptr);
328            assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
329        }
330
331        assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
332        unsafe {
333            let _ = std::rc::Rc::from_raw(inner_raw_ptr);
334        }
335        assert_eq!(std::rc::Rc::strong_count(&b.0), 1);
336    }
337}