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