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 kalloc::AllocError;
13use zr::Opaque;
14
15#[repr(transparent)]
18pub struct OpaqueRefCounted<T>(Opaque<T>);
19
20impl<T> OpaqueRefCounted<T> {
21 pub fn get(&self) -> *mut T {
23 self.0.get()
24 }
25}
26
27impl<T> HasRefCount for OpaqueRefCounted<T> {
28 fn ref_count(&self) -> &RefCounted {
29 unsafe { &*(self.get() as *const RefCounted) }
31 }
32}
33
34#[repr(C)]
40#[derive(Default)]
41pub struct OpaqueRefCountedFacade<B = RefCounted> {
42 _marker: PhantomData<(PhantomPinned, fn() -> B)>,
43 _facade: zr::OpaqueFacade,
44}
45
46unsafe impl<B> Send for OpaqueRefCountedFacade<B> {}
47unsafe impl<B> Sync for OpaqueRefCountedFacade<B> {}
48
49impl<B: HasRefCount> HasRefCount for OpaqueRefCountedFacade<B> {
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 unsafe fn recycle(ptr: NonNull<Self>) {
61 unsafe {
62 B::recycle(ptr.cast::<B>());
63 }
64 }
65
66 fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> {
67 Err(AllocError)
68 }
69}
70
71pub unsafe trait IsOpaqueRefCounted: Deref + Sized {
79 type TargetBase: HasRefCount + Recyclable;
80}
81
82impl<T: IsOpaqueRefCounted> HasRefCount for T {
83 fn ref_count(&self) -> &RefCounted {
84 let base_ptr = self.deref() as *const T::Target as *const T::TargetBase;
85 unsafe { (*base_ptr).ref_count() }
87 }
88}
89
90unsafe impl<T: IsOpaqueRefCounted> Recyclable for T {
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 fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> {
99 Err(AllocError)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::recyclable::Recyclable;
107 use crate::ref_ptr::RefPtr;
108 use core::ffi::c_void;
109 use core::ptr::NonNull;
110
111 unsafe extern "C" {
112 fn create_cpp_ref_counted_object(destroyed: *mut bool) -> *mut c_void;
113 fn destroy_cpp_ref_counted_object(ptr: *mut c_void);
114 }
115
116 pub struct TestCppRefCountedObject;
117
118 unsafe impl Recyclable for OpaqueRefCounted<TestCppRefCountedObject> {
119 unsafe fn recycle(ptr: NonNull<Self>) {
120 unsafe {
121 destroy_cpp_ref_counted_object(ptr.as_ptr() as *mut c_void);
122 }
123 }
124
125 fn allocate(_value: Self) -> Result<NonNull<Self>, ::kalloc::AllocError> {
126 Err(::kalloc::AllocError)
127 }
128 }
129
130 #[test]
131 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
132 fn test_cross_lang_ref_ptr() {
133 use core::sync::atomic::{AtomicBool, Ordering};
134
135 let destroyed = AtomicBool::new(false);
136 unsafe {
137 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
138 assert!(!destroyed.load(Ordering::Relaxed));
139
140 {
141 let ref_ptr =
142 RefPtr::from_raw(raw_ptr as *mut OpaqueRefCounted<TestCppRefCountedObject>);
143 assert!(!destroyed.load(Ordering::Relaxed));
144
145 let ref_ptr_clone = ref_ptr.clone();
146 assert!(!destroyed.load(Ordering::Relaxed));
147
148 drop(ref_ptr_clone);
150 assert!(!destroyed.load(Ordering::Relaxed));
151 } assert!(destroyed.load(Ordering::Relaxed));
154 }
155 }
156
157 #[test]
158 fn test_opaque_ref_counted_allocate_fails() {
159 let val = OpaqueRefCounted(Opaque::uninit());
160 let res = OpaqueRefCounted::<TestCppRefCountedObject>::allocate(val);
161 assert!(res.is_err());
162 }
163
164 pub struct TestCppFacadeBase;
165 unsafe impl Recyclable for TestCppFacadeBase {
166 unsafe fn recycle(ptr: NonNull<Self>) {
167 unsafe {
168 destroy_cpp_ref_counted_object(ptr.as_ptr() as *mut c_void);
169 }
170 }
171 fn allocate(_value: Self) -> Result<NonNull<Self>, ::kalloc::AllocError> {
172 Err(::kalloc::AllocError)
173 }
174 }
175 impl HasRefCount for TestCppFacadeBase {
176 fn ref_count(&self) -> &RefCounted {
177 unsafe { &*(self as *const Self as *const RefCounted) }
178 }
179 }
180
181 #[repr(C)]
182 pub struct TestSubtypeFacade {
183 _facade: OpaqueRefCountedFacade<TestCppFacadeBase>,
184 }
185 impl Deref for TestSubtypeFacade {
186 type Target = TestCppFacadeBase;
187 fn deref(&self) -> &Self::Target {
188 unsafe { &*(self as *const Self as *const TestCppFacadeBase) }
189 }
190 }
191 unsafe impl IsOpaqueRefCounted for TestSubtypeFacade {
192 type TargetBase = TestCppFacadeBase;
193 }
194
195 #[test]
196 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
197 fn test_facade_ref_ptr() {
198 use core::sync::atomic::{AtomicBool, Ordering};
199
200 let destroyed = AtomicBool::new(false);
201 unsafe {
202 let raw_ptr = create_cpp_ref_counted_object(destroyed.as_ptr());
203 assert!(!destroyed.load(Ordering::Relaxed));
204
205 {
206 let ref_ptr = RefPtr::from_raw(raw_ptr as *mut TestSubtypeFacade);
207 assert!(!destroyed.load(Ordering::Relaxed));
208
209 let ref_ptr_clone = ref_ptr.clone();
210 assert!(!destroyed.load(Ordering::Relaxed));
211
212 drop(ref_ptr_clone);
213 assert!(!destroyed.load(Ordering::Relaxed));
214 }
215
216 assert!(destroyed.load(Ordering::Relaxed));
217 }
218 }
219}