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