1 //! This library provides wrapper types that permit sending non `Send` types to 2 //! other threads and use runtime checks to ensure safety. 3 //! 4 //! It provides three types: [`Fragile`] and [`Sticky`] which are similar in nature 5 //! but have different behaviors with regards to how destructors are executed and 6 //! the extra [`SemiSticky`] type which uses [`Sticky`] if the value has a 7 //! destructor and [`Fragile`] if it does not. 8 //! 9 //! All three types wrap a value and provide a `Send` bound. Neither of the types permit 10 //! access to the enclosed value unless the thread that wrapped the value is attempting 11 //! to access it. The difference between the types starts playing a role once 12 //! destructors are involved. 13 //! 14 //! A [`Fragile`] will actually send the `T` from thread to thread but will only 15 //! permit the original thread to invoke the destructor. If the value gets dropped 16 //! in a different thread, the destructor will panic. 17 //! 18 //! A [`Sticky`] on the other hand does not actually send the `T` around but keeps 19 //! it stored in the original thread's thread local storage. If it gets dropped 20 //! in the originating thread it gets cleaned up immediately, otherwise it leaks 21 //! until the thread shuts down naturally. [`Sticky`] because it borrows into the 22 //! TLS also requires you to "prove" that you are not doing any funny business with 23 //! the borrowed value that lives for longer than the current stack frame which 24 //! results in a slightly more complex API. 25 //! 26 //! There is a third typed called [`SemiSticky`] which shares the API with [`Sticky`] 27 //! but internally uses a boxed [`Fragile`] if the type does not actually need a dtor 28 //! in which case [`Fragile`] is preferred. 29 //! 30 //! # Fragile Usage 31 //! 32 //! [`Fragile`] is the easiest type to use. It works almost like a cell. 33 //! 34 //! ``` 35 //! use std::thread; 36 //! use fragile::Fragile; 37 //! 38 //! // creating and using a fragile object in the same thread works 39 //! let val = Fragile::new(true); 40 //! assert_eq!(*val.get(), true); 41 //! assert!(val.try_get().is_ok()); 42 //! 43 //! // once send to another thread it stops working 44 //! thread::spawn(move || { 45 //! assert!(val.try_get().is_err()); 46 //! }).join() 47 //! .unwrap(); 48 //! ``` 49 //! 50 //! # Sticky Usage 51 //! 52 //! [`Sticky`] is similar to [`Fragile`] but because it places the value in the 53 //! thread local storage it comes with some extra restrictions to make it sound. 54 //! The advantage is it can be dropped from any thread but it comes with extra 55 //! restrictions. In particular it requires that values placed in it are `'static` 56 //! and that [`StackToken`]s are used to restrict lifetimes. 57 //! 58 //! ``` 59 //! use std::thread; 60 //! use fragile::Sticky; 61 //! 62 //! // creating and using a fragile object in the same thread works 63 //! fragile::stack_token!(tok); 64 //! let val = Sticky::new(true); 65 //! assert_eq!(*val.get(tok), true); 66 //! assert!(val.try_get(tok).is_ok()); 67 //! 68 //! // once send to another thread it stops working 69 //! thread::spawn(move || { 70 //! fragile::stack_token!(tok); 71 //! assert!(val.try_get(tok).is_err()); 72 //! }).join() 73 //! .unwrap(); 74 //! ``` 75 //! 76 //! # Why? 77 //! 78 //! Most of the time trying to use this crate is going to indicate some code smell. But 79 //! there are situations where this is useful. For instance you might have a bunch of 80 //! non `Send` types but want to work with a `Send` error type. In that case the non 81 //! sendable extra information can be contained within the error and in cases where the 82 //! error did not cross a thread boundary yet extra information can be obtained. 83 //! 84 //! # Drop / Cleanup Behavior 85 //! 86 //! All types will try to eagerly drop a value if they are dropped on the right thread. 87 //! [`Sticky`] and [`SemiSticky`] will however temporarily leak memory until a thread 88 //! shuts down if the value is dropped on the wrong thread. The benefit however is that 89 //! if you have that type of situation, and you can live with the consequences, the 90 //! type is not panicking. A [`Fragile`] dropped in the wrong thread will not just panic, 91 //! it will effectively also tear down the process because panicking in destructors is 92 //! non recoverable. 93 //! 94 //! # Features 95 //! 96 //! By default the crate has no dependencies. Optionally the `slab` feature can 97 //! be enabled which optimizes the internal storage of the [`Sticky`] type to 98 //! make it use a [`slab`](https://docs.rs/slab/latest/slab/) instead. 99 mod errors; 100 mod fragile; 101 mod registry; 102 mod semisticky; 103 mod sticky; 104 mod thread_id; 105 106 use std::marker::PhantomData; 107 108 pub use crate::errors::InvalidThreadAccess; 109 pub use crate::fragile::Fragile; 110 pub use crate::semisticky::SemiSticky; 111 pub use crate::sticky::Sticky; 112 113 /// A token that is placed to the stack to constrain lifetimes. 114 /// 115 /// For more information about how these work see the documentation of 116 /// [`stack_token!`] which is the only way to create this token. 117 pub struct StackToken(PhantomData<*const ()>); 118 119 impl StackToken { 120 /// Stack tokens must only be created on the stack. 121 #[doc(hidden)] __private_new() -> StackToken122 pub unsafe fn __private_new() -> StackToken { 123 // we place a const pointer in there to get a type 124 // that is neither Send nor Sync. 125 StackToken(PhantomData) 126 } 127 } 128 129 /// Crates a token on the stack with a certain name for semi-sticky. 130 /// 131 /// The argument to the macro is the target name of a local variable 132 /// which holds a reference to a stack token. Because this is the 133 /// only way to create such a token, it acts as a proof to [`Sticky`] 134 /// or [`SemiSticky`] that can be used to constrain the lifetime of the 135 /// return values to the stack frame. 136 /// 137 /// This is necessary as otherwise a [`Sticky`] placed in a [`Box`] and 138 /// leaked with [`Box::leak`] (which creates a static lifetime) would 139 /// otherwise create a reference with `'static` lifetime. This is incorrect 140 /// as the actual lifetime is constrained to the lifetime of the thread. 141 /// For more information see [`issue 26`](https://github.com/mitsuhiko/fragile/issues/26). 142 /// 143 /// ```rust 144 /// let sticky = fragile::Sticky::new(true); 145 /// 146 /// // this places a token on the stack. 147 /// fragile::stack_token!(my_token); 148 /// 149 /// // the token needs to be passed to `get` and others. 150 /// let _ = sticky.get(my_token); 151 /// ``` 152 #[macro_export] 153 macro_rules! stack_token { 154 ($name:ident) => { 155 let $name = &unsafe { $crate::StackToken::__private_new() }; 156 }; 157 } 158