1use crate::recyclable::Recyclable;
8use core::ops::{Deref, DerefMut};
9use core::ptr::NonNull;
10use kalloc::AllocError;
11
12#[repr(C)]
19pub struct UniquePtr<T>
20where
21 T: Recyclable,
22{
23 ptr: NonNull<T>,
24}
25
26impl<T: Recyclable> UniquePtr<T> {
27 pub unsafe fn from_raw(ptr: *mut T) -> Self {
35 unsafe { UniquePtr { ptr: NonNull::new_unchecked(ptr) } }
37 }
38
39 pub fn try_new(value: T) -> Result<UniquePtr<T>, AllocError> {
45 let ptr = T::allocate(value)?;
46 Ok(UniquePtr { ptr })
47 }
48
49 pub fn as_ptr(this: &Self) -> *const T {
51 this.ptr.as_ptr()
52 }
53
54 pub fn as_mut_ptr(this: &mut Self) -> *mut T {
56 this.ptr.as_ptr()
57 }
58
59 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 } 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 } assert!(!destroyed.load(Ordering::Relaxed));
138
139 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
161 #[test]
162 #[cfg_attr(miri, ignore = "miri does not support calling foreign functions")]
163 fn test_unique_ptr_cpp_drops() {
164 let destroyed = AtomicBool::new(false);
165 unsafe {
166 let raw_ptr = create_cpp_object(destroyed.as_ptr() as *mut bool);
167 assert!(!destroyed.load(Ordering::Relaxed));
168 {
169 let _unique_ptr = UniquePtr::from_raw(raw_ptr as *mut Opaque<TestCppObject>);
170 assert!(!destroyed.load(Ordering::Relaxed));
171 } assert!(destroyed.load(Ordering::Relaxed));
174 }
175 }
176
177 #[test]
178 fn test_unique_ptr_as_mut_ptr() {
179 let destroyed = Arc::new(AtomicBool::new(false));
180 let mut unique_ptr =
181 UniquePtr::try_new(TestRustObject { destroyed: destroyed.clone() }).unwrap();
182 let mut_ptr = UniquePtr::as_mut_ptr(&mut unique_ptr);
183 assert!(!mut_ptr.is_null());
184 }
185
186 #[test]
187 fn test_unique_ptr_deref_mut() {
188 let destroyed = Arc::new(AtomicBool::new(false));
189 let mut unique_ptr =
190 UniquePtr::try_new(TestRustObject { destroyed: destroyed.clone() }).unwrap();
191 let _deref_mut: &mut TestRustObject = &mut unique_ptr;
192 }
193
194 #[test]
195 fn test_unique_ptr_cpp_alloc_fail() {
196 let dummy_val = Opaque::uninit();
197 let res = Opaque::<TestCppObject>::allocate(dummy_val);
198 assert!(res.is_err());
199 }
200}