blob: b3b0672dfeabd2b4db9bcff4a3a99505a584b8d2 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Drop flags.
//!
//! The [`Pin<P>`] guarantees state that if we have a `T` allocated somewhere,
//! and we construct a pinned reference to it such as a `Pin<&'a mut T>`, then
//! before that "somewhere" in memory is reused by another Rust object, `T`'s
//! destructor must run.
//!
//! Normally, this isn't a problem for Rust code, since the storage of an object
//! is destroyed immediately after it is destroyed. [`DerefMove`], however,
//! breaks this expectation: it separates the destructors from its storage and
//! contents into two separately destroyed objects: a [`AsMove::Storage`] and a
//! [`MoveRef`]. If the [`MoveRef`] is [`mem::forget`]'ed, we lose: the storage
//! will potentially be re-used.
//!
//! Therefore, we must somehow detect that [`MoveRef`]s fail to be destroyed
//! when the destructor for the corresponding storage is run, and remediate it,
//! either by leaking heap storage or aborting if we would free stack storage
//! (a panic is insufficient, since that location can be reused if the panic is
//! caught).
//!
//! A [`DropFlag`] allows us to achieve this. It is a generalized, library-level
//! version of the Rust language's drop flags, which it uses to dynamically
//! determine whether to run destructors of stack-allocated values that might
//! have been moved from. Unlike Rust language drop flags, a [`DropFlag`] is
//! actually a counter, rather than a boolean. This allows storage that holds
//! many objects, like a vector, ensure that all contents have been properly
//! destroyed.
//!
//! This module also provides two helper types simplify safe creation and
//! management of drop flags.
//!
//! See the [Rustonomicon entry](https://doc.rust-lang.org/nomicon/drop-flags.html)
//! for the Rust language equivalent.
//!
//! # Safety
//!
//! No function in this module is `unsafe`: instead, functions that construct
//! [`MoveRef`]s out of [`DropFlag`]s are `unsafe`, and their callers are
//! responsible for ensuring that the passed-in [`DropFlag`] helps uphold the
//! relevant invariants.
use core::cell::Cell;
use core::mem;
use core::mem::ManuallyDrop;
use core::ops::Deref;
use core::ops::DerefMut;
#[cfg(doc)]
use {
crate::move_ref::{AsMove, DerefMove, MoveRef},
alloc::boxed::Box,
core::pin::Pin,
};
/// A drop flag, for tracking successful destruction.
///
/// A `DropFlag` is a reference to a counter somewhere on the stack that lives
/// adjacent to storage for some value. It is just a counter: `unsafe` code is
/// expected to associate semantic meaning to it.
///
/// A flag with a value of zero is usually called "dead", and setting a flag to
/// the dead state is called clearing it.
///
/// See the [module documentation][self] for more information.
#[derive(Clone, Copy)]
pub struct DropFlag<'frame> {
counter: &'frame Cell<usize>,
}
impl DropFlag<'_> {
/// Increments the internal counter.
///
/// This function does not provide any overflow protection; `unsafe` code is
/// responsible for making sure that cannot happen.
#[inline]
pub fn inc(self) {
self.counter.set(self.counter.get() + 1)
}
/// Decrements the internal counter and returns true if it became zero.
///
/// This function will return `false` if the counter was already zero.
#[inline]
pub fn dec_and_check_if_died(self) -> bool {
if self.counter.get() == 0 {
return false;
}
self.counter.set(self.counter.get() - 1);
self.is_dead()
}
/// Returns whether the internal counter is zero.
#[inline]
pub fn is_dead(self) -> bool {
self.counter.get() == 0
}
/// Lengthens the lifetime of `self`.
#[inline]
#[allow(unused)]
pub(crate) unsafe fn longer_lifetime<'a>(self) -> DropFlag<'a> {
DropFlag {
counter: mem::transmute(self.counter),
}
}
}
/// A wrapper for managing when a value gets dropped via a [`DropFlag`].
///
/// This type tracks the destruction state of some value relative to another
/// value via its [`DropFlag`]: for example, it might be the storage of a value
/// wrapped up in a [`MoveRef`]. When a `DroppingFlag` is destroyed, it will
/// run the destructor for the wrapped value if and only if the [`DropFlag`]
/// is dead.
///
/// This type can be viewed as using a [`DropFlag`] to "complete" a
/// [`ManuallyDrop<T>`] by explicitly tracking whether it has been dropped. The
/// flag can be used to signal whether to destroy or leak the value, but the
/// destruction occurs lazily rather than immediately when the flag is flipped.
///
/// This is useful as an [`AsMove::Storage`] type for types where the storage
/// should be leaked if the inner type was somehow not destroyed, such as in
/// the case of heap-allocated storage like [`Box<T>`].
pub struct DroppingFlag<T> {
value: ManuallyDrop<T>,
counter: Cell<usize>,
}
impl<T> DroppingFlag<T> {
/// Wraps a new value to have its drop state managed by a `DropFlag`.
///
/// The drop flag will start out dead and needs to be manually incremented.
pub fn new(value: T) -> Self {
Self {
value: ManuallyDrop::new(value),
counter: Cell::new(0),
}
}
/// Gets a reference to the drop flag.
///
/// This function is safe; the returned reference to the drop flag cannot be
/// used to make a previously dropped value live again.
pub fn flag(slot: &Self) -> DropFlag {
DropFlag {
counter: &slot.counter,
}
}
/// Splits this slot into a reference to the wrapped value plus a reference to
/// the drop flag.
///
/// This function is safe; the returned reference to the drop flag cannot be
/// used to make a previously dropped value live again, since the value is
/// not destroyed before the wrapper is.
pub fn as_parts(slot: &Self) -> (&T, DropFlag) {
(
&slot.value,
DropFlag {
counter: &slot.counter,
},
)
}
/// Splits this slot into a reference to the wrapped value plus a reference to
/// the drop flag.
///
/// This function is safe; the returned reference to the drop flag cannot be
/// used to make a previously dropped value live again, since the value is
/// not destroyed before the wrapper is.
pub fn as_parts_mut(slot: &mut Self) -> (&mut T, DropFlag) {
(
&mut slot.value,
DropFlag {
counter: &slot.counter,
},
)
}
}
impl<T> Deref for DroppingFlag<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.value
}
}
impl<T> DerefMut for DroppingFlag<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> Drop for DroppingFlag<T> {
fn drop(&mut self) {
if Self::flag(self).is_dead() {
unsafe {
ManuallyDrop::drop(&mut self.value);
}
}
}
}
/// An RAII trap that ensures a drop flag is correctly cleared.
///
/// This type is *similar* to a [`DroppingFlag`], except that it does not wrap
/// a value and rather than leaking memory aborts the program if its flag is
/// not cleared.
///
/// This type is useful for safely constructing [`MoveRef`]s.
pub struct TrappedFlag {
counter: Cell<usize>,
// In debug mode, we capture the location the trap is created at, to help
// connect an eventual failure to the matching storage.
#[cfg(debug_assertions)]
location: &'static core::panic::Location<'static>,
}
impl TrappedFlag {
/// Creates a new trap with a dead flag.
#[cfg(debug_assertions)]
#[track_caller]
pub fn new() -> Self {
Self {
counter: Cell::new(0),
location: core::panic::Location::caller(),
}
}
/// Creates a new trap with a dead flag.
#[cfg(not(debug_assertions))]
pub fn new() -> Self {
Self {
counter: Cell::new(0),
}
}
/// Returns a reference to the [`DropFlag`].
pub fn flag(&self) -> DropFlag {
DropFlag {
counter: &self.counter,
}
}
/// Preemptively checks that this flag has been cleared.
///
/// Aborts (rather than panicking!) if the assertion fails.
pub fn assert_cleared(&self) {
if self.flag().is_dead() {
return;
}
// We can force an abort by triggering a panic mid-unwind.
// This is the only way to force an LLVM abort from inside of `core`.
struct DoublePanic;
impl Drop for DoublePanic {
fn drop(&mut self) {
// In tests, we don't double-panic so that we can observe the
// failure correctly.
if cfg!(not(test)) {
panic!()
}
}
}
let _dp = DoublePanic;
#[cfg(debug_assertions)]
panic!("a critical drop flag at {} was not cleared!", self.location);
#[cfg(not(debug_assertions))]
panic!("a critical drop flag was not cleared!");
}
}
impl Default for TrappedFlag {
fn default() -> Self {
Self::new()
}
}
impl Drop for TrappedFlag {
fn drop(&mut self) {
self.assert_cleared();
}
}
/// A [`DropFlag`] source that doesn't do anything with it.
///
/// This is similar to `TrappedFlag`, but where it does not abort the program
/// if used incorrectly. This type is generally only useful when some separate
/// mechanism is ensuring that invariants are not violated.
pub struct QuietFlag {
counter: Cell<usize>,
}
impl QuietFlag {
/// Creates a new dead flag.
pub fn new() -> Self {
Self {
counter: Cell::new(0),
}
}
/// Returns a reference to the [`DropFlag`].
pub fn flag(&self) -> DropFlag {
DropFlag {
counter: &self.counter,
}
}
}
impl Default for QuietFlag {
fn default() -> Self {
Self::new()
}
}