1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Support for `cxx` types. 16 17 use std::mem::MaybeUninit; 18 use std::pin::Pin; 19 20 use cxx::memory::UniquePtrTarget; 21 use cxx::UniquePtr; 22 23 use crate::move_ref::AsMove; 24 use crate::slot::DroppingSlot; 25 use crate::DerefMove; 26 use crate::Emplace; 27 use crate::MoveRef; 28 use crate::TryNew; 29 30 /// A type which has the ability to create heap storage space 31 /// for itself in C++, without initializing that storage. 32 /// 33 /// # Safety 34 /// 35 /// Implementers must ensure that the pointer returned by 36 /// `allocate_uninitialized_cpp_storage` is a valid, non-null, 37 /// pointer to a new but uninitialized storage block, and that 38 /// such blocks must be freeable using either of these routes: 39 /// 40 /// * before they're initialized, using `free_uninitialized_cpp_storage` 41 /// * after they're initialized, via a delete expression like `delete p;` 42 pub unsafe trait MakeCppStorage: Sized { 43 /// Allocates heap space for this type in C++ and return a pointer 44 /// to that space, but do not initialize that space (i.e. do not 45 /// yet call a constructor). 46 /// 47 /// # Safety 48 /// 49 /// To avoid memory leaks, callers must ensure that this space is 50 /// freed using `free_uninitialized_cpp_storage`, or is converted into 51 /// a [`UniquePtr`] such that it can later be freed by 52 /// `std::unique_ptr<T, std::default_delete<T>>`. allocate_uninitialized_cpp_storage() -> *mut Self53 unsafe fn allocate_uninitialized_cpp_storage() -> *mut Self; 54 55 /// Frees a C++ allocation which has not yet 56 /// had a constructor called. 57 /// 58 /// # Safety 59 /// 60 /// Callers guarantee that the pointer here was allocated by 61 /// `allocate_uninitialized_cpp_storage` and has not been 62 /// initialized. free_uninitialized_cpp_storage(ptr: *mut Self)63 unsafe fn free_uninitialized_cpp_storage(ptr: *mut Self); 64 } 65 66 impl<T: MakeCppStorage + UniquePtrTarget> Emplace<T> for UniquePtr<T> { 67 type Output = Self; 68 try_emplace<N: TryNew<Output = T>>(n: N) -> Result<Self, N::Error>69 fn try_emplace<N: TryNew<Output = T>>(n: N) -> Result<Self, N::Error> { 70 unsafe { 71 let uninit_ptr = T::allocate_uninitialized_cpp_storage(); 72 let uninit = 73 Pin::new_unchecked(&mut *(uninit_ptr as *mut MaybeUninit<T>)); 74 // FIXME - this is not panic safe. 75 let result = n.try_new(uninit); 76 if let Err(err) = result { 77 T::free_uninitialized_cpp_storage(uninit_ptr); 78 return Err(err); 79 } 80 Ok(UniquePtr::from_raw(uninit_ptr)) 81 } 82 } 83 } 84 85 /// This is an implementation detail of the support for moving out of 86 /// a [`cxx::UniquePtr`]. It stores a raw pointer which points to 87 /// C++ space which is allocated yet unoccupied, and will arrange to 88 /// deallocate that if it goes out of scope. 89 #[doc(hidden)] 90 pub struct DeallocateSpaceGuard<T: MakeCppStorage>(*mut T); 91 92 impl<T: MakeCppStorage> DeallocateSpaceGuard<T> { assume_init_mut(&mut self) -> &mut T93 fn assume_init_mut(&mut self) -> &mut T { 94 unsafe { &mut *self.0 } 95 } 96 } 97 98 impl<T: MakeCppStorage> Drop for DeallocateSpaceGuard<T> { drop(&mut self)99 fn drop(&mut self) { 100 unsafe { T::free_uninitialized_cpp_storage(self.0) }; 101 } 102 } 103 104 impl<T> AsMove for UniquePtr<T> 105 where 106 T: UniquePtrTarget + MakeCppStorage, 107 { 108 type Storage = DeallocateSpaceGuard<T>; 109 110 #[inline] as_move<'frame>( self, storage: DroppingSlot<'frame, Self::Storage>, ) -> Pin<MoveRef<'frame, Self::Target>> where Self: 'frame,111 fn as_move<'frame>( 112 self, 113 storage: DroppingSlot<'frame, Self::Storage>, 114 ) -> Pin<MoveRef<'frame, Self::Target>> 115 where 116 Self: 'frame, 117 { 118 let cast = DeallocateSpaceGuard(self.into_raw()); 119 let (storage, drop_flag) = storage.put(cast); 120 let this = 121 unsafe { MoveRef::new_unchecked(storage.assume_init_mut(), drop_flag) }; 122 MoveRef::into_pin(this) 123 } 124 } 125 126 unsafe impl<T> DerefMove for UniquePtr<T> 127 where 128 T: MakeCppStorage + UniquePtrTarget, 129 T: Unpin, 130 { 131 #[inline] deref_move<'frame>( self, storage: DroppingSlot<'frame, Self::Storage>, ) -> MoveRef<'frame, Self::Target> where Self: 'frame,132 fn deref_move<'frame>( 133 self, 134 storage: DroppingSlot<'frame, Self::Storage>, 135 ) -> MoveRef<'frame, Self::Target> 136 where 137 Self: 'frame, 138 { 139 Pin::into_inner(self.as_move(storage)) 140 } 141 } 142