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}