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 adopt_raw(ptr: *mut T) -> Self {
52 unsafe {
54 (*ptr).ref_count().adopt();
55 Self::from_raw(ptr)
56 }
57 }
58
59 pub unsafe fn try_from_raw(ptr: *const T) -> Option<Self> {
69 NonNull::new(ptr as *mut T).map(|ptr| RefPtr { ptr })
70 }
71
72 pub unsafe fn try_new(value: T) -> Result<RefPtr<T>, AllocError> {
84 let mut ptr = T::allocate(value)?;
85 unsafe { ptr.as_mut().ref_count().adopt() };
88 Ok(RefPtr { ptr })
89 }
90
91 pub fn as_ptr(this: &Self) -> *const T {
93 this.ptr.as_ptr()
94 }
95
96 pub fn ptr_eq(a: &Self, b: &Self) -> bool {
98 a.ptr == b.ptr
99 }
100
101 pub fn into_raw(this: Self) -> *const T {
105 let ptr = this.ptr;
106 core::mem::forget(this);
107 ptr.as_ptr()
108 }
109
110 pub unsafe fn cast<U>(self) -> RefPtr<U>
117 where
118 U: HasRefCount + Recyclable,
119 {
120 let ptr = self.ptr.cast::<U>();
121 core::mem::forget(self);
122 RefPtr { ptr }
123 }
124
125 #[inline]
127 pub fn try_pin_init<E>(init: impl PinInit<T, E>) -> Result<Self, E>
128 where
129 T: UninitRecyclable,
130 E: From<AllocError>,
131 {
132 let ptr = T::allocate_uninit()?;
133 let guard = UninitRefGuard { ptr };
134 let slot = guard.ptr.as_ptr() as *mut T;
135 unsafe { init.__pinned_init(slot)? };
137 unsafe { (*slot).ref_count().adopt() };
139 let initialized_ptr = guard.ptr.cast::<T>();
140 core::mem::forget(guard);
141 let initialized_ref = RefPtr { ptr: initialized_ptr };
142 Ok(initialized_ref)
143 }
144
145 #[inline]
147 pub fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
148 where
149 T: UninitRecyclable,
150 E: From<AllocError>,
151 {
152 let ptr = T::allocate_uninit()?;
153 let guard = UninitRefGuard { ptr };
154 let slot = guard.ptr.as_ptr() as *mut T;
155 unsafe { init.__init(slot)? };
157 unsafe { (*slot).ref_count().adopt() };
159 let initialized_ptr = guard.ptr.cast::<T>();
160 core::mem::forget(guard);
161 Ok(RefPtr { ptr: initialized_ptr })
162 }
163
164 #[inline]
166 pub fn pin_init(init: impl PinInit<T, core::convert::Infallible>) -> Result<Self, AllocError>
167 where
168 T: UninitRecyclable,
169 {
170 let init = unsafe {
171 ::pin_init::pin_init_from_closure(|slot| {
172 init.__pinned_init(slot).map_err(|i| match i {})
173 })
174 };
175 Self::try_pin_init(init)
176 }
177
178 #[inline]
180 pub fn init(init: impl Init<T, core::convert::Infallible>) -> Result<Self, AllocError>
181 where
182 T: UninitRecyclable,
183 {
184 let init = unsafe {
185 ::pin_init::init_from_closure(|slot| init.__init(slot).map_err(|i| match i {}))
186 };
187 Self::try_init(init)
188 }
189
190 #[inline]
196 pub fn add_ref(target: &T) {
197 target.ref_count().add_ref();
198 }
199
200 #[inline]
202 pub fn from_ref(target: &T) -> Self {
203 target.ref_count().add_ref();
204 RefPtr { ptr: NonNull::from(target) }
205 }
206}
207
208impl<T: HasRefCount + Recyclable + HasRefCountUpgradeable> RefPtr<T> {
209 pub unsafe fn make_ref_ptr_upgrade_from_raw(ptr: *const T) -> Option<Self> {
233 unsafe {
235 if !ptr.as_ref_unchecked().ref_count().add_ref_maybe_in_destructor() {
236 None
237 } else {
238 Some(Self::from_raw(ptr))
239 }
240 }
241 }
242}
243
244impl<T: HasRefCount + Recyclable> Deref for RefPtr<T> {
245 type Target = T;
246 #[inline]
247 fn deref(&self) -> &Self::Target {
248 unsafe { self.ptr.as_ref() }
249 }
250}
251
252impl<T: HasRefCount + Recyclable> Clone for RefPtr<T> {
253 #[inline]
254 fn clone(&self) -> Self {
255 self.deref().ref_count().add_ref();
256 RefPtr { ptr: self.ptr }
257 }
258}
259
260impl<T: HasRefCount + Recyclable> Drop for RefPtr<T> {
261 #[inline]
262 fn drop(&mut self) {
263 if self.deref().ref_count().release() {
264 unsafe {
265 T::recycle(self.ptr);
266 }
267 }
268 }
269}
270
271impl<T: HasRefCount + Recyclable> PartialEq for RefPtr<T> {
272 #[inline]
273 fn eq(&self, other: &Self) -> bool {
274 RefPtr::ptr_eq(self, other)
275 }
276}
277
278impl<T: HasRefCount + Recyclable> Eq for RefPtr<T> {}
279
280unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Send for RefPtr<T> {}
281unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Sync for RefPtr<T> {}
282
283struct UninitRefGuard<T: UninitRecyclable> {
284 ptr: NonNull<MaybeUninit<T>>,
285}
286
287impl<T: UninitRecyclable> Drop for UninitRefGuard<T> {
288 fn drop(&mut self) {
289 unsafe {
290 T::recycle_uninit(self.ptr);
291 }
292 }
293}
294
295#[macro_export]
297macro_rules! make_ref_counted {
298 ($ty:ident { $($field:ident : $val:expr),* $(,)? }) => {
299 unsafe {
301 $crate::RefPtr::try_new($ty {
302 ref_count: $crate::RefCounted::new(),
303 __fbl_ref_counted_guard: (),
304 $($field : $val),*
305 })
306 }
307 };
308}
309
310#[macro_export]
313macro_rules! pin_make_ref_counted {
314 ($ty:ident { $($field:tt)* }) => {
315 $crate::RefPtr::pin_init($crate::pin_init::pin_init!($ty {
316 ref_count: $crate::RefCounted::new(),
317 __fbl_ref_counted_guard: (),
318 $($field)*
319 }))
320 };
321}
322
323#[macro_export]
326macro_rules! try_pin_make_ref_counted {
327 ($ty:ident { $($field:tt)* }) => {
328 $crate::RefPtr::try_pin_init($crate::pin_init::pin_init!($ty {
329 ref_count: $crate::RefCounted::new(),
330 __fbl_ref_counted_guard: (),
331 $($field)*
332 }))
333 };
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 use core::ffi::c_void;
340 use core::pin::Pin;
341 use core::ptr::null;
342 use core::sync::atomic::{AtomicBool, Ordering};
343
344 extern crate alloc;
345 extern crate std;
346 use alloc::sync::Arc;
347 use std::sync::{Barrier, Mutex};
348 use std::thread;
349
350 #[unsafe(no_mangle)]
351 pub extern "C" fn rust_recycle_test_rust_ref_counted(ptr: *mut c_void) {
352 unsafe { TestRustRefCounted::recycle_ffi(ptr) }
353 }
354
355 unsafe extern "C" {
356 fn test_import_rust_ref_counted(ptr: *mut c_void);
357 }
358
359 #[fbl::ref_counted]
360 #[pin_init::pin_data(PinnedDrop)]
361 #[derive(crate::Recyclable)]
362 #[repr(C)]
363 pub struct TestRustRefCounted {
364 destroyed: Arc<AtomicBool>,
365 }
366
367 ::zr::static_assert!(core::mem::size_of::<RefPtr<TestRustRefCounted>>() == 8);
368 ::zr::static_assert!(core::mem::align_of::<RefPtr<TestRustRefCounted>>() == 8);
369 ::zr::static_assert!(core::mem::size_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
370 ::zr::static_assert!(core::mem::align_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
371
372 #[pin_init::pinned_drop]
373 impl pin_init::PinnedDrop for TestRustRefCounted {
374 fn drop(self: Pin<&mut Self>) {
375 self.destroyed.store(true, Ordering::Relaxed);
376 }
377 }
378
379 #[test]
380 fn test_rust_drops_reference() {
381 let destroyed = Arc::new(AtomicBool::new(false));
382 {
383 let ref_ptr =
384 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
385 assert!(!destroyed.load(Ordering::Relaxed));
386 let ref_ptr_clone = ref_ptr.clone();
387 drop(ref_ptr_clone);
388 assert!(!destroyed.load(Ordering::Relaxed));
389 } assert!(destroyed.load(Ordering::Relaxed));
392 }
393
394 #[test]
395 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
396 fn test_cpp_drops_reference() {
397 let destroyed = Arc::new(AtomicBool::new(false));
398 let ref_ptr =
399 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
400 let raw_ptr = RefPtr::into_raw(ref_ptr);
401
402 unsafe {
403 assert!(!destroyed.load(Ordering::Relaxed));
404 test_import_rust_ref_counted(raw_ptr as *const TestRustRefCounted as *mut c_void);
406 assert!(destroyed.load(Ordering::Relaxed));
409 }
410 }
411
412 #[test]
413 fn test_ref_ptr_compare() {
414 let destroyed1 = Arc::new(AtomicBool::new(false));
415 let destroyed2 = Arc::new(AtomicBool::new(false));
416 let ptr1 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed1.clone() }).unwrap();
417 let ptr2 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed2.clone() }).unwrap();
418 let ptr1_clone = ptr1.clone();
419
420 assert!(ptr1 == ptr1);
421 assert!(ptr1 != ptr2);
422 assert!(ptr1 == ptr1_clone);
423 }
424
425 #[test]
426 fn test_rust_pin_init() {
427 let destroyed = Arc::new(AtomicBool::new(false));
428 let destroyed_clone = destroyed.clone();
429 {
430 let ref_ptr =
431 pin_make_ref_counted!(TestRustRefCounted { destroyed: destroyed_clone }).unwrap();
432 assert!(!destroyed.load(Ordering::Relaxed));
433 let ref_ptr_clone = ref_ptr.clone();
434 drop(ref_ptr_clone);
435 assert!(!destroyed.load(Ordering::Relaxed));
436 } assert!(destroyed.load(Ordering::Relaxed));
438 }
439
440 #[fbl::ref_counted]
441 #[pin_init::pin_data]
442 #[derive(crate::Recyclable)]
443 #[repr(C)]
444 struct FallibleInit {
445 value: i32,
446 }
447
448 #[test]
449 fn test_rust_try_pin_init_fail() {
450 let init = unsafe {
451 ::pin_init::pin_init_from_closure(
452 |_slot: *mut FallibleInit| -> Result<(), AllocError> { Err(AllocError) },
453 )
454 };
455 let res = RefPtr::try_pin_init(init);
456 assert!(res.is_err());
457 }
458
459 #[test]
460 fn test_null_try_from() {
461 let maybe_ref_ptr = unsafe { RefPtr::try_from_raw(null::<TestRustRefCounted>()) };
462 assert!(maybe_ref_ptr.is_none());
463 }
464
465 #[test]
466 fn test_add_ref() {
467 let destroyed = Arc::new(AtomicBool::new(false));
468 {
469 let ref_ptr =
470 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
471 assert!(!destroyed.load(Ordering::Relaxed));
472 RefPtr::add_ref(&*ref_ptr);
473 let ref_ptr2 = unsafe { RefPtr::from_raw(RefPtr::as_ptr(&ref_ptr)) };
474 drop(ref_ptr);
475 assert!(!destroyed.load(Ordering::Relaxed));
476 drop(ref_ptr2);
477 assert!(destroyed.load(Ordering::Relaxed));
478 }
479 }
480
481 #[test]
482 fn test_from_ref() {
483 let destroyed = Arc::new(AtomicBool::new(false));
484 {
485 let ref_ptr =
486 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
487 assert!(!destroyed.load(Ordering::Relaxed));
488 let ref_ptr2 = RefPtr::from_ref(&*ref_ptr);
489 assert!(ref_ptr == ref_ptr2);
490 drop(ref_ptr);
491 assert!(!destroyed.load(Ordering::Relaxed));
492 drop(ref_ptr2);
493 assert!(destroyed.load(Ordering::Relaxed));
494 }
495 }
496
497 #[test]
498 fn test_adopt_raw() {
499 let destroyed = Arc::new(AtomicBool::new(false));
500 let ptr = TestRustRefCounted::allocate(TestRustRefCounted {
501 ref_count: crate::RefCounted::new(),
502 __fbl_ref_counted_guard: (),
503 destroyed: destroyed.clone(),
504 })
505 .unwrap();
506
507 {
508 let ref_ptr = unsafe { RefPtr::adopt_raw(ptr.as_ptr()) };
511 assert_eq!(ref_ptr.ref_count().ref_count_debug(), 1);
512 assert!(!destroyed.load(Ordering::Relaxed));
513 }
514
515 assert!(destroyed.load(Ordering::Relaxed));
516 }
517
518 #[fbl::ref_counted]
519 #[derive(crate::Recyclable)]
520 #[repr(C)]
521 struct RawUpgradeTester {
522 mutex: Arc<Mutex<()>>,
523 destroying: Arc<AtomicBool>,
524 destroying_barrier: Option<Arc<Barrier>>,
525 }
526
527 impl HasRefCountUpgradeable for RawUpgradeTester {}
528
529 impl Drop for RawUpgradeTester {
530 fn drop(&mut self) {
531 self.destroying.store(true, Ordering::SeqCst);
532 if let Some(barrier) = &self.destroying_barrier {
533 barrier.wait();
534 }
535 let _guard = self.mutex.lock().unwrap();
536 }
537 }
538
539 #[test]
540 fn test_upgrade_fail() {
541 let mutex = Arc::new(Mutex::new(()));
542 let destroying = Arc::new(AtomicBool::new(false));
543 let destroying_barrier = Arc::new(Barrier::new(2));
544
545 let ref_ptr = make_ref_counted!(RawUpgradeTester {
546 mutex: mutex.clone(),
547 destroying: destroying.clone(),
548 destroying_barrier: Some(destroying_barrier.clone()),
549 })
550 .unwrap();
551 let raw = RefPtr::as_ptr(&ref_ptr);
552
553 let handle = {
554 let _guard = mutex.lock().unwrap();
555 let handle = thread::spawn(move || {
556 drop(ref_ptr);
559 });
560
561 destroying_barrier.wait();
563 assert!(destroying.load(Ordering::SeqCst));
564
565 let upgrade1 = unsafe { RefPtr::make_ref_ptr_upgrade_from_raw(raw) };
568 assert!(upgrade1.is_none());
569
570 let upgrade2 = unsafe { RefPtr::make_ref_ptr_upgrade_from_raw(raw) };
573 assert!(upgrade2.is_none());
574
575 handle
576 };
577
578 handle.join().unwrap();
579 }
580
581 #[test]
582 fn test_upgrade_success() {
583 let mutex = Arc::new(Mutex::new(()));
584 let destroying = Arc::new(AtomicBool::new(false));
585
586 let ref_ptr = make_ref_counted!(RawUpgradeTester {
587 mutex: mutex.clone(),
588 destroying: destroying.clone(),
589 destroying_barrier: None,
590 })
591 .unwrap();
592 let raw = RefPtr::as_ptr(&ref_ptr);
593
594 {
595 let _guard = mutex.lock().unwrap();
596 let upgrade = unsafe { RefPtr::make_ref_ptr_upgrade_from_raw(raw) };
600 assert!(upgrade.is_some());
601 }
602
603 drop(ref_ptr);
604 assert!(destroying.load(Ordering::SeqCst));
605 }
606}