1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! 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() };
    };
}