//! 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(Weak

); /// 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 { /// The actual explosive (might be armed or disarmed) bomb: DropBomb, /// The underlying `Arc` #[deref(forward)] #[deref_mut(forward)] arc: Arc

, } impl ProtectedWeak

{ /// 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> { Some(ProtectedArc::new(self.0.upgrade()?)) } /// Convert back into an unprotected `Weak`. /// /// # CORRECTNESS /// /// You must arrange that the drop reentrancy requirements aren't violated /// by `Arc`s made from the returned `Weak`. pub(crate) fn unprotect(self) -> Weak

{ self.0 } } impl ProtectedArc

{ /// 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(); } }