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::{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(ptr: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(ptr: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 /// The inner refcounted type
105 type RefCountedType: RefCounted;
106
107 // rustdoc-stripper-ignore-next
108 /// Converts the SharedType into its inner RefCountedType
109 fn into_refcounted(self) -> Self::RefCountedType;
110
111 // rustdoc-stripper-ignore-next
112 /// Constructs a SharedType from a RefCountedType
113 fn from_refcounted(this: Self::RefCountedType) -> Self;
114}
115
116// rustdoc-stripper-ignore-next
117/// Register a boxed `glib::Type` ID for `T`.
118///
119/// This must be called only once and will panic on a second call.
120///
121/// See [`Shared!`] for defining a function that ensures that
122/// this is only called once and returns the type id.
123///
124/// [`Shared!`]: ../../derive.Shared.html
125pub fn register_shared_type<T: SharedType>() -> crate::Type {
126 unsafe {
127 use std::ffi::CString;
128 unsafe extern "C" fn shared_ref<T: SharedType>(v: ffi::gpointer) -> ffi::gpointer {
129 T::RefCountedType::ref_(v as *const <T::RefCountedType as RefCounted>::InnerType)
130 as ffi::gpointer
131 }
132 unsafe extern "C" fn shared_unref<T: SharedType>(v: ffi::gpointer) {
133 let _ = T::RefCountedType::from_raw(
134 v as *const <T::RefCountedType as RefCounted>::InnerType,
135 );
136 }
137
138 let type_name = CString::new(T::NAME).unwrap();
139 assert_eq!(
140 gobject_ffi::g_type_from_name(type_name.as_ptr()),
141 gobject_ffi::G_TYPE_INVALID,
142 "Type {} has already been registered",
143 type_name.to_str().unwrap()
144 );
145
146 let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
147 type_name.as_ptr(),
148 Some(shared_ref::<T>),
149 Some(shared_unref::<T>),
150 ));
151 assert!(type_.is_valid());
152
153 type_
154 }
155}
156
157#[cfg(test)]
158mod test {
159 use super::*;
160 // We rename the current crate as glib, since the macros in glib-macros
161 // generate the glib namespace through the crate_ident_new utility,
162 // and that returns `glib` (and not `crate`) when called inside the glib crate
163 use crate as glib;
164
165 #[derive(Clone, Debug, PartialEq, Eq)]
166 struct MySharedInner {
167 foo: String,
168 }
169
170 #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
171 #[shared_boxed_type(name = "MySharedArc")]
172 struct MySharedArc(std::sync::Arc<MySharedInner>);
173
174 #[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
175 #[shared_boxed_type(name = "MySharedRc")]
176 struct MySharedRc(std::rc::Rc<MySharedInner>);
177
178 #[test]
179 fn test_register() {
180 assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
181 assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
182 }
183
184 #[test]
185 fn test_value_arc() {
186 assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
187
188 let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
189 foo: String::from("abc"),
190 }));
191 let v = b.to_value();
192 let b2 = v.get::<MySharedArc>().unwrap();
193 assert!(std::sync::Arc::ptr_eq(&b.0, &b2.0));
194 }
195
196 #[test]
197 fn test_value_rc() {
198 assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
199
200 let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
201 foo: String::from("abc"),
202 }));
203 let v = b.to_value();
204 let b2 = v.get::<MySharedRc>().unwrap();
205 assert!(std::rc::Rc::ptr_eq(&b.0, &b2.0));
206 }
207
208 #[test]
209 fn same_ffi_pointer_arc() {
210 assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
211
212 let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
213 foo: String::from("abc"),
214 }));
215
216 let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
217
218 assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
219
220 let inner_raw_ptr_clone =
221 unsafe { <MySharedArc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
222
223 assert_eq!(std::sync::Arc::strong_count(&b.0), 3);
224 assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
225
226 let _ = unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
227 let _ =
228 unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
229 assert_eq!(std::sync::Arc::strong_count(&b.0), 1);
230 }
231
232 #[test]
233 fn same_ffi_pointer_rc() {
234 assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
235
236 let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
237 foo: String::from("abc"),
238 }));
239
240 let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
241
242 assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
243
244 let inner_raw_ptr_clone =
245 unsafe { <MySharedRc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
246
247 assert_eq!(std::rc::Rc::strong_count(&b.0), 3);
248 assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
249
250 let _ = unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
251 let _ =
252 unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
253 assert_eq!(std::rc::Rc::strong_count(&b.0), 1);
254 }
255
256 #[test]
257 fn from_glib_borrow_arc() {
258 assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
259
260 let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
261 foo: String::from("abc"),
262 }));
263
264 let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
265
266 assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
267
268 unsafe {
269 let _ = MySharedArc::from_glib_borrow(inner_raw_ptr);
270 assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
271 }
272
273 assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
274 }
275
276 #[test]
277 fn from_glib_borrow_rc() {
278 assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
279
280 let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
281 foo: String::from("abc"),
282 }));
283
284 let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
285
286 assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
287
288 unsafe {
289 let _ = MySharedRc::from_glib_borrow(inner_raw_ptr);
290 assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
291 }
292
293 assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
294 }
295}
296