//! This library provides wrapper types that permit sending non `Send` types to //! other threads and use runtime checks to ensure safety. //! //! It provides three types: [`Fragile`] and [`Sticky`] which are similar in nature //! but have different behaviors with regards to how destructors are executed and //! the extra [`SemiSticky`] type which uses [`Sticky`] if the value has a //! destructor and [`Fragile`] if it does not. //! //! All three types wrap a value and provide a `Send` bound. Neither of the types permit //! access to the enclosed value unless the thread that wrapped the value is attempting //! to access it. The difference between the types starts playing a role once //! destructors are involved. //! //! A [`Fragile`] will actually send the `T` from thread to thread but will only //! permit the original thread to invoke the destructor. If the value gets dropped //! in a different thread, the destructor will panic. //! //! A [`Sticky`] on the other hand does not actually send the `T` around but keeps //! it stored in the original thread's thread local storage. If it gets dropped //! in the originating thread it gets cleaned up immediately, otherwise it leaks //! until the thread shuts down naturally. [`Sticky`] because it borrows into the //! TLS also requires you to "prove" that you are not doing any funny business with //! the borrowed value that lives for longer than the current stack frame which //! results in a slightly more complex API. //! //! There is a third typed called [`SemiSticky`] which shares the API with [`Sticky`] //! but internally uses a boxed [`Fragile`] if the type does not actually need a dtor //! in which case [`Fragile`] is preferred. //! //! # Fragile Usage //! //! [`Fragile`] is the easiest type to use. It works almost like a cell. //! //! ``` //! use std::thread; //! use fragile::Fragile; //! //! // creating and using a fragile object in the same thread works //! let val = Fragile::new(true); //! assert_eq!(*val.get(), true); //! assert!(val.try_get().is_ok()); //! //! // once send to another thread it stops working //! thread::spawn(move || { //! assert!(val.try_get().is_err()); //! }).join() //! .unwrap(); //! ``` //! //! # Sticky Usage //! //! [`Sticky`] is similar to [`Fragile`] but because it places the value in the //! thread local storage it comes with some extra restrictions to make it sound. //! The advantage is it can be dropped from any thread but it comes with extra //! restrictions. In particular it requires that values placed in it are `'static` //! and that [`StackToken`]s are used to restrict lifetimes. //! //! ``` //! use std::thread; //! use fragile::Sticky; //! //! // creating and using a fragile object in the same thread works //! fragile::stack_token!(tok); //! let val = Sticky::new(true); //! assert_eq!(*val.get(tok), true); //! assert!(val.try_get(tok).is_ok()); //! //! // once send to another thread it stops working //! thread::spawn(move || { //! fragile::stack_token!(tok); //! assert!(val.try_get(tok).is_err()); //! }).join() //! .unwrap(); //! ``` //! //! # Why? //! //! Most of the time trying to use this crate is going to indicate some code smell. But //! there are situations where this is useful. For instance you might have a bunch of //! non `Send` types but want to work with a `Send` error type. In that case the non //! sendable extra information can be contained within the error and in cases where the //! error did not cross a thread boundary yet extra information can be obtained. //! //! # Drop / Cleanup Behavior //! //! All types will try to eagerly drop a value if they are dropped on the right thread. //! [`Sticky`] and [`SemiSticky`] will however temporarily leak memory until a thread //! shuts down if the value is dropped on the wrong thread. The benefit however is that //! if you have that type of situation, and you can live with the consequences, the //! type is not panicking. A [`Fragile`] dropped in the wrong thread will not just panic, //! it will effectively also tear down the process because panicking in destructors is //! non recoverable. //! //! # Features //! //! By default the crate has no dependencies. Optionally the `slab` feature can //! be enabled which optimizes the internal storage of the [`Sticky`] type to //! make it use a [`slab`](https://docs.rs/slab/latest/slab/) instead. mod errors; mod fragile; mod registry; mod semisticky; mod sticky; mod thread_id; use std::marker::PhantomData; pub use crate::errors::InvalidThreadAccess; pub use crate::fragile::Fragile; pub use crate::semisticky::SemiSticky; pub use crate::sticky::Sticky; /// A token that is placed to the stack to constrain lifetimes. /// /// For more information about how these work see the documentation of /// [`stack_token!`] which is the only way to create this token. pub struct StackToken(PhantomData<*const ()>); impl StackToken { /// Stack tokens must only be created on the stack. #[doc(hidden)] pub unsafe fn __private_new() -> StackToken { // we place a const pointer in there to get a type // that is neither Send nor Sync. StackToken(PhantomData) } } /// Crates a token on the stack with a certain name for semi-sticky. /// /// The argument to the macro is the target name of a local variable /// which holds a reference to a stack token. Because this is the /// only way to create such a token, it acts as a proof to [`Sticky`] /// or [`SemiSticky`] that can be used to constrain the lifetime of the /// return values to the stack frame. /// /// This is necessary as otherwise a [`Sticky`] placed in a [`Box`] and /// leaked with [`Box::leak`] (which creates a static lifetime) would /// otherwise create a reference with `'static` lifetime. This is incorrect /// as the actual lifetime is constrained to the lifetime of the thread. /// For more information see [`issue 26`](https://github.com/mitsuhiko/fragile/issues/26). /// /// ```rust /// let sticky = fragile::Sticky::new(true); /// /// // this places a token on the stack. /// fragile::stack_token!(my_token); /// /// // the token needs to be passed to `get` and others. /// let _ = sticky.get(my_token); /// ``` #[macro_export] macro_rules! stack_token { ($name:ident) => { let $name = &unsafe { $crate::StackToken::__private_new() }; }; }