| 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)] |
| 122 | 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 | |