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 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 pub fn add_ref(target: &T) {
178 target.ref_count().add_ref();
179 }
180
181 pub fn from_ref(target: &T) -> Self {
183 target.ref_count().add_ref();
184 RefPtr { ptr: NonNull::from(target) }
185 }
186}
187
188impl<T: HasRefCount + Recyclable> Deref for RefPtr<T> {
189 type Target = T;
190 fn deref(&self) -> &Self::Target {
191 unsafe { self.ptr.as_ref() }
192 }
193}
194
195impl<T: HasRefCount + Recyclable> Clone for RefPtr<T> {
196 fn clone(&self) -> Self {
197 self.deref().ref_count().add_ref();
198 RefPtr { ptr: self.ptr }
199 }
200}
201
202impl<T: HasRefCount + Recyclable> Drop for RefPtr<T> {
203 fn drop(&mut self) {
204 if self.deref().ref_count().release() {
205 unsafe {
206 T::recycle(self.ptr);
207 }
208 }
209 }
210}
211
212impl<T: HasRefCount + Recyclable> PartialEq for RefPtr<T> {
213 fn eq(&self, other: &Self) -> bool {
214 RefPtr::ptr_eq(self, other)
215 }
216}
217
218impl<T: HasRefCount + Recyclable> Eq for RefPtr<T> {}
219
220unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Send for RefPtr<T> {}
221unsafe impl<T: HasRefCount + Recyclable + Send + Sync> Sync for RefPtr<T> {}
222
223struct UninitRefGuard<T: UninitRecyclable> {
224 ptr: NonNull<MaybeUninit<T>>,
225}
226
227impl<T: UninitRecyclable> Drop for UninitRefGuard<T> {
228 fn drop(&mut self) {
229 unsafe {
230 T::recycle_uninit(self.ptr);
231 }
232 }
233}
234
235#[macro_export]
237macro_rules! make_ref_counted {
238 ($ty:ident { $($field:ident : $val:expr),* $(,)? }) => {
239 unsafe {
241 $crate::RefPtr::try_new($ty {
242 ref_count: $crate::RefCounted::new(),
243 __fbl_ref_counted_guard: (),
244 $($field : $val),*
245 })
246 }
247 };
248}
249
250#[macro_export]
253macro_rules! pin_make_ref_counted {
254 ($ty:ident { $($field:tt)* }) => {
255 $crate::RefPtr::pin_init($crate::pin_init::pin_init!($ty {
256 ref_count: $crate::RefCounted::new(),
257 __fbl_ref_counted_guard: (),
258 $($field)*
259 }))
260 };
261}
262
263#[macro_export]
266macro_rules! try_pin_make_ref_counted {
267 ($ty:ident { $($field:tt)* }) => {
268 $crate::RefPtr::try_pin_init($crate::pin_init::pin_init!($ty {
269 ref_count: $crate::RefCounted::new(),
270 __fbl_ref_counted_guard: (),
271 $($field)*
272 }))
273 };
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279 use core::ffi::c_void;
280 use core::pin::Pin;
281 use core::ptr::null;
282 use core::sync::atomic::{AtomicBool, Ordering};
283
284 extern crate alloc;
285 use alloc::sync::Arc;
286
287 #[unsafe(no_mangle)]
288 pub extern "C" fn rust_recycle_test_rust_ref_counted(ptr: *mut c_void) {
289 unsafe { TestRustRefCounted::recycle_ffi(ptr) }
290 }
291
292 unsafe extern "C" {
293 fn test_import_rust_ref_counted(ptr: *mut c_void);
294 }
295
296 #[fbl::ref_counted]
297 #[pin_init::pin_data(PinnedDrop)]
298 #[derive(crate::Recyclable)]
299 #[repr(C)]
300 pub struct TestRustRefCounted {
301 destroyed: Arc<AtomicBool>,
302 }
303
304 ::zr::static_assert!(core::mem::size_of::<RefPtr<TestRustRefCounted>>() == 8);
305 ::zr::static_assert!(core::mem::align_of::<RefPtr<TestRustRefCounted>>() == 8);
306 ::zr::static_assert!(core::mem::size_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
307 ::zr::static_assert!(core::mem::align_of::<Option<RefPtr<TestRustRefCounted>>>() == 8);
308
309 #[pin_init::pinned_drop]
310 impl pin_init::PinnedDrop for TestRustRefCounted {
311 fn drop(self: Pin<&mut Self>) {
312 self.destroyed.store(true, Ordering::Relaxed);
313 }
314 }
315
316 #[test]
317 fn test_rust_drops_reference() {
318 let destroyed = Arc::new(AtomicBool::new(false));
319 {
320 let ref_ptr =
321 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
322 assert!(!destroyed.load(Ordering::Relaxed));
323 let ref_ptr_clone = ref_ptr.clone();
324 drop(ref_ptr_clone);
325 assert!(!destroyed.load(Ordering::Relaxed));
326 } assert!(destroyed.load(Ordering::Relaxed));
329 }
330
331 #[test]
332 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
333 fn test_cpp_drops_reference() {
334 let destroyed = Arc::new(AtomicBool::new(false));
335 let ref_ptr =
336 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
337 let raw_ptr = RefPtr::into_raw(ref_ptr);
338
339 unsafe {
340 assert!(!destroyed.load(Ordering::Relaxed));
341 test_import_rust_ref_counted(raw_ptr as *const TestRustRefCounted as *mut c_void);
343 assert!(destroyed.load(Ordering::Relaxed));
346 }
347 }
348
349 #[test]
350 fn test_ref_ptr_compare() {
351 let destroyed1 = Arc::new(AtomicBool::new(false));
352 let destroyed2 = Arc::new(AtomicBool::new(false));
353 let ptr1 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed1.clone() }).unwrap();
354 let ptr2 = make_ref_counted!(TestRustRefCounted { destroyed: destroyed2.clone() }).unwrap();
355 let ptr1_clone = ptr1.clone();
356
357 assert!(ptr1 == ptr1);
358 assert!(ptr1 != ptr2);
359 assert!(ptr1 == ptr1_clone);
360 }
361
362 #[test]
363 fn test_rust_pin_init() {
364 let destroyed = Arc::new(AtomicBool::new(false));
365 let destroyed_clone = destroyed.clone();
366 {
367 let ref_ptr =
368 pin_make_ref_counted!(TestRustRefCounted { destroyed: destroyed_clone }).unwrap();
369 assert!(!destroyed.load(Ordering::Relaxed));
370 let ref_ptr_clone = ref_ptr.clone();
371 drop(ref_ptr_clone);
372 assert!(!destroyed.load(Ordering::Relaxed));
373 } assert!(destroyed.load(Ordering::Relaxed));
375 }
376
377 #[fbl::ref_counted]
378 #[pin_init::pin_data]
379 #[derive(crate::Recyclable)]
380 #[repr(C)]
381 struct FallibleInit {
382 value: i32,
383 }
384
385 #[test]
386 fn test_rust_try_pin_init_fail() {
387 let init = unsafe {
388 ::pin_init::pin_init_from_closure(
389 |_slot: *mut FallibleInit| -> Result<(), AllocError> { Err(AllocError) },
390 )
391 };
392 let res = RefPtr::try_pin_init(init);
393 assert!(res.is_err());
394 }
395
396 #[test]
397 fn test_null_try_from() {
398 let maybe_ref_ptr = unsafe { RefPtr::try_from_raw(null::<TestRustRefCounted>()) };
399 assert!(maybe_ref_ptr.is_none());
400 }
401
402 #[test]
403 fn test_add_ref() {
404 let destroyed = Arc::new(AtomicBool::new(false));
405 {
406 let ref_ptr =
407 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
408 assert!(!destroyed.load(Ordering::Relaxed));
409 RefPtr::add_ref(&*ref_ptr);
410 let ref_ptr2 = unsafe { RefPtr::from_raw(RefPtr::as_ptr(&ref_ptr)) };
411 drop(ref_ptr);
412 assert!(!destroyed.load(Ordering::Relaxed));
413 drop(ref_ptr2);
414 assert!(destroyed.load(Ordering::Relaxed));
415 }
416 }
417
418 #[test]
419 fn test_from_ref() {
420 let destroyed = Arc::new(AtomicBool::new(false));
421 {
422 let ref_ptr =
423 make_ref_counted!(TestRustRefCounted { destroyed: destroyed.clone() }).unwrap();
424 assert!(!destroyed.load(Ordering::Relaxed));
425 let ref_ptr2 = RefPtr::from_ref(&*ref_ptr);
426 assert!(ref_ptr == ref_ptr2);
427 drop(ref_ptr);
428 assert!(!destroyed.load(Ordering::Relaxed));
429 drop(ref_ptr2);
430 assert!(destroyed.load(Ordering::Relaxed));
431 }
432 }
433}