1use crate::recyclable::{Recyclable, UninitRecyclable};
8use crate::ref_counted::HasRefCount;
9use core::mem::MaybeUninit;
10use core::ops::Deref;
11use core::ptr::NonNull;
12use kalloc::AllocError;
13
14use pin_init::{Init, PinInit};
15
16#[repr(C)]
22pub struct RefPtr<T>
23where
24 T: HasRefCount + Recyclable,
25{
26 ptr: NonNull<T>,
27}
28
29impl<T: HasRefCount + Recyclable> RefPtr<T> {
30 pub unsafe fn from_raw(ptr: *const T) -> Self {
39 unsafe { RefPtr { ptr: NonNull::new_unchecked(ptr as *mut T) } }
41 }
42
43 pub unsafe fn try_from_raw(ptr: *const T) -> Option<Self> {
53 NonNull::new(ptr as *mut T).map(|ptr| RefPtr { ptr })
54 }
55
56 pub unsafe fn try_new(value: T) -> Result<RefPtr<T>, AllocError> {
68 let mut ptr = T::allocate(value)?;
69 unsafe { ptr.as_mut().ref_count().adopt() };
72 Ok(RefPtr { ptr })
73 }
74
75 pub fn as_ptr(this: &Self) -> *const T {
77 this.ptr.as_ptr()
78 }
79
80 pub fn ptr_eq(a: &Self, b: &Self) -> bool {
82 a.ptr == b.ptr
83 }
84
85 pub fn into_raw(this: Self) -> *const T {
89 let ptr = this.ptr;
90 core::mem::forget(this);
91 ptr.as_ptr()
92 }
93
94 pub fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Self, E>
96 where
97 T: UninitRecyclable,
98 E: From<AllocError>,
99 {
100 let ptr = T::allocate_uninit()?;
101 let guard = UninitRefGuard { ptr };
102 let slot = guard.ptr.as_ptr() as *mut T;
103 unsafe { init.__pinned_init(slot)? };
105 unsafe { (*slot).ref_count().adopt() };
107 let initialized_ptr = guard.ptr.cast::<T>();
108 core::mem::forget(guard);
109 let initialized_ref = RefPtr { ptr: initialized_ptr };
110 Ok(initialized_ref)
111 }
112
113 pub fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
115 where
116 T: UninitRecyclable,
117 E: From<AllocError>,
118 {
119 let ptr = T::allocate_uninit()?;
120 let guard = UninitRefGuard { ptr };
121 let slot = guard.ptr.as_ptr() as *mut T;
122 unsafe { init.__init(slot)? };
124 unsafe { (*slot).ref_count().adopt() };
126 let initialized_ptr = guard.ptr.cast::<T>();
127 core::mem::forget(guard);
128 Ok(RefPtr { ptr: initialized_ptr })
129 }
130
131 #[inline]
133 pub fn pin_init(init: impl PinInit<T, core::convert::Infallible>) -> Result<Self, AllocError>
134 where
135 T: UninitRecyclable,
136 {
137 let init = unsafe {
138 ::pin_init::pin_init_from_closure(|slot| {
139 init.__pinned_init(slot).map_err(|i| match i {})
140 })
141 };
142 Self::try_pin_init(init)
143 }
144
145 #[inline]
147 pub fn init(init: impl Init<T, core::convert::Infallible>) -> Result<Self, AllocError>
148 where
149 T: UninitRecyclable,
150 {
151 let init = unsafe {
152 ::pin_init::init_from_closure(|slot| init.__init(slot).map_err(|i| match i {}))
153 };
154 Self::try_init(init)
155 }
156}
157
158impl<T: HasRefCount + Recyclable> Deref for RefPtr<T> {
159 type Target = T;
160 fn deref(&self) -> &Self::Target {
161 unsafe { self.ptr.as_ref() }
162 }
163}
164
165impl<T: HasRefCount + Recyclable> Clone for RefPtr<T> {
166 fn clone(&self) -> Self {
167 self.deref().ref_count().add_ref();
168 RefPtr { ptr: self.ptr }
169 }
170}
171
172impl<T: HasRefCount + Recyclable> Drop for RefPtr<T> {
173 fn drop(&mut self) {
174 if self.deref().ref_count().release() {
175 unsafe {
176 T::recycle(self.ptr);
177 }
178 }
179 }
180}
181
182impl<T: HasRefCount + Recyclable> PartialEq for RefPtr<T> {
183 fn eq(&self, other: &Self) -> bool {
184 RefPtr::ptr_eq(self, other)
185 }
186}
187
188impl<T: HasRefCount + Recyclable> Eq for RefPtr<T> {}
189
190unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Send for RefPtr<T> {}
191unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Sync for RefPtr<T> {}
192
193struct UninitRefGuard<T: UninitRecyclable> {
194 ptr: NonNull<MaybeUninit<T>>,
195}
196
197impl<T: UninitRecyclable> Drop for UninitRefGuard<T> {
198 fn drop(&mut self) {
199 unsafe {
200 T::recycle_uninit(self.ptr);
201 }
202 }
203}
204
205#[macro_export]
207macro_rules! make_ref_counted {
208 ($ty:ident { $($field:ident : $val:expr),* $(,)? }) => {
209 unsafe {
211 $crate::RefPtr::try_new($ty {
212 ref_count: $crate::RefCounted::new(),
213 __fbl_ref_counted_guard: (),
214 $($field : $val),*
215 })
216 }
217 };
218}
219
220#[macro_export]
222macro_rules! pin_make_ref_counted {
223 ($ty:ident { $($field:tt)* }) => {
224 $crate::RefPtr::pin_init($crate::pin_init::pin_init!($ty {
225 ref_count: $crate::RefCounted::new(),
226 __fbl_ref_counted_guard: (),
227 $($field)*
228 }))
229 };
230}
231
232#[macro_export]
234macro_rules! try_pin_make_ref_counted {
235 ($ty:ident { $($field:tt)* }) => {
236 $crate::RefPtr::try_pin_init($crate::pin_init::pin_init!($ty {
237 ref_count: $crate::RefCounted::new(),
238 __fbl_ref_counted_guard: (),
239 $($field)*
240 }))
241 };
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use core::ffi::c_void;
248 use core::pin::Pin;
249 use core::ptr::null;
250 use core::sync::atomic::{AtomicBool, Ordering};
251
252 extern crate alloc;
253 use alloc::sync::Arc;
254
255 #[unsafe(no_mangle)]
256 pub extern "C" fn rust_recycle_test_rust_ref_counted(ptr: *mut c_void) {
257 unsafe { TestRustRefCounted::recycle_ffi(ptr) }
258 }
259
260 unsafe extern "C" {
261 fn test_import_rust_ref_counted(ptr: *mut c_void);
262 }
263
264 #[fbl::ref_counted]
265 #[pin_init::pin_data(PinnedDrop)]
266 #[derive(crate::Recyclable)]
267 #[repr(C)]
268 pub struct TestRustRefCounted {
269 destroyed: Arc<AtomicBool>,
270 }
271
272 ::zr::static_assert!(core::mem::size_of::<RefPtr<TestRustRefCounted>>() == 8);
273 ::zr::static_assert!(core::mem::align_of::<RefPtr<TestRustRefCounted>>() == 8);
274 ::zr::static_assert!(core::mem::size_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
275 ::zr::static_assert!(core::mem::align_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
276
277 #[pin_init::pinned_drop]
278 impl pin_init::PinnedDrop for TestRustRefCounted {
279 fn drop(self: Pin<&mut Self>) {
280 self.destroyed.store(true, Ordering::Relaxed);
281 }
282 }
283
284 #[test]
285 fn test_rust_drops_reference() {
286 let destroyed = Arc::new(AtomicBool::new(false));
287 {
288 let ref_ptr =
289 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
290 assert!(!destroyed.load(Ordering::Relaxed));
291 let ref_ptr_clone = ref_ptr.clone();
292 drop(ref_ptr_clone);
293 assert!(!destroyed.load(Ordering::Relaxed));
294 } assert!(destroyed.load(Ordering::Relaxed));
297 }
298
299 #[test]
300 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
301 fn test_cpp_drops_reference() {
302 let destroyed = Arc::new(AtomicBool::new(false));
303 let ref_ptr =
304 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
305 let raw_ptr = RefPtr::into_raw(ref_ptr);
306
307 unsafe {
308 assert!(!destroyed.load(Ordering::Relaxed));
309 test_import_rust_ref_counted(raw_ptr as *const TestRustRefCounted as *mut c_void);
311 assert!(destroyed.load(Ordering::Relaxed));
314 }
315 }
316
317 #[test]
318 fn test_ref_ptr_compare() {
319 let destroyed1 = Arc::new(AtomicBool::new(false));
320 let destroyed2 = Arc::new(AtomicBool::new(false));
321 let ptr1 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed1.clone() }).unwrap();
322 let ptr2 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed2.clone() }).unwrap();
323 let ptr1_clone = ptr1.clone();
324
325 assert!(ptr1 == ptr1);
326 assert!(ptr1 != ptr2);
327 assert!(ptr1 == ptr1_clone);
328 }
329
330 #[test]
331 fn test_rust_pin_init() {
332 let destroyed = Arc::new(AtomicBool::new(false));
333 let destroyed_clone = destroyed.clone();
334 {
335 let ref_ptr =
336 pin_make_ref_counted!(TestRustRefCounted { destroyed: destroyed_clone }).unwrap();
337 assert!(!destroyed.load(Ordering::Relaxed));
338 let ref_ptr_clone = ref_ptr.clone();
339 drop(ref_ptr_clone);
340 assert!(!destroyed.load(Ordering::Relaxed));
341 } assert!(destroyed.load(Ordering::Relaxed));
343 }
344
345 #[fbl::ref_counted]
346 #[pin_init::pin_data]
347 #[derive(crate::Recyclable)]
348 #[repr(C)]
349 struct FallibleInit {
350 value: i32,
351 }
352
353 #[test]
354 fn test_rust_try_pin_init_fail() {
355 let init = unsafe {
356 ::pin_init::pin_init_from_closure(
357 |_slot: *mut FallibleInit| -> Result<(), AllocError> { Err(AllocError) },
358 )
359 };
360 let res = RefPtr::try_pin_init(init);
361 assert!(res.is_err());
362 }
363
364 #[test]
365 fn test_null_try_from() {
366 let maybe_ref_ptr = unsafe { RefPtr::try_from_raw(null::<TestRustRefCounted>()) };
367 assert!(maybe_ref_ptr.is_none());
368 }
369}