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 fn ref_count(&self) -> &RefCounted {
50 unsafe {
52 let b_ptr = self as *const Self as *const B;
53 (*b_ptr).ref_count()
54 }
55 }
56}
57
58unsafe impl<B: Recyclable> Recyclable for OpaqueRefCountedFacade<B> {
59 unsafe fn recycle(ptr: NonNull<Self>) {
60 unsafe {
61 B::recycle(ptr.cast::<B>());
62 }
63 }
64}
65
66pub unsafe trait IsOpaqueRefCounted: Deref + Sized {
75 type TargetBase: HasRefCount + Recyclable;
76}
77
78impl<T: IsOpaqueRefCounted> HasRefCount for T {
79 fn ref_count(&self) -> &RefCounted {
80 let base_ptr = self.deref() as *const T::Target as *const T::TargetBase;
81 unsafe { (*base_ptr).ref_count() }
83 }
84}
85
86unsafe impl<T: IsOpaqueRefCounted> Recyclable for T {
87 unsafe fn recycle(ptr: NonNull<Self>) {
88 unsafe {
89 let base_ptr = ptr.cast::<T::TargetBase>();
90 <T::TargetBase as Recyclable>::recycle(base_ptr);
91 }
92 }
93}
94
95#[macro_export]
117macro_rules! impl_opaque_ref_counted_facade {
118 (
119 $(#[$meta:meta])*
120 $vis:vis struct $name:ident,
121 $recycle_fn:path $(,)?
122 ) => {
123 $(#[$meta])*
124 #[repr(C)]
125 $vis struct $name {
126 _facade: $crate::OpaqueRefCountedFacade,
127 }
128
129 impl $crate::HasRefCount for $name {
130 fn ref_count(&self) -> &$crate::RefCounted {
131 unsafe { &*(self as *const Self as *const $crate::RefCounted) }
134 }
135 }
136
137 unsafe impl $crate::Recyclable for $name {
139 unsafe fn recycle(ptr: core::ptr::NonNull<Self>) {
140 unsafe {
142 $recycle_fn(ptr.as_ptr() as *mut Self);
143 }
144 }
145 }
146 };
147 (
148 $(#[$meta:meta])*
149 $vis:vis struct $name:ident,
150 $recycle_fn:path,
151 $get_ref_counted_fn:path $(,)?
152 ) => {
153 $(#[$meta])*
154 #[repr(C)]
155 $vis struct $name {
156 _facade: $crate::OpaqueRefCountedFacade,
157 }
158
159 impl $crate::HasRefCount for $name {
160 fn ref_count(&self) -> &$crate::RefCounted {
161 unsafe {
164 &*($get_ref_counted_fn(self as *const Self as *mut Self)
165 as *const $crate::RefCounted)
166 }
167 }
168 }
169
170 unsafe impl $crate::Recyclable for $name {
172 unsafe fn recycle(ptr: core::ptr::NonNull<Self>) {
173 unsafe {
175 $recycle_fn(ptr.as_ptr() as *mut Self);
176 }
177 }
178 }
179 };
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::recyclable::Recyclable;
186 use crate::ref_ptr::RefPtr;
187 use core::ffi::c_void;
188 use core::ptr::NonNull;
189
190 unsafe extern "C" {
191 fn create_cpp_ref_counted_object(destroyed: *mut bool) -> *mut c_void;
192 fn destroy_cpp_ref_counted_object(ptr: *mut c_void);
193 }
194
195 pub struct TestCppRefCountedObject;
196
197 unsafe impl Recyclable for OpaqueRefCounted<TestCppRefCountedObject> {
198 unsafe fn recycle(ptr: NonNull<Self>) {
199 unsafe {
200 destroy_cpp_ref_counted_object(ptr.as_ptr() as *mut c_void);
201 }
202 }
203 }
204
205 #[test]
206 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
207 fn test_cross_lang_ref_ptr() {
208 use core::sync::atomic::{AtomicBool, Ordering};
209
210 let destroyed = AtomicBool::new(false);
211 unsafe {
212 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
213 assert!(!destroyed.load(Ordering::Relaxed));
214
215 {
216 let ref_ptr =
217 RefPtr::from_raw(raw_ptr as *mut OpaqueRefCounted<TestCppRefCountedObject>);
218 assert!(!destroyed.load(Ordering::Relaxed));
219
220 let ref_ptr_clone = ref_ptr.clone();
221 assert!(!destroyed.load(Ordering::Relaxed));
222
223 drop(ref_ptr_clone);
225 assert!(!destroyed.load(Ordering::Relaxed));
226 } assert!(destroyed.load(Ordering::Relaxed));
229 }
230 }
231
232 #[test]
233 fn test_opaque_ref_counted_allocate_fails() {
234 let val = OpaqueRefCounted(Opaque::uninit());
235 let res = OpaqueRefCounted::<TestCppRefCountedObject>::allocate(val);
236 assert!(res.is_err());
237 }
238
239 pub struct TestCppFacadeBase;
240 unsafe impl Recyclable for TestCppFacadeBase {
241 unsafe fn recycle(ptr: NonNull<Self>) {
242 unsafe {
243 destroy_cpp_ref_counted_object(ptr.as_ptr() as *mut c_void);
244 }
245 }
246 }
247 impl HasRefCount for TestCppFacadeBase {
248 fn ref_count(&self) -> &RefCounted {
249 unsafe { &*(self as *const Self as *const RefCounted) }
250 }
251 }
252
253 #[repr(C)]
254 pub struct TestSubtypeFacade {
255 _facade: OpaqueRefCountedFacade<TestCppFacadeBase>,
256 }
257 impl Deref for TestSubtypeFacade {
258 type Target = TestCppFacadeBase;
259 fn deref(&self) -> &Self::Target {
260 unsafe { &*(self as *const Self as *const TestCppFacadeBase) }
261 }
262 }
263 unsafe impl IsOpaqueRefCounted for TestSubtypeFacade {
264 type TargetBase = TestCppFacadeBase;
265 }
266
267 #[test]
268 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
269 fn test_facade_ref_ptr() {
270 use core::sync::atomic::{AtomicBool, Ordering};
271
272 let destroyed = AtomicBool::new(false);
273 unsafe {
274 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
275 assert!(!destroyed.load(Ordering::Relaxed));
276
277 {
278 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestSubtypeFacade);
279 assert!(!destroyed.load(Ordering::Relaxed));
280
281 let ref_ptr_clone = ref_ptr.clone();
282 assert!(!destroyed.load(Ordering::Relaxed));
283
284 drop(ref_ptr_clone);
285 assert!(!destroyed.load(Ordering::Relaxed));
286 }
287
288 assert!(destroyed.load(Ordering::Relaxed));
289 }
290 }
291
292 unsafe extern "C" fn test_release_fn(ptr: *mut TestMacroFacade) {
293 unsafe {
294 destroy_cpp_ref_counted_object(ptr as *mut c_void);
295 }
296 }
297
298 impl_opaque_ref_counted_facade!(
299 pub struct TestMacroFacade,
300 test_release_fn,
301 );
302
303 #[test]
304 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
305 fn test_macro_facade_ref_ptr() {
306 use core::sync::atomic::{AtomicBool, Ordering};
307
308 let destroyed = AtomicBool::new(false);
309 unsafe {
310 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
311 assert!(!destroyed.load(Ordering::Relaxed));
312
313 {
314 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestMacroFacade);
315 assert!(!destroyed.load(Ordering::Relaxed));
316
317 let ref_ptr_clone = ref_ptr.clone();
318 assert!(!destroyed.load(Ordering::Relaxed));
319
320 drop(ref_ptr_clone);
321 assert!(!destroyed.load(Ordering::Relaxed));
322 }
323
324 assert!(destroyed.load(Ordering::Relaxed));
325 }
326 }
327
328 unsafe extern "C" fn test_get_ref_counted_fn(
329 ptr: *mut TestMacroFacadeWithGetter,
330 ) -> *mut RefCounted {
331 ptr as *mut RefCounted
332 }
333
334 unsafe extern "C" fn test_release_fn_with_getter(ptr: *mut TestMacroFacadeWithGetter) {
335 unsafe {
336 destroy_cpp_ref_counted_object(ptr as *mut c_void);
337 }
338 }
339
340 impl_opaque_ref_counted_facade!(
341 pub struct TestMacroFacadeWithGetter,
342 test_release_fn_with_getter,
343 test_get_ref_counted_fn,
344 );
345
346 #[test]
347 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
348 fn test_macro_facade_with_getter_ref_ptr() {
349 use core::sync::atomic::{AtomicBool, Ordering};
350
351 let destroyed = AtomicBool::new(false);
352 unsafe {
353 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
354 assert!(!destroyed.load(Ordering::Relaxed));
355
356 {
357 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestMacroFacadeWithGetter);
358 assert!(!destroyed.load(Ordering::Relaxed));
359
360 let ref_ptr_clone = ref_ptr.clone();
361 assert!(!destroyed.load(Ordering::Relaxed));
362
363 drop(ref_ptr_clone);
364 assert!(!destroyed.load(Ordering::Relaxed));
365 }
366
367 assert!(destroyed.load(Ordering::Relaxed));
368 }
369 }
370}