1use crate::recyclable::{Recyclable, UninitRecyclable};
8use crate::ref_counted::{HasRefCount, HasRefCountUpgradeable};
9use core::mem::MaybeUninit;
10use core::ops::Deref;
11use core::ptr::NonNull;
12use kalloc::AllocError;
13
14use pin_init::{Init, PinInit};
15
16#[repr(transparent)]
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 unsafe fn cast<U>(self) -> RefPtr<U>
101 where
102 U: HasRefCount + Recyclable,
103 {
104 let ptr = self.ptr.cast::<U>();
105 core::mem::forget(self);
106 RefPtr { ptr }
107 }
108
109 pub fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Self, E>
111 where
112 T: UninitRecyclable,
113 E: From<AllocError>,
114 {
115 let ptr = T::allocate_uninit()?;
116 let guard = UninitRefGuard { ptr };
117 let slot = guard.ptr.as_ptr() as *mut T;
118 unsafe { init.__pinned_init(slot)? };
120 unsafe { (*slot).ref_count().adopt() };
122 let initialized_ptr = guard.ptr.cast::<T>();
123 core::mem::forget(guard);
124 let initialized_ref = RefPtr { ptr: initialized_ptr };
125 Ok(initialized_ref)
126 }
127
128 pub fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
130 where
131 T: UninitRecyclable,
132 E: From<AllocError>,
133 {
134 let ptr = T::allocate_uninit()?;
135 let guard = UninitRefGuard { ptr };
136 let slot = guard.ptr.as_ptr() as *mut T;
137 unsafe { init.__init(slot)? };
139 unsafe { (*slot).ref_count().adopt() };
141 let initialized_ptr = guard.ptr.cast::<T>();
142 core::mem::forget(guard);
143 Ok(RefPtr { ptr: initialized_ptr })
144 }
145
146 #[inline]
148 pub fn pin_init(init: impl PinInit<T, core::convert::Infallible>) -> Result<Self, AllocError>
149 where
150 T: UninitRecyclable,
151 {
152 let init = unsafe {
153 ::pin_init::pin_init_from_closure(|slot| {
154 init.__pinned_init(slot).map_err(|i| match i {})
155 })
156 };
157 Self::try_pin_init(init)
158 }
159
160 #[inline]
162 pub fn init(init: impl Init<T, core::convert::Infallible>) -> Result<Self, AllocError>
163 where
164 T: UninitRecyclable,
165 {
166 let init = unsafe {
167 ::pin_init::init_from_closure(|slot| init.__init(slot).map_err(|i| match i {}))
168 };
169 Self::try_init(init)
170 }
171
172 #[inline]
178 pub fn add_ref(target: &T) {
179 target.ref_count().add_ref();
180 }
181
182 #[inline]
184 pub fn from_ref(target: &T) -> Self {
185 target.ref_count().add_ref();
186 RefPtr { ptr: NonNull::from(target) }
187 }
188}
189
190impl<T: HasRefCount + Recyclable + HasRefCountUpgradeable> RefPtr<T> {
191 pub unsafe fn make_ref_ptr_upgrade_from_raw(ptr: *const T) -> Option<Self> {
215 unsafe {
217 if !ptr.as_ref_unchecked().ref_count().add_ref_maybe_in_destructor() {
218 None
219 } else {
220 Some(Self::from_raw(ptr))
221 }
222 }
223 }
224}
225
226impl<T: HasRefCount + Recyclable> Deref for RefPtr<T> {
227 type Target = T;
228 #[inline]
229 fn deref(&self) -> &Self::Target {
230 unsafe { self.ptr.as_ref() }
231 }
232}
233
234impl<T: HasRefCount + Recyclable> Clone for RefPtr<T> {
235 #[inline]
236 fn clone(&self) -> Self {
237 self.deref().ref_count().add_ref();
238 RefPtr { ptr: self.ptr }
239 }
240}
241
242impl<T: HasRefCount + Recyclable> Drop for RefPtr<T> {
243 #[inline]
244 fn drop(&mut self) {
245 if self.deref().ref_count().release() {
246 unsafe {
247 T::recycle(self.ptr);
248 }
249 }
250 }
251}
252
253impl<T: HasRefCount + Recyclable> PartialEq for RefPtr<T> {
254 #[inline]
255 fn eq(&self, other: &Self) -> bool {
256 RefPtr::ptr_eq(self, other)
257 }
258}
259
260impl<T: HasRefCount + Recyclable> Eq for RefPtr<T> {}
261
262unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Send for RefPtr<T> {}
263unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Sync for RefPtr<T> {}
264
265struct UninitRefGuard<T: UninitRecyclable> {
266 ptr: NonNull<MaybeUninit<T>>,
267}
268
269impl<T: UninitRecyclable> Drop for UninitRefGuard<T> {
270 fn drop(&mut self) {
271 unsafe {
272 T::recycle_uninit(self.ptr);
273 }
274 }
275}
276
277#[macro_export]
279macro_rules! make_ref_counted {
280 ($ty:ident { $($field:ident : $val:expr),* $(,)? }) => {
281 unsafe {
283 $crate::RefPtr::try_new($ty {
284 ref_count: $crate::RefCounted::new(),
285 __fbl_ref_counted_guard: (),
286 $($field : $val),*
287 })
288 }
289 };
290}
291
292#[macro_export]
295macro_rules! pin_make_ref_counted {
296 ($ty:ident { $($field:tt)* }) => {
297 $crate::RefPtr::pin_init($crate::pin_init::pin_init!($ty {
298 ref_count: $crate::RefCounted::new(),
299 __fbl_ref_counted_guard: (),
300 $($field)*
301 }))
302 };
303}
304
305#[macro_export]
308macro_rules! try_pin_make_ref_counted {
309 ($ty:ident { $($field:tt)* }) => {
310 $crate::RefPtr::try_pin_init($crate::pin_init::pin_init!($ty {
311 ref_count: $crate::RefCounted::new(),
312 __fbl_ref_counted_guard: (),
313 $($field)*
314 }))
315 };
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321 use core::ffi::c_void;
322 use core::pin::Pin;
323 use core::ptr::null;
324 use core::sync::atomic::{AtomicBool, Ordering};
325
326 extern crate alloc;
327 extern crate std;
328 use alloc::sync::Arc;
329 use std::sync::{Barrier, Mutex};
330 use std::thread;
331
332 #[unsafe(no_mangle)]
333 pub extern "C" fn rust_recycle_test_rust_ref_counted(ptr: *mut c_void) {
334 unsafe { TestRustRefCounted::recycle_ffi(ptr) }
335 }
336
337 unsafe extern "C" {
338 fn test_import_rust_ref_counted(ptr: *mut c_void);
339 }
340
341 #[fbl::ref_counted]
342 #[pin_init::pin_data(PinnedDrop)]
343 #[derive(crate::Recyclable)]
344 #[repr(C)]
345 pub struct TestRustRefCounted {
346 destroyed: Arc<AtomicBool>,
347 }
348
349 ::zr::static_assert!(core::mem::size_of::<RefPtr<TestRustRefCounted>>() == 8);
350 ::zr::static_assert!(core::mem::align_of::<RefPtr<TestRustRefCounted>>() == 8);
351 ::zr::static_assert!(core::mem::size_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
352 ::zr::static_assert!(core::mem::align_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
353
354 #[pin_init::pinned_drop]
355 impl pin_init::PinnedDrop for TestRustRefCounted {
356 fn drop(self: Pin<&mut Self>) {
357 self.destroyed.store(true, Ordering::Relaxed);
358 }
359 }
360
361 #[test]
362 fn test_rust_drops_reference() {
363 let destroyed = Arc::new(AtomicBool::new(false));
364 {
365 let ref_ptr =
366 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
367 assert!(!destroyed.load(Ordering::Relaxed));
368 let ref_ptr_clone = ref_ptr.clone();
369 drop(ref_ptr_clone);
370 assert!(!destroyed.load(Ordering::Relaxed));
371 } assert!(destroyed.load(Ordering::Relaxed));
374 }
375
376 #[test]
377 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
378 fn test_cpp_drops_reference() {
379 let destroyed = Arc::new(AtomicBool::new(false));
380 let ref_ptr =
381 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
382 let raw_ptr = RefPtr::into_raw(ref_ptr);
383
384 unsafe {
385 assert!(!destroyed.load(Ordering::Relaxed));
386 test_import_rust_ref_counted(raw_ptr as *const TestRustRefCounted as *mut c_void);
388 assert!(destroyed.load(Ordering::Relaxed));
391 }
392 }
393
394 #[test]
395 fn test_ref_ptr_compare() {
396 let destroyed1 = Arc::new(AtomicBool::new(false));
397 let destroyed2 = Arc::new(AtomicBool::new(false));
398 let ptr1 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed1.clone() }).unwrap();
399 let ptr2 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed2.clone() }).unwrap();
400 let ptr1_clone = ptr1.clone();
401
402 assert!(ptr1 == ptr1);
403 assert!(ptr1 != ptr2);
404 assert!(ptr1 == ptr1_clone);
405 }
406
407 #[test]
408 fn test_rust_pin_init() {
409 let destroyed = Arc::new(AtomicBool::new(false));
410 let destroyed_clone = destroyed.clone();
411 {
412 let ref_ptr =
413 pin_make_ref_counted!(TestRustRefCounted { destroyed: destroyed_clone }).unwrap();
414 assert!(!destroyed.load(Ordering::Relaxed));
415 let ref_ptr_clone = ref_ptr.clone();
416 drop(ref_ptr_clone);
417 assert!(!destroyed.load(Ordering::Relaxed));
418 } assert!(destroyed.load(Ordering::Relaxed));
420 }
421
422 #[fbl::ref_counted]
423 #[pin_init::pin_data]
424 #[derive(crate::Recyclable)]
425 #[repr(C)]
426 struct FallibleInit {
427 value: i32,
428 }
429
430 #[test]
431 fn test_rust_try_pin_init_fail() {
432 let init = unsafe {
433 ::pin_init::pin_init_from_closure(
434 |_slot: *mut FallibleInit| -> Result<(), AllocError> { Err(AllocError) },
435 )
436 };
437 let res = RefPtr::try_pin_init(init);
438 assert!(res.is_err());
439 }
440
441 #[test]
442 fn test_null_try_from() {
443 let maybe_ref_ptr = unsafe { RefPtr::try_from_raw(null::<TestRustRefCounted>()) };
444 assert!(maybe_ref_ptr.is_none());
445 }
446
447 #[test]
448 fn test_add_ref() {
449 let destroyed = Arc::new(AtomicBool::new(false));
450 {
451 let ref_ptr =
452 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
453 assert!(!destroyed.load(Ordering::Relaxed));
454 RefPtr::add_ref(&*ref_ptr);
455 let ref_ptr2 = unsafe { RefPtr::from_raw(RefPtr::as_ptr(&ref_ptr)) };
456 drop(ref_ptr);
457 assert!(!destroyed.load(Ordering::Relaxed));
458 drop(ref_ptr2);
459 assert!(destroyed.load(Ordering::Relaxed));
460 }
461 }
462
463 #[test]
464 fn test_from_ref() {
465 let destroyed = Arc::new(AtomicBool::new(false));
466 {
467 let ref_ptr =
468 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
469 assert!(!destroyed.load(Ordering::Relaxed));
470 let ref_ptr2 = RefPtr::from_ref(&*ref_ptr);
471 assert!(ref_ptr == ref_ptr2);
472 drop(ref_ptr);
473 assert!(!destroyed.load(Ordering::Relaxed));
474 drop(ref_ptr2);
475 assert!(destroyed.load(Ordering::Relaxed));
476 }
477 }
478
479 #[fbl::ref_counted]
480 #[derive(crate::Recyclable)]
481 #[repr(C)]
482 struct RawUpgradeTester {
483 mutex: Arc<Mutex<()>>,
484 destroying: Arc<AtomicBool>,
485 destroying_barrier: Option<Arc<Barrier>>,
486 }
487
488 impl HasRefCountUpgradeable for RawUpgradeTester {}
489
490 impl Drop for RawUpgradeTester {
491 fn drop(&mut self) {
492 self.destroying.store(true, Ordering::SeqCst);
493 if let Some(barrier) = &self.destroying_barrier {
494 barrier.wait();
495 }
496 let _guard = self.mutex.lock().unwrap();
497 }
498 }
499
500 #[test]
501 fn test_upgrade_fail() {
502 let mutex = Arc::new(Mutex::new(()));
503 let destroying = Arc::new(AtomicBool::new(false));
504 let destroying_barrier = Arc::new(Barrier::new(2));
505
506 let ref_ptr = make_ref_counted!(RawUpgradeTester {
507 mutex: mutex.clone(),
508 destroying: destroying.clone(),
509 destroying_barrier: Some(destroying_barrier.clone()),
510 })
511 .unwrap();
512 let raw = RefPtr::as_ptr(&ref_ptr);
513
514 let handle = {
515 let _guard = mutex.lock().unwrap();
516 let handle = thread::spawn(move || {
517 drop(ref_ptr);
520 });
521
522 destroying_barrier.wait();
524 assert!(destroying.load(Ordering::SeqCst));
525
526 let upgrade1 = unsafe { RefPtr::make_ref_ptr_upgrade_from_raw(raw) };
529 assert!(upgrade1.is_none());
530
531 let upgrade2 = unsafe { RefPtr::make_ref_ptr_upgrade_from_raw(raw) };
534 assert!(upgrade2.is_none());
535
536 handle
537 };
538
539 handle.join().unwrap();
540 }
541
542 #[test]
543 fn test_upgrade_success() {
544 let mutex = Arc::new(Mutex::new(()));
545 let destroying = Arc::new(AtomicBool::new(false));
546
547 let ref_ptr = make_ref_counted!(RawUpgradeTester {
548 mutex: mutex.clone(),
549 destroying: destroying.clone(),
550 destroying_barrier: None,
551 })
552 .unwrap();
553 let raw = RefPtr::as_ptr(&ref_ptr);
554
555 {
556 let _guard = mutex.lock().unwrap();
557 let upgrade = unsafe { RefPtr::make_ref_ptr_upgrade_from_raw(raw) };
561 assert!(upgrade.is_some());
562 }
563
564 drop(ref_ptr);
565 assert!(destroying.load(Ordering::SeqCst));
566 }
567}