Skip to main content

fbl/
unique_ptr.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use crate::recyclable::Recyclable;
8use core::ops::{Deref, DerefMut};
9use core::ptr::NonNull;
10use kalloc::AllocError;
11
12/// `UniquePtr<T>` holds a unique pointer to an object of type T and deletes the
13/// object when the `UniquePtr` goes out of scope.
14///
15/// T must implement the `Recyclable` trait to define how the object is destroyed.
16/// This allows `UniquePtr` to manage both Rust-allocated objects and C++-allocated
17/// objects.
18#[repr(C)]
19pub struct UniquePtr<T>
20where
21    T: Recyclable,
22{
23    ptr: NonNull<T>,
24}
25
26impl<T: Recyclable> UniquePtr<T> {
27    /// Constructs a `UniquePtr` from a raw pointer.
28    ///
29    /// # Safety
30    ///
31    /// - `ptr` must be valid and the sole owning reference to the object pointed to by `ptr`.
32    /// - `ptr` must have been allocated in such a way that calling `T::recycle(ptr)` is a
33    ///   correct way to deallocate the pointer.
34    pub unsafe fn from_raw(ptr: *mut T) -> Self {
35        // SAFETY: The caller must ensure that ptr is valid.
36        unsafe { UniquePtr { ptr: NonNull::new_unchecked(ptr) } }
37    }
38
39    /// Helper function that allocates a new instance of `T` using `T::allocate` and
40    /// returns a `UniquePtr` wrapping it.
41    ///
42    /// For Rust-allocated objects where `T` derives `Recyclable`, this will use
43    /// `Box::try_new`.
44    pub fn try_new(value: T) -> Result<UniquePtr<T>, AllocError> {
45        let ptr = T::allocate(value)?;
46        Ok(UniquePtr { ptr })
47    }
48
49    /// Returns the raw pointer to the object.
50    pub fn as_ptr(this: &Self) -> *const T {
51        this.ptr.as_ptr()
52    }
53
54    /// Returns a mutable raw pointer to the object.
55    pub fn as_mut_ptr(this: &mut Self) -> *mut T {
56        this.ptr.as_ptr()
57    }
58
59    /// Consume the `UniquePtr` and return the raw pointer without destroying the object.
60    ///
61    /// The caller is responsible for managing the object's lifetime after this call.
62    pub fn into_raw(this: Self) -> *mut T {
63        let ptr = this.ptr.as_ptr();
64        core::mem::forget(this);
65        ptr
66    }
67}
68
69impl<T: Recyclable> Deref for UniquePtr<T> {
70    type Target = T;
71    fn deref(&self) -> &Self::Target {
72        unsafe { self.ptr.as_ref() }
73    }
74}
75
76impl<T: Recyclable> DerefMut for UniquePtr<T> {
77    fn deref_mut(&mut self) -> &mut Self::Target {
78        unsafe { self.ptr.as_mut() }
79    }
80}
81
82impl<T: Recyclable> Drop for UniquePtr<T> {
83    fn drop(&mut self) {
84        unsafe {
85            T::recycle(self.ptr);
86        }
87    }
88}
89
90unsafe impl<T: Recyclable + Send> Send for UniquePtr<T> {}
91unsafe impl<T: Recyclable + Sync> Sync for UniquePtr<T> {}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use core::ffi::c_void;
97    use core::sync::atomic::{AtomicBool, Ordering};
98    use kalloc::Box;
99    use zr::Opaque;
100    extern crate alloc;
101    use alloc::sync::Arc;
102
103    #[derive(fbl::Recyclable)]
104    pub struct TestRustObject {
105        destroyed: Arc<AtomicBool>,
106    }
107
108    impl Drop for TestRustObject {
109        fn drop(&mut self) {
110            self.destroyed.store(true, Ordering::Relaxed);
111        }
112    }
113
114    #[test]
115    fn test_unique_ptr_drops() {
116        let destroyed = Arc::new(AtomicBool::new(false));
117        {
118            let obj = TestRustObject { destroyed: destroyed.clone() };
119            let _unique_ptr = UniquePtr::try_new(obj).unwrap();
120            assert!(!destroyed.load(Ordering::Relaxed));
121        } // unique_ptr drops here
122
123        assert!(destroyed.load(Ordering::Relaxed));
124    }
125
126    #[test]
127    fn test_unique_ptr_into_raw() {
128        let destroyed = Arc::new(AtomicBool::new(false));
129        let raw_ptr;
130        {
131            let obj = TestRustObject { destroyed: destroyed.clone() };
132            let unique_ptr = UniquePtr::try_new(obj).unwrap();
133            assert!(!destroyed.load(Ordering::Relaxed));
134            raw_ptr = UniquePtr::into_raw(unique_ptr);
135        } // unique_ptr drops here but into_raw called, so no destruction
136
137        assert!(!destroyed.load(Ordering::Relaxed));
138
139        // Clean up manually
140        unsafe {
141            drop(Box::from_raw(raw_ptr));
142        }
143        assert!(destroyed.load(Ordering::Relaxed));
144    }
145
146    unsafe extern "C" {
147        fn create_cpp_object(destroyed: *mut bool) -> *mut c_void;
148        fn destroy_cpp_object(ptr: *mut c_void);
149    }
150
151    pub struct TestCppObject;
152
153    unsafe impl Recyclable for Opaque<TestCppObject> {
154        unsafe fn recycle(ptr: NonNull<Self>) {
155            unsafe {
156                destroy_cpp_object(ptr.as_ptr() as *mut c_void);
157            }
158        }
159
160        fn allocate(_value: Self) -> Result<NonNull<Self>, AllocError> {
161            Err(AllocError)
162        }
163    }
164
165    #[test]
166    #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
167    fn test_unique_ptr_cpp_drops() {
168        let destroyed = AtomicBool::new(false);
169        unsafe {
170            let raw_ptr = create_cpp_object(destroyed.as_ptr() as *mut bool);
171            assert!(!destroyed.load(Ordering::Relaxed));
172            {
173                let _unique_ptr = UniquePtr::from_raw(raw_ptr as *mut Opaque<TestCppObject>);
174                assert!(!destroyed.load(Ordering::Relaxed));
175            } // unique_ptr drops here
176
177            assert!(destroyed.load(Ordering::Relaxed));
178        }
179    }
180
181    #[test]
182    fn test_unique_ptr_as_mut_ptr() {
183        let destroyed = Arc::new(AtomicBool::new(false));
184        let mut unique_ptr =
185            UniquePtr::try_new(TestRustObject { destroyed: destroyed.clone() }).unwrap();
186        let mut_ptr = UniquePtr::as_mut_ptr(&mut unique_ptr);
187        assert!(!mut_ptr.is_null());
188    }
189
190    #[test]
191    fn test_unique_ptr_deref_mut() {
192        let destroyed = Arc::new(AtomicBool::new(false));
193        let mut unique_ptr =
194            UniquePtr::try_new(TestRustObject { destroyed: destroyed.clone() }).unwrap();
195        let _deref_mut: &mut TestRustObject = &mut unique_ptr;
196    }
197
198    #[test]
199    fn test_unique_ptr_cpp_alloc_fail() {
200        let dummy_val = Opaque::uninit();
201        let res = Opaque::<TestCppObject>::allocate(dummy_val);
202        assert!(res.is_err());
203    }
204}