Skip to main content

fragile/
lib.rs

1#![deny(unsafe_op_in_unsafe_fn)]
2
3//! This library provides wrapper types that permit sending non `Send` types to
4//! other threads and use runtime checks to ensure safety.
5//!
6//! It provides three types: [`Fragile`] and [`Sticky`] which are similar in nature
7//! but have different behaviors with regards to how destructors are executed and
8//! the extra [`SemiSticky`] type which uses [`Sticky`] if the value has a
9//! destructor and [`Fragile`] if it does not.
10//!
11//! All three types wrap a value and provide a `Send` bound.  Neither of the types permit
12//! access to the enclosed value unless the thread that wrapped the value is attempting
13//! to access it.  The difference between the types starts playing a role once
14//! destructors are involved.
15//!
16//! A [`Fragile`] will actually send the `T` from thread to thread but will only
17//! permit the original thread to invoke the destructor.  If the value gets dropped
18//! in a different thread, the destructor will panic.
19//!
20//! A [`Sticky`] on the other hand does not actually send the `T` around but keeps
21//! it stored in the original thread's thread local storage.  If it gets dropped
22//! in the originating thread it gets cleaned up immediately, otherwise it retains
23//! the value until the thread shuts down naturally. Access uses scoped callbacks so
24//! references to the TLS value cannot outlive the callback or the originating thread.
25//!
26//! There is a third type 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, values placed in it must be `'static`, and access
56//! is limited to callbacks that cannot return a reference to the stored value.
57//!
58//! ```
59//! use std::thread;
60//! use fragile::Sticky;
61//!
62//! // Creating and using a sticky object in the same thread works.
63//! let val = Sticky::new(true);
64//! assert!(val.with(|value| *value));
65//! assert!(val.try_with(|value| *value).is_ok());
66//!
67//! // Once sent to another thread, its value cannot be accessed.
68//! thread::spawn(move || {
69//!     assert!(val.try_with(|_| ()).is_err());
70//! }).join()
71//!     .unwrap();
72//! ```
73//!
74//! # Why?
75//!
76//! Most of the time trying to use this crate is going to indicate some code smell.  But
77//! there are situations where this is useful.  For instance you might have a bunch of
78//! non `Send` types but want to work with a `Send` error type.  In that case the non
79//! sendable extra information can be contained within the error and in cases where the
80//! error did not cross a thread boundary yet extra information can be obtained.
81//!
82//! # Drop / Cleanup Behavior
83//!
84//! All types will try to eagerly drop a value if they are dropped on the right thread.
85//! [`Sticky`] and [`SemiSticky`] will however retain memory until a thread shuts down
86//! if the value is dropped on the wrong thread. The benefit is that these types do not
87//! panic. A [`Fragile`] dropped in the wrong thread will not just panic,
88//! it will effectively also tear down the process because panicking in destructors is
89//! non recoverable.
90//!
91//! # Features
92//!
93//! The default `stream` feature enables `Future` and `Stream` support and uses
94//! `futures-core`. Disable default features to use the crate without dependencies.
95//! The optional `slab` feature optimizes [`Sticky`]'s internal registry.
96//!
97//! The `future` feature implements [`Future`](std::future::Future) for the wrapper
98//! types. The `stream` feature includes `future` and implements
99//! `futures_core::Stream` as well.
100mod errors;
101mod fragile;
102mod registry;
103mod semisticky;
104mod sticky;
105mod thread_id;
106
107#[cfg(feature = "future")]
108mod futures;
109
110pub use crate::errors::InvalidThreadAccess;
111pub use crate::fragile::Fragile;
112pub use crate::semisticky::SemiSticky;
113pub use crate::sticky::Sticky;