moveit/cxx_support.rs
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
17use std::mem::MaybeUninit;
18use std::pin::Pin;
19
20use cxx::memory::UniquePtrTarget;
21use cxx::UniquePtr;
22
23use crate::move_ref::AsMove;
24use crate::slot::DroppingSlot;
25use crate::DerefMove;
26use crate::Emplace;
27use crate::MoveRef;
28use 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;`
42pub 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>>`.
53 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.
63 unsafe fn free_uninitialized_cpp_storage(ptr: *mut Self);
64}
65
66impl<T: MakeCppStorage + UniquePtrTarget> Emplace<T> for UniquePtr<T> {
67 type Output = Self;
68
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)]
90pub struct DeallocateSpaceGuard<T: MakeCppStorage>(*mut T);
91
92impl<T: MakeCppStorage> DeallocateSpaceGuard<T> {
93 fn assume_init_mut(&mut self) -> &mut T {
94 unsafe { &mut *self.0 }
95 }
96}
97
98impl<T: MakeCppStorage> Drop for DeallocateSpaceGuard<T> {
99 fn drop(&mut self) {
100 unsafe { T::free_uninitialized_cpp_storage(self.0) };
101 }
102}
103
104impl<T> AsMove for UniquePtr<T>
105where
106 T: UniquePtrTarget + MakeCppStorage,
107{
108 type Storage = DeallocateSpaceGuard<T>;
109
110 #[inline]
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
126unsafe impl<T> DerefMove for UniquePtr<T>
127where
128 T: MakeCppStorage + UniquePtrTarget,
129 T: Unpin,
130{
131 #[inline]
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}