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 | |