fbl/
opaque_ref_counted.rs1use crate::recyclable::Recyclable;
8use crate::ref_counted::{HasRefCount, RefCounted};
9use core::marker::{PhantomData, PhantomPinned};
10use core::ops::Deref;
11use core::ptr::NonNull;
12use zr::Opaque;
13
14#[repr(transparent)]
17pub struct OpaqueRefCounted<T>(Opaque<T>);
18
19impl<T> OpaqueRefCounted<T> {
20 pub fn get(&self) -> *mut T {
22 self.0.get()
23 }
24}
25
26impl<T> HasRefCount for OpaqueRefCounted<T> {
27 fn ref_count(&self) -> &RefCounted {
28 unsafe { &*(self.get() as *const RefCounted) }
30 }
31}
32
33#[repr(C)]
39#[derive(Default)]
40pub struct OpaqueRefCountedFacade<B = RefCounted> {
41 _marker: PhantomData<(PhantomPinned, fn() -> B)>,
42 _facade: zr::OpaqueFacade,
43}
44
45unsafe impl<B> Send for OpaqueRefCountedFacade<B> {}
46unsafe impl<B> Sync for OpaqueRefCountedFacade<B> {}
47
48impl<B: HasRefCount> HasRefCount for OpaqueRefCountedFacade<B> {
49 #[inline]
50 fn ref_count(&self) -> &RefCounted {
51 unsafe {
53 let b_ptr = self as *const Self as *const B;
54 (*b_ptr).ref_count()
55 }
56 }
57}
58
59unsafe impl<B: Recyclable> Recyclable for OpaqueRefCountedFacade<B> {
60 #[inline]
61 unsafe fn recycle(ptr: NonNull<Self>) {
62 unsafe {
63 B::recycle(ptr.cast::<B>());
64 }
65 }
66}
67
68pub unsafe trait IsOpaqueRefCounted: Deref + Sized {
77 type TargetBase: HasRefCount + Recyclable;
78}
79
80impl<T: IsOpaqueRefCounted> HasRefCount for T {
81 #[inline]
82 fn ref_count(&self) -> &RefCounted {
83 let base_ptr = self.deref() as *const T::Target as *const T::TargetBase;
84 unsafe { (*base_ptr).ref_count() }
86 }
87}
88
89unsafe impl<T: IsOpaqueRefCounted> Recyclable for T {
90 #[inline]
91 unsafe fn recycle(ptr: NonNull<Self>) {
92 unsafe {
93 let base_ptr = ptr.cast::<T::TargetBase>();
94 <T::TargetBase as Recyclable>::recycle(base_ptr);
95 }
96 }
97}
98
99#[macro_export]
121macro_rules! impl_opaque_ref_counted_facade {
122 (
123 $(#[$meta:meta])*
124 $vis:vis struct $name:ident,
125 $recycle_fn:path $(,)?
126 ) => {
127 $(#[$meta])*
128 #[repr(C)]
129 $vis struct $name {
130 _facade: $crate::OpaqueRefCountedFacade,
131 }
132
133 impl $crate::HasRefCount for $name {
134 #[inline]
135 fn ref_count(&self) -> &$crate::RefCounted {
136 unsafe { &*(self as *const Self as *const $crate::RefCounted) }
139 }
140 }
141
142 unsafe impl $crate::Recyclable for $name {
144 #[inline]
145 unsafe fn recycle(ptr: core::ptr::NonNull<Self>) {
146 unsafe {
148 $recycle_fn(ptr.as_ptr() as *mut Self);
149 }
150 }
151 }
152 };
153 (
154 $(#[$meta:meta])*
155 $vis:vis struct $name:ident,
156 $recycle_fn:path,
157 $get_ref_counted_fn:path $(,)?
158 ) => {
159 $(#[$meta])*
160 #[repr(C)]
161 $vis struct $name {
162 _facade: $crate::OpaqueRefCountedFacade,
163 }
164
165 impl $crate::HasRefCount for $name {
166 #[inline]
167 fn ref_count(&self) -> &$crate::RefCounted {
168 unsafe {
171 &*($get_ref_counted_fn(self as *const Self as *mut Self)
172 as *const $crate::RefCounted)
173 }
174 }
175 }
176
177 unsafe impl $crate::Recyclable for $name {
179 #[inline]
180 unsafe fn recycle(ptr: core::ptr::NonNull<Self>) {
181 unsafe {
183 $recycle_fn(ptr.as_ptr() as *mut Self);
184 }
185 }
186 }
187 };
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193 use crate::recyclable::Recyclable;
194 use crate::ref_ptr::RefPtr;
195 use core::ffi::c_void;
196 use core::ptr::NonNull;
197
198 unsafe extern "C" {
199 fn create_cpp_ref_counted_object(destroyed: *mut bool) -> *mut c_void;
200 fn destroy_cpp_ref_counted_object(ptr: *mut c_void);
201 }
202
203 pub struct TestCppRefCountedObject;
204
205 unsafe impl Recyclable for OpaqueRefCounted<TestCppRefCountedObject> {
206 unsafe fn recycle(ptr: NonNull<Self>) {
207 unsafe {
208 destroy_cpp_ref_counted_object(ptr.as_ptr() as *mut c_void);
209 }
210 }
211 }
212
213 #[test]
214 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
215 fn test_cross_lang_ref_ptr() {
216 use core::sync::atomic::{AtomicBool, Ordering};
217
218 let destroyed = AtomicBool::new(false);
219 unsafe {
220 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
221 assert!(!destroyed.load(Ordering::Relaxed));
222
223 {
224 let ref_ptr =
225 RefPtr::from_raw(raw_ptr as *mut OpaqueRefCounted<TestCppRefCountedObject>);
226 assert!(!destroyed.load(Ordering::Relaxed));
227
228 let ref_ptr_clone = ref_ptr.clone();
229 assert!(!destroyed.load(Ordering::Relaxed));
230
231 drop(ref_ptr_clone);
233 assert!(!destroyed.load(Ordering::Relaxed));
234 } assert!(destroyed.load(Ordering::Relaxed));
237 }
238 }
239
240 #[test]
241 fn test_opaque_ref_counted_allocate_fails() {
242 let val = OpaqueRefCounted(Opaque::uninit());
243 let res = OpaqueRefCounted::<TestCppRefCountedObject>::allocate(val);
244 assert!(res.is_err());
245 }
246
247 pub struct TestCppFacadeBase;
248 unsafe impl Recyclable for TestCppFacadeBase {
249 unsafe fn recycle(ptr: NonNull<Self>) {
250 unsafe {
251 destroy_cpp_ref_counted_object(ptr.as_ptr() as *mut c_void);
252 }
253 }
254 }
255 impl HasRefCount for TestCppFacadeBase {
256 fn ref_count(&self) -> &RefCounted {
257 unsafe { &*(self as *const Self as *const RefCounted) }
258 }
259 }
260
261 #[repr(C)]
262 pub struct TestSubtypeFacade {
263 _facade: OpaqueRefCountedFacade<TestCppFacadeBase>,
264 }
265 impl Deref for TestSubtypeFacade {
266 type Target = TestCppFacadeBase;
267 fn deref(&self) -> &Self::Target {
268 unsafe { &*(self as *const Self as *const TestCppFacadeBase) }
269 }
270 }
271 unsafe impl IsOpaqueRefCounted for TestSubtypeFacade {
272 type TargetBase = TestCppFacadeBase;
273 }
274
275 #[test]
276 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
277 fn test_facade_ref_ptr() {
278 use core::sync::atomic::{AtomicBool, Ordering};
279
280 let destroyed = AtomicBool::new(false);
281 unsafe {
282 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
283 assert!(!destroyed.load(Ordering::Relaxed));
284
285 {
286 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestSubtypeFacade);
287 assert!(!destroyed.load(Ordering::Relaxed));
288
289 let ref_ptr_clone = ref_ptr.clone();
290 assert!(!destroyed.load(Ordering::Relaxed));
291
292 drop(ref_ptr_clone);
293 assert!(!destroyed.load(Ordering::Relaxed));
294 }
295
296 assert!(destroyed.load(Ordering::Relaxed));
297 }
298 }
299
300 unsafe extern "C" fn test_release_fn(ptr: *mut TestMacroFacade) {
301 unsafe {
302 destroy_cpp_ref_counted_object(ptr as *mut c_void);
303 }
304 }
305
306 impl_opaque_ref_counted_facade!(
307 pub struct TestMacroFacade,
308 test_release_fn,
309 );
310
311 #[test]
312 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
313 fn test_macro_facade_ref_ptr() {
314 use core::sync::atomic::{AtomicBool, Ordering};
315
316 let destroyed = AtomicBool::new(false);
317 unsafe {
318 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
319 assert!(!destroyed.load(Ordering::Relaxed));
320
321 {
322 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestMacroFacade);
323 assert!(!destroyed.load(Ordering::Relaxed));
324
325 let ref_ptr_clone = ref_ptr.clone();
326 assert!(!destroyed.load(Ordering::Relaxed));
327
328 drop(ref_ptr_clone);
329 assert!(!destroyed.load(Ordering::Relaxed));
330 }
331
332 assert!(destroyed.load(Ordering::Relaxed));
333 }
334 }
335
336 unsafe extern "C" fn test_get_ref_counted_fn(
337 ptr: *mut TestMacroFacadeWithGetter,
338 ) -> *mut RefCounted {
339 ptr as *mut RefCounted
340 }
341
342 unsafe extern "C" fn test_release_fn_with_getter(ptr: *mut TestMacroFacadeWithGetter) {
343 unsafe {
344 destroy_cpp_ref_counted_object(ptr as *mut c_void);
345 }
346 }
347
348 impl_opaque_ref_counted_facade!(
349 pub struct TestMacroFacadeWithGetter,
350 test_release_fn_with_getter,
351 test_get_ref_counted_fn,
352 );
353
354 #[test]
355 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
356 fn test_macro_facade_with_getter_ref_ptr() {
357 use core::sync::atomic::{AtomicBool, Ordering};
358
359 let destroyed = AtomicBool::new(false);
360 unsafe {
361 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
362 assert!(!destroyed.load(Ordering::Relaxed));
363
364 {
365 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestMacroFacadeWithGetter);
366 assert!(!destroyed.load(Ordering::Relaxed));
367
368 let ref_ptr_clone = ref_ptr.clone();
369 assert!(!destroyed.load(Ordering::Relaxed));
370
371 drop(ref_ptr_clone);
372 assert!(!destroyed.load(Ordering::Relaxed));
373 }
374
375 assert!(destroyed.load(Ordering::Relaxed));
376 }
377 }
378}