//! Newtype which helps assure lack of drop entrance hazards //! //! Provides a drop bomb which will help tests detect latent bugs. //! //! We want this because there are places where we handle an Arc containing //! a user-provided trait object, but where we want to prevent invoking //! the user's Drop impl since that may lead to reentrancy. //! //! See the section on "Reentrancy" in the docs for `mtracker::State`. //! //! Outside tests, the types in this module are equivalent to `std::sync`'s. //! So, we never panic in a drop in production. //! Dropping in the wrong place might lead to a deadlock //! (due to mutex reentrancy) //! but this is far from certain: //! probably, while we're running, the caller has another live reference, //! so the drop of the underlying type won't happen now anyway. //! //! In any case, drop bombs mustn't be used in production. //! Not only can they escalate the severity of problems, //! where the program might blunder on, //! but also //! because Rust upstream are seriously considering //! [turning them into aborts](https://github.com/rust-lang/rfcs/pull/3288)! // // There are no separate tests for this module. Drop bombs are hard to test for. // However, in an ad-hoc test, the bomb has been shown to be able to explode, // if a `ProtectedArc` is dropped. use crate::internal_prelude::*; /// A `Weak
`, but upgradeable only to a `ProtectedArc`, not a raw `Arc`.
#[derive(Debug)]
pub(crate) struct ProtectedWeak );
/// An `Arc`, but containing a type which should only be dropped in certain places
///
/// In non `#[cfg(test)]` builds, this is just `Arc `.
///
/// When testing, it has a drop bomb. You must call `.promise_dropping_is_ok`.
/// It will panic if it's simply dropped.
#[derive(Debug, Deref, DerefMut)]
pub(crate) struct ProtectedArc ,
}
impl {
/// Make a new `ProtectedWeak`
pub(crate) fn new(p: Weak ) -> Self {
ProtectedWeak(p)
}
/// Upgrade a `ProtectedWeak` to a `ProtectedArc`, if it's not been garbage collected
pub(crate) fn upgrade(&self) -> Option {
self.0
}
}
impl {
/// Make a new `ProtectedArc` from a raw `Arc`
///
/// # CORRECTNESS
///
/// Presumably the `Arc` came from an uncontrolled external source, such as user code.
pub(crate) fn new(arc: Arc ) -> Self {
let bomb = DropBomb::new_armed();
ProtectedArc { arc, bomb }
}
/// Obtain a `ProtectedWeak` from a `&ProtectedArc`
//
// If this were a more general-purpose library, we'd avoid this and other methods on `self`.
pub(crate) fn downgrade(&self) -> ProtectedWeak {
ProtectedWeak(Arc::downgrade(&self.arc))
}
/// Convert back into an unprotected `Arc`
///
/// # CORRECTNESS
///
/// If the return value is dropped, the location must be suitable for that.
/// Or, maybe the returned value is going to calling code in the external user,
/// (which, therefore, wouldn't pose a reentrancy hazard).
pub(crate) fn promise_dropping_is_ok(mut self) -> Arc {
self.bomb.disarm();
self.arc
}
}
#[cfg(test)]
mod test {
// @@ begin test lint list maintained by maint/add_warning @@
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::string_slice)] // See arti#2571
//!
#![allow(clippy::let_and_return)] // TODO this lint is annoying and we should disable it
use super::*;
struct Payload;
#[test]
fn fine() {
let arc = Arc::new(Payload);
let prot = ProtectedArc::new(arc);
let arc = prot.promise_dropping_is_ok();
drop(arc);
}
#[test]
fn bad() {
let arc = Arc::new(Payload);
let mut prot = ProtectedArc::new(arc);
let h = prot.bomb.make_simulated();
drop(prot);
h.expect_exploded();
}
}