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