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/// Declares a zero-sized facade struct for an opaque `ktl::unique_ptr`-owned C++ object and
94/// implements `Recyclable` for it so it can be held by `fbl::UniquePtr`.
95#[macro_export]
96macro_rules! impl_opaque_unique_facade {
97    (
98        $(#[$meta:meta])*
99        $vis:vis struct $name:ident,
100        $destroy_fn:path $(,)?
101    ) => {
102        $(#[$meta])*
103        #[repr(C)]
104        $vis struct $name {
105            _facade: $crate::OpaqueFacade,
106        }
107
108        // SAFETY: `$name` represents a C++ heap-allocated object destroyed by `$destroy_fn`.
109        unsafe impl $crate::Recyclable for $name {
110            unsafe fn recycle(ptr: core::ptr::NonNull<Self>) {
111                // SAFETY: `ptr` is the unique owning pointer being dropped by `UniquePtr`.
112                unsafe {
113                    $destroy_fn(ptr.as_ptr() as *mut Self);
114                }
115            }
116        }
117    };
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use core::ffi::c_void;
124    use core::sync::atomic::{AtomicBool, Ordering};
125    use kalloc::Box;
126    use zr::Opaque;
127    extern crate alloc;
128    use alloc::sync::Arc;
129
130    #[derive(fbl::Recyclable)]
131    pub struct TestRustObject {
132        destroyed: Arc<AtomicBool>,
133    }
134
135    impl Drop for TestRustObject {
136        fn drop(&mut self) {
137            self.destroyed.store(true, Ordering::Relaxed);
138        }
139    }
140
141    #[test]
142    fn test_unique_ptr_drops() {
143        let destroyed = Arc::new(AtomicBool::new(false));
144        {
145            let obj = TestRustObject { destroyed: destroyed.clone() };
146            let _unique_ptr = UniquePtr::try_new(obj).unwrap();
147            assert!(!destroyed.load(Ordering::Relaxed));
148        } // unique_ptr drops here
149
150        assert!(destroyed.load(Ordering::Relaxed));
151    }
152
153    #[test]
154    fn test_unique_ptr_into_raw() {
155        let destroyed = Arc::new(AtomicBool::new(false));
156        let raw_ptr;
157        {
158            let obj = TestRustObject { destroyed: destroyed.clone() };
159            let unique_ptr = UniquePtr::try_new(obj).unwrap();
160            assert!(!destroyed.load(Ordering::Relaxed));
161            raw_ptr = UniquePtr::into_raw(unique_ptr);
162        } // unique_ptr drops here but into_raw called, so no destruction
163
164        assert!(!destroyed.load(Ordering::Relaxed));
165
166        // Clean up manually
167        unsafe {
168            drop(Box::from_raw(raw_ptr));
169        }
170        assert!(destroyed.load(Ordering::Relaxed));
171    }
172
173    unsafe extern "C" {
174        fn create_cpp_object(destroyed: *mut bool) -> *mut c_void;
175        fn destroy_cpp_object(ptr: *mut c_void);
176    }
177
178    pub struct TestCppObject;
179
180    unsafe impl Recyclable for Opaque<TestCppObject> {
181        unsafe fn recycle(ptr: NonNull<Self>) {
182            unsafe {
183                destroy_cpp_object(ptr.as_ptr() as *mut c_void);
184            }
185        }
186    }
187
188    #[test]
189    #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
190    fn test_unique_ptr_cpp_drops() {
191        let destroyed = AtomicBool::new(false);
192        unsafe {
193            let raw_ptr = create_cpp_object(destroyed.as_ptr() as *mut bool);
194            assert!(!destroyed.load(Ordering::Relaxed));
195            {
196                let _unique_ptr = UniquePtr::from_raw(raw_ptr as *mut Opaque<TestCppObject>);
197                assert!(!destroyed.load(Ordering::Relaxed));
198            } // unique_ptr drops here
199
200            assert!(destroyed.load(Ordering::Relaxed));
201        }
202    }
203
204    #[test]
205    fn test_unique_ptr_as_mut_ptr() {
206        let destroyed = Arc::new(AtomicBool::new(false));
207        let mut unique_ptr =
208            UniquePtr::try_new(TestRustObject { destroyed: destroyed.clone() }).unwrap();
209        let mut_ptr = UniquePtr::as_mut_ptr(&mut unique_ptr);
210        assert!(!mut_ptr.is_null());
211    }
212
213    #[test]
214    fn test_unique_ptr_deref_mut() {
215        let destroyed = Arc::new(AtomicBool::new(false));
216        let mut unique_ptr =
217            UniquePtr::try_new(TestRustObject { destroyed: destroyed.clone() }).unwrap();
218        let _deref_mut: &mut TestRustObject = &mut unique_ptr;
219    }
220
221    #[test]
222    fn test_unique_ptr_cpp_alloc_fail() {
223        let dummy_val = Opaque::uninit();
224        let res = Opaque::<TestCppObject>::allocate(dummy_val);
225        assert!(res.is_err());
226    }
227}