moveit/
drop_flag.rs

1// Copyright 2021 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//! Drop flags.
16//!
17//! The [`Pin<P>`] guarantees state that if we have a `T` allocated somewhere,
18//! and we construct a pinned reference to it such as a `Pin<&'a mut T>`, then
19//! before that "somewhere" in memory is reused by another Rust object, `T`'s
20//! destructor must run.
21//!
22//! Normally, this isn't a problem for Rust code, since the storage of an object
23//! is destroyed immediately after it is destroyed. [`DerefMove`], however,
24//! breaks this expectation: it separates the destructors from its storage and
25//! contents into two separately destroyed objects: a [`AsMove::Storage`] and a
26//! [`MoveRef`]. If the [`MoveRef`] is [`mem::forget`]'ed, we lose: the storage
27//! will potentially be re-used.
28//!
29//! Therefore, we must somehow detect that [`MoveRef`]s fail to be destroyed
30//! when the destructor for the corresponding storage is run, and remediate it,
31//! either by leaking heap storage or aborting if we would free stack storage
32//! (a panic is insufficient, since that location can be reused if the panic is
33//! caught).
34//!
35//! A [`DropFlag`] allows us to achieve this. It is a generalized, library-level
36//! version of the Rust language's drop flags, which it uses to dynamically
37//! determine whether to run destructors of stack-allocated values that might
38//! have been moved from. Unlike Rust language drop flags, a [`DropFlag`] is
39//! actually a counter, rather than a boolean. This allows storage that holds
40//! many objects, like a vector, ensure that all contents have been properly
41//! destroyed.
42//!
43//! This module also provides two helper types simplify safe creation and
44//! management of drop flags.
45//!
46//! See the [Rustonomicon entry](https://doc.rust-lang.org/nomicon/drop-flags.html)
47//! for the Rust language equivalent.
48//!
49//! # Safety
50//!
51//! No function in this module is `unsafe`: instead, functions that construct
52//! [`MoveRef`]s out of [`DropFlag`]s are `unsafe`, and their callers are
53//! responsible for ensuring that the passed-in [`DropFlag`] helps uphold the
54//! relevant invariants.
55
56use core::cell::Cell;
57use core::mem;
58use core::mem::ManuallyDrop;
59use core::ops::Deref;
60use core::ops::DerefMut;
61
62#[cfg(doc)]
63use {
64  crate::move_ref::{AsMove, DerefMove, MoveRef},
65  alloc::boxed::Box,
66  core::pin::Pin,
67};
68
69/// A drop flag, for tracking successful destruction.
70///
71/// A `DropFlag` is a reference to a counter somewhere on the stack that lives
72/// adjacent to storage for some value. It is just a counter: `unsafe` code is
73/// expected to associate semantic meaning to it.
74///
75/// A flag with a value of zero is usually called "dead", and setting a flag to
76/// the dead state is called clearing it.
77///
78/// See the [module documentation][self] for more information.
79#[derive(Clone, Copy)]
80pub struct DropFlag<'frame> {
81  counter: &'frame Cell<usize>,
82}
83
84impl DropFlag<'_> {
85  /// Increments the internal counter.
86  ///
87  /// This function does not provide any overflow protection; `unsafe` code is
88  /// responsible for making sure that cannot happen.
89  #[inline]
90  pub fn inc(self) {
91    self.counter.set(self.counter.get() + 1)
92  }
93
94  /// Decrements the internal counter and returns true if it became zero.
95  ///
96  /// This function will return `false` if the counter was already zero.
97  #[inline]
98  pub fn dec_and_check_if_died(self) -> bool {
99    if self.counter.get() == 0 {
100      return false;
101    }
102    self.counter.set(self.counter.get() - 1);
103    self.is_dead()
104  }
105
106  /// Returns whether the internal counter is zero.
107  #[inline]
108  pub fn is_dead(self) -> bool {
109    self.counter.get() == 0
110  }
111
112  /// Lengthens the lifetime of `self`.
113  #[inline]
114  #[allow(unused)]
115  pub(crate) unsafe fn longer_lifetime<'a>(self) -> DropFlag<'a> {
116    DropFlag {
117      counter: mem::transmute(self.counter),
118    }
119  }
120}
121
122/// A wrapper for managing when a value gets dropped via a [`DropFlag`].
123///
124/// This type tracks the destruction state of some value relative to another
125/// value via its [`DropFlag`]: for example, it might be the storage of a value
126/// wrapped up in a [`MoveRef`]. When a `DroppingFlag` is destroyed, it will
127/// run the destructor for the wrapped value if and only if the [`DropFlag`]
128/// is dead.
129///
130/// This type can be viewed as using a [`DropFlag`] to "complete" a
131/// [`ManuallyDrop<T>`] by explicitly tracking whether it has been dropped. The
132/// flag can be used to signal whether to destroy or leak the value, but the
133/// destruction occurs lazily rather than immediately when the flag is flipped.
134///
135/// This is useful as an [`AsMove::Storage`] type for types where the storage
136/// should be leaked if the inner type was somehow not destroyed, such as in
137/// the case of heap-allocated storage like [`Box<T>`].
138pub struct DroppingFlag<T> {
139  value: ManuallyDrop<T>,
140  counter: Cell<usize>,
141}
142
143impl<T> DroppingFlag<T> {
144  /// Wraps a new value to have its drop state managed by a `DropFlag`.
145  ///
146  /// The drop flag will start out dead and needs to be manually incremented.
147  pub fn new(value: T) -> Self {
148    Self {
149      value: ManuallyDrop::new(value),
150      counter: Cell::new(0),
151    }
152  }
153
154  /// Gets a reference to the drop flag.
155  ///
156  /// This function is safe; the returned reference to the drop flag cannot be
157  /// used to make a previously dropped value live again.
158  pub fn flag(slot: &Self) -> DropFlag {
159    DropFlag {
160      counter: &slot.counter,
161    }
162  }
163
164  /// Splits this slot into a reference to the wrapped value plus a reference to
165  /// the drop flag.
166  ///
167  /// This function is safe; the returned reference to the drop flag cannot be
168  /// used to make a previously dropped value live again, since the value is
169  /// not destroyed before the wrapper is.
170  pub fn as_parts(slot: &Self) -> (&T, DropFlag) {
171    (
172      &slot.value,
173      DropFlag {
174        counter: &slot.counter,
175      },
176    )
177  }
178
179  /// Splits this slot into a reference to the wrapped value plus a reference to
180  /// the drop flag.
181  ///
182  /// This function is safe; the returned reference to the drop flag cannot be
183  /// used to make a previously dropped value live again, since the value is
184  /// not destroyed before the wrapper is.
185  pub fn as_parts_mut(slot: &mut Self) -> (&mut T, DropFlag) {
186    (
187      &mut slot.value,
188      DropFlag {
189        counter: &slot.counter,
190      },
191    )
192  }
193}
194
195impl<T> Deref for DroppingFlag<T> {
196  type Target = T;
197  #[inline]
198  fn deref(&self) -> &T {
199    &self.value
200  }
201}
202
203impl<T> DerefMut for DroppingFlag<T> {
204  #[inline]
205  fn deref_mut(&mut self) -> &mut T {
206    &mut self.value
207  }
208}
209
210impl<T> Drop for DroppingFlag<T> {
211  fn drop(&mut self) {
212    if Self::flag(self).is_dead() {
213      unsafe {
214        ManuallyDrop::drop(&mut self.value);
215      }
216    }
217  }
218}
219
220/// An RAII trap that ensures a drop flag is correctly cleared.
221///
222/// This type is *similar* to a [`DroppingFlag`], except that it does not wrap
223/// a value and rather than leaking memory aborts the program if its flag is
224/// not cleared.
225///
226/// This type is useful for safely constructing [`MoveRef`]s.
227pub struct TrappedFlag {
228  counter: Cell<usize>,
229
230  // In debug mode, we capture the location the trap is created at, to help
231  // connect an eventual failure to the matching storage.
232  #[cfg(debug_assertions)]
233  location: &'static core::panic::Location<'static>,
234}
235
236impl TrappedFlag {
237  /// Creates a new trap with a dead flag.
238  #[cfg(debug_assertions)]
239  #[track_caller]
240  pub fn new() -> Self {
241    Self {
242      counter: Cell::new(0),
243      location: core::panic::Location::caller(),
244    }
245  }
246
247  /// Creates a new trap with a dead flag.
248  #[cfg(not(debug_assertions))]
249  pub fn new() -> Self {
250    Self {
251      counter: Cell::new(0),
252    }
253  }
254
255  /// Returns a reference to the [`DropFlag`].
256  pub fn flag(&self) -> DropFlag {
257    DropFlag {
258      counter: &self.counter,
259    }
260  }
261
262  /// Preemptively checks that this flag has been cleared.
263  ///
264  /// Aborts (rather than panicking!) if the assertion fails.
265  pub fn assert_cleared(&self) {
266    if self.flag().is_dead() {
267      return;
268    }
269
270    // We can force an abort by triggering a panic mid-unwind.
271    // This is the only way to force an LLVM abort from inside of `core`.
272    struct DoublePanic;
273    impl Drop for DoublePanic {
274      fn drop(&mut self) {
275        // In tests, we don't double-panic so that we can observe the
276        // failure correctly.
277        if cfg!(not(test)) {
278          panic!()
279        }
280      }
281    }
282
283    let _dp = DoublePanic;
284
285    #[cfg(debug_assertions)]
286    panic!("a critical drop flag at {} was not cleared!", self.location);
287
288    #[cfg(not(debug_assertions))]
289    panic!("a critical drop flag was not cleared!");
290  }
291}
292
293impl Default for TrappedFlag {
294  fn default() -> Self {
295    Self::new()
296  }
297}
298
299impl Drop for TrappedFlag {
300  fn drop(&mut self) {
301    self.assert_cleared();
302  }
303}
304
305/// A [`DropFlag`] source that doesn't do anything with it.
306///
307/// This is similar to `TrappedFlag`, but where it does not abort the program
308/// if used incorrectly. This type is generally only useful when some separate
309/// mechanism is ensuring that invariants are not violated.
310pub struct QuietFlag {
311  counter: Cell<usize>,
312}
313
314impl QuietFlag {
315  /// Creates a new dead flag.
316  pub fn new() -> Self {
317    Self {
318      counter: Cell::new(0),
319    }
320  }
321
322  /// Returns a reference to the [`DropFlag`].
323  pub fn flag(&self) -> DropFlag {
324    DropFlag {
325      counter: &self.counter,
326    }
327  }
328}
329
330impl Default for QuietFlag {
331  fn default() -> Self {
332    Self::new()
333  }
334}