linked/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Copyright (c) Folo authors.
3
4//! Mechanisms for creating families of linked objects that can collaborate across threads
5//! while being internally single-threaded.
6//!
7//! The problem this crate solves is that while writing highly efficient lock-free thread-local
8//! code can yield great performance, it comes with serious drawbacks in terms of usability and
9//! developer experience.
10//!
11//! This crate bridges the gap by providing patterns and mechanisms that facilitate thread-local
12//! behavior while presenting a simple and reasonably ergonomic API to user code:
13//!
14//! * Internally, a linked object can take advantage of lock-free thread-isolated logic for **high
15//!   performance and efficiency** because it operates as a multithreaded family of single-threaded
16//!   objects, each of which implements local behavior on a single thread.
17//! * Externally, the linked object looks and acts very much like a regular Rust object and can
18//!   operate on multiple threads, providing **a reasonably simple API with minimal extra
19//!   complexity**.
20//!
21#![ doc=mermaid!( "../doc/linked.mermaid") ]
22//!
23//!
24//! The patterns and mechanisms provided by this crate are designed to make it easy to create such
25//! object families and to provide primitives that allow these object families to be used without
26//! the user code having to understand how the objects are wired up inside or keeping track of which
27//! instance is meant to be used where and on which thread.
28//!
29//! This is part of the [Folo project](https://github.com/folo-rs/folo) that provides mechanisms for
30//! high-performance hardware-aware programming in Rust.
31//!
32//! # What is a linked object?
33//!
34//! Linked objects are types decorated with `#[linked::object]` whose instances:
35//!
36//! 1. are each local to a single thread (i.e. [`!Send`][4]); (see also, [linked objects on multiple
37//!    threads][multiple-threads])
38//! 1. and are internally connected to other instances from the same family (note that multiple
39//!    families of the same type may exist, each instance belongs to exactly one);
40//! 1. and share some state between instances in the same family, e.g. via messaging or synchronized
41//!    storage;
42//! 1. and perform all collaboration between instances in the same family without involvement of
43//!    user code (i.e. there is no `Arc` or `Mutex` that the user needs to create/operate).
44//!
45//! In most cases, as long as logic is thread-local, user code can treat linked objects like any
46//! other Rust structs. The mechanisms only have an effect when [instances on multiple threads need
47//! to collaborate][multiple-threads].
48//!
49//! Note that despite instances of linked objects being designed for thread-local use, there may
50//! still exist multiple instances per thread in the same family. You can explicitly opt-in to
51//! "one per thread" behavior via the [`linked::PerThread<T>`][3] wrapper.
52//!
53//! # What is a family of linked objects?
54//!
55//! A family of linked objects is the unit of collaboration between instances. Each instance in a
56//! family can communicate with all other instances in the same family through shared state or other
57//! synchronization mechanisms. They act as a single distributed object, exhibiting thread-local
58//! behavior by default and internally triggering global behavior as needed.
59//!
60//! Instances are defined as belonging to the same family if they:
61//!
62//! - are created via cloning;
63//! - or are created by obtaining a thread-safe [Handle] from another family member, which is
64//!   thereafter converted to a new instance (potentially on a different thread);
65//! - or are obtained from the same static variable in a [`linked::instance_per_access!`][1]
66//!   or [`linked::instance_per_thread!`][2] macro block;
67//! - or are created from the same [`linked::PerThread<T>`][3] or one of its clones.
68//!
69//! # Using and defining linked objects
70//!
71//! A very basic and contrived example is a `Thing` that shares a `value` between all its instances.
72//!
73//! This object can generally be used like any other Rust type. All linked objects support cloning,
74//! since that is one of the primary mechanisms for creating additional linked instances.
75//!
76//! ```rust
77//! # use std::sync::{Arc, Mutex};
78//! # #[linked::object]
79//! # struct Thing {
80//! #     value: Arc<Mutex<String>>,
81//! # }
82//! # impl Thing {
83//! #     pub fn new(initial_value: String) -> Self {
84//! #         let shared_value = Arc::new(Mutex::new(initial_value));
85//! #         linked::new!(Self {
86//! #             value: shared_value.clone(),
87//! #         })
88//! #     }
89//! #     pub fn value(&self) -> String {
90//! #         self.value.lock().unwrap().clone()
91//! #     }
92//! #     pub fn set_value(&self, value: String) {
93//! #         *self.value.lock().unwrap() = value;
94//! #     }
95//! # }
96//! let thing1 = Thing::new("hello".to_string());
97//! let thing2 = thing1.clone();
98//! assert_eq!(thing1.value(), "hello");
99//! assert_eq!(thing2.value(), "hello");
100//!
101//! thing1.set_value("world".to_string());
102//! assert_eq!(thing1.value(), "world");
103//! assert_eq!(thing2.value(), "world");
104//! ```
105//!
106//! We can compare this example to the linked object definition above:
107//!
108//! * The relation between instances is established via cloning.
109//! * The `value` is shared.
110//! * Implementing the collaboration between instances does not require anything (e.g. a `Mutex`
111//!   or `mpsc::channel`) from user code.
112//!
113//! The implementation of this type is the following:
114//!
115//! ```rust
116//! use std::sync::{Arc, Mutex};
117//!
118//! #[linked::object]
119//! pub struct Thing {
120//!     value: Arc<Mutex<String>>,
121//! }
122//!
123//! impl Thing {
124//!     pub fn new(initial_value: String) -> Self {
125//!         let shared_value = Arc::new(Mutex::new(initial_value));
126//!
127//!         linked::new!(Self {
128//!             // Capture `shared_value` to reuse it for all instances in the family.
129//!             value: Arc::clone(&shared_value),
130//!         })
131//!     }
132//!
133//!     pub fn value(&self) -> String {
134//!         self.value.lock().unwrap().clone()
135//!     }
136//!
137//!     pub fn set_value(&self, value: String) {
138//!         *self.value.lock().unwrap() = value;
139//!     }
140//! }
141//! ```
142//!
143//! Note: because this is a contrived example, this type is not very useful as it does not have
144//! any high-efficiency thread-local logic that would benefit from the linked object pattern. See
145//! the [Implementing local behavior][local-behavior] section.
146//!
147//! The implementation steps to apply the pattern to a struct are:
148//!
149//! * Apply [`#[linked::object]`][crate::object] on the struct. This will automatically
150//!   derive the `linked::Object` and `Clone` traits and implement various other behind-the-scenes
151//!   mechanisms required for the linked object pattern to operate.
152//! * In the constructor, call [`linked::new!`][crate::new] to create the first instance.
153//!
154//! [`linked::new!`][crate::new] is a wrapper around a `Self` struct-expression. What makes
155//! it special is that **this struct-expression will be called for every instance that is ever
156//! created in the same family of linked objects**. This expression captures the state of the
157//! constructor (e.g. in the above example, it captures `shared_value`). Use the captured
158//! state to set up any shared connections between instances in the same family (e.g. by sharing
159//! an `Arc` or connecting message channels).
160//!
161//! The captured values must be thread-safe (`Send` + `Sync` + `'static`), while the `Thing`
162//! struct itself does not need to be thread-safe. In fact, the linked object pattern forces
163//! it to be `!Send` and `!Sync` to avoid accidental multithreaded use of a single instance.
164//! See the next chapter to understand how to implement multithreaded logic.
165//!
166//! # Linked objects on multiple threads
167//! [multiple-threads]: #multiple-threads
168//!
169//! Each instance of a linked object is single-threaded (enforced at compile time via `!Send`). This
170//! raises an obvious question: how can I create different instances on different threads if the
171//! instances cannot be sent between threads?
172//!
173//! There are three mechanisms for this:
174//!
175//! * Use a static variable in a [`linked::instance_per_access!`][1] or
176//!   [`linked::instance_per_thread!`][2] block, with the former creating a new linked instance
177//!   on each access and the latter reusing per-thread instances. All instances resolved via such
178//!   static variables are linked to the same family.
179//! * Use a [`linked::PerThread<T>`][3] wrapper to create a thread-local instance of `T`. You can
180//!   freely send the `PerThread<T>` or its clones between threads, unpacking it into a thread-local
181//!   linked instance once the `PerThread<T>` arrives on the destination thread.
182//! * Use a [`Handle`] to transfer an instance between threads. A handle is a thread-safe reference
183//!   to a linked object family, from which instances belonging to that family can be created. This
184//!   is the low-level primitive used by all the other mechanisms internally.
185//!
186//! You can think of a `PerThread<T>` as a special-case `Handle<T>` that always returns the same
187//! instance on the same thread, unlike `Handle<T>` which can be used to create any number of
188//! separate instances on any thread. The purpose of the static variables and `PerThread<T>` is
189//! to minimize the bookkeeping required in user code to manage the linked object instances.
190//!
191//! Example of using a static variable to connect instances on different threads:
192//!
193//! ```rust
194//! # use std::sync::{Arc, Mutex};
195//! # #[linked::object]
196//! # struct Thing {
197//! #     value: Arc<Mutex<String>>,
198//! # }
199//! # impl Thing {
200//! #     pub fn new(initial_value: String) -> Self {
201//! #         let shared_value = Arc::new(Mutex::new(initial_value));
202//! #         linked::new!(Self {
203//! #             value: shared_value.clone(),
204//! #         })
205//! #     }
206//! #     pub fn value(&self) -> String {
207//! #         self.value.lock().unwrap().clone()
208//! #     }
209//! #     pub fn set_value(&self, value: String) {
210//! #         *self.value.lock().unwrap() = value;
211//! #     }
212//! # }
213//! use std::thread;
214//!
215//! linked::instance_per_access!(static THE_THING: Thing = Thing::new("hello".to_string()));
216//!
217//! let thing = THE_THING.get();
218//! assert_eq!(thing.value(), "hello");
219//!
220//! thing.set_value("world".to_string());
221//!
222//! thread::spawn(|| {
223//!     let thing = THE_THING.get();
224//!     assert_eq!(thing.value(), "world");
225//! }).join().unwrap();
226//! ```
227//!
228//! Example of using a [`PerThread<T>`][3] to create thread-local instances on each thread:
229//!
230//! ```rust
231//! # use std::sync::{Arc, Mutex};
232//! # #[linked::object]
233//! # struct Thing {
234//! #     value: Arc<Mutex<String>>,
235//! # }
236//! # impl Thing {
237//! #     pub fn new(initial_value: String) -> Self {
238//! #         let shared_value = Arc::new(Mutex::new(initial_value));
239//! #         linked::new!(Self {
240//! #             value: shared_value.clone(),
241//! #         })
242//! #     }
243//! #     pub fn value(&self) -> String {
244//! #         self.value.lock().unwrap().clone()
245//! #     }
246//! #     pub fn set_value(&self, value: String) {
247//! #         *self.value.lock().unwrap() = value;
248//! #     }
249//! # }
250//! use linked::PerThread;
251//! use std::thread;
252//!
253//! let thing_per_thread = PerThread::new(Thing::new("hello".to_string()));
254//!
255//! // Obtain a local instance on demand.
256//! let thing = thing_per_thread.local();
257//! assert_eq!(thing.value(), "hello");
258//!
259//! thing.set_value("world".to_string());
260//!
261//! // We move the `thing_per_thread` to another thread (you can also just clone it).
262//! thread::spawn(move || {
263//!     let thing = thing_per_thread.local();
264//!     assert_eq!(thing.value(), "world");
265//! }).join().unwrap();
266//! ```
267//!
268//! Example of using a [Handle] to transfer an instance to another thread:
269//!
270//! ```rust
271//! # use std::sync::{Arc, Mutex};
272//! # #[linked::object]
273//! # struct Thing {
274//! #     value: Arc<Mutex<String>>,
275//! # }
276//! # impl Thing {
277//! #     pub fn new(initial_value: String) -> Self {
278//! #         let shared_value = Arc::new(Mutex::new(initial_value));
279//! #         linked::new!(Self {
280//! #             value: shared_value.clone(),
281//! #         })
282//! #     }
283//! #     pub fn value(&self) -> String {
284//! #         self.value.lock().unwrap().clone()
285//! #     }
286//! #     pub fn set_value(&self, value: String) {
287//! #         *self.value.lock().unwrap() = value;
288//! #     }
289//! # }
290//! use linked::Object; // This brings .handle() into scope.
291//! use std::thread;
292//!
293//! let thing = Thing::new("hello".to_string());
294//! assert_eq!(thing.value(), "hello");
295//!
296//! thing.set_value("world".to_string());
297//!
298//! let thing_handle = thing.handle();
299//!
300//! thread::spawn(|| {
301//!     let thing: Thing = thing_handle.into();
302//!     assert_eq!(thing.value(), "world");
303//! }).join().unwrap();
304//! ```
305//!
306//! # Implementing local behavior
307//! [local-behavior]: #local-behavior
308//!
309//! The linked object pattern does not change the fact that synchronized state is expensive.
310//! Whenever possible, linked objects should operate on local state - the entire purpose of this
311//! pattern is to put local behavior front and center and make any sharing require explicit intent.
312//!
313//! Let's extend the above example type with a local counter that counts the number of times
314//! the value has been modified via the current instance. This is local behavior that does not
315//! require any synchronization with other instances.
316//!
317//! ```rust
318//! use std::sync::{Arc, Mutex};
319//!
320//! #[linked::object]
321//! pub struct Thing {
322//!     // Shared state - synchronized with other instances in the family.
323//!     value: Arc<Mutex<String>>,
324//!
325//!     // Local state - not synchronized with other instances in the family.
326//!     update_count: usize,
327//! }
328//!
329//! impl Thing {
330//!     pub fn new(initial_value: String) -> Self {
331//!         let shared_value = Arc::new(Mutex::new(initial_value));
332//!
333//!         linked::new!(Self {
334//!             // Capture `shared_value` to reuse it for all instances in the family.
335//!             value: Arc::clone(&shared_value),
336//!
337//!             // Local state is simply initialized to 0 for every instance.
338//!             update_count: 0,
339//!         })
340//!     }
341//!
342//!     pub fn value(&self) -> String {
343//!         self.value.lock().unwrap().clone()
344//!     }
345//!
346//!     pub fn set_value(&mut self, value: String) {
347//!         *self.value.lock().unwrap() = value;
348//!          self.update_count += 1;
349//!     }
350//!
351//!     pub fn update_count(&self) -> usize {
352//!         self.update_count
353//!     }
354//! }
355//! ```
356//!
357//! Local behavior consists of simply operating on regular non-synchronized fields of the struct.
358//!
359//! However, note that we had to modify `set_value()` to receive `&mut self` instead of the
360//! previous `&self`. This is because we now need to modify a field of the object, so we need
361//! an exclusive `&mut` reference to the instance.
362//!
363//! `&mut self` is not a universally applicable technique - if you are using a linked object in
364//! per-thread mode then all access must be via `&self` shared references because there can be
365//! multiple references simultaneously alive per thread. This means there can be no `&mut self`
366//! and we need to use interior mutability (e.g. [`Cell`][6], [`RefCell`][7], etc.) to support
367//! local behavior together with per-thread instantiation.
368//!
369//! Attempting to use the above example type in a per-thread context will simply mean that the
370//! `set_value()` method cannot be called because there is no way to create a `&mut self` reference.
371//!
372//! Example of the same type using [`Cell`][6] to support local behavior
373//! without `&mut self`:
374//!
375//! ```rust
376//! use std::cell::Cell;
377//! use std::sync::{Arc, Mutex};
378//!
379//! #[linked::object]
380//! pub struct Thing {
381//!     // Shared state - synchronized with other instances in the family.
382//!     value: Arc<Mutex<String>>,
383//!
384//!     // Local state - not synchronized with other instances in the family.
385//!     update_count: Cell<usize>,
386//! }
387//!
388//! impl Thing {
389//!     pub fn new(initial_value: String) -> Self {
390//!         let shared_value = Arc::new(Mutex::new(initial_value));
391//!
392//!         linked::new!(Self {
393//!             // Capture `shared_value` to reuse it for all instances in the family.
394//!             value: Arc::clone(&shared_value),
395//!
396//!             // Local state is simply initialized to 0 for every instance.
397//!             update_count: Cell::new(0),
398//!         })
399//!     }
400//!
401//!     pub fn value(&self) -> String {
402//!         self.value.lock().unwrap().clone()
403//!     }
404//!
405//!     pub fn set_value(&self, value: String) {
406//!         *self.value.lock().unwrap() = value;
407//!          self.update_count.set(self.update_count.get() + 1);
408//!     }
409//!
410//!     pub fn update_count(&self) -> usize {
411//!         self.update_count.get()
412//!     }
413//! }
414//! ```
415//!
416//! # Using linked objects via abstractions
417//!
418//! You may find yourself in a situation where you need to use a linked object type `T` through
419//! a trait object of a trait `Xyz`, where `T: Xyz`. That is, you may want to use your `T` as a
420//! `dyn Xyz`. This is a common pattern in Rust but with the linked objects pattern there is
421//! a choice you must make:
422//!
423//! * If the linked objects are **always** to be accessed via trait objects (`dyn Xyz`), wrap
424//!   the `dyn Xyz` instances in [`linked::Box`], returning such a box already in the constructor.
425//! * If the linked objects are **sometimes** to be accessed via trait objects, you can on-demand
426//!   wrap them into a [`std::boxed::Box<dyn Xyz>`][5].
427//!
428//! The difference is that [`linked::Box`] preserves the linked object functionality even for the
429//! `dyn Xyz` form - you can clone the box, obtain a [`Handle<linked::Box<dyn Xyz>>`][Handle] to
430//! extend the object family to another thread and store such a box in a static variable in a
431//! [`linked::instance_per_access!`][1] or [`linked::instance_per_thread!`][2] block or a
432//! [`PerThread<T>`][3] for automatic instance management.
433//!
434//! In contrast, when you use a [`std::boxed::Box<dyn Xyz>`][5], you lose the linked
435//! object functionality (but only for the instance that you put in the box). Internally, the boxed
436//! instance keeps working as it always did but you cannot use the linked object API on it, such
437//! as obtaining a handle.
438//!
439//! Example of using a linked object via a trait object using [`linked::Box`], for scenarios
440//! where the linked object is always accessed via a trait object:
441//!
442//! ```rust
443//! # trait ConfigSource {}
444//! # impl ConfigSource for XmlConfig {}
445//! // If using linked::Box, do not put `#[linked::object]` on the struct.
446//! // The linked::Box itself is the linked object and our struct is only its contents.
447//! struct XmlConfig {
448//!     config: String
449//! }
450//!
451//! impl XmlConfig {
452//!     pub fn new_as_config_source() -> linked::Box<dyn ConfigSource> {
453//!         // Constructing instances works logically the same as for regular linked objects.
454//!         //
455//!         // The only differences are:
456//!         // 1. We use `linked::new_box!` instead of `linked::new!`
457//!         // 2. There is an additional parameter to the macro to name the trait object type.
458//!         linked::new_box!(
459//!             dyn ConfigSource,
460//!             Self {
461//!                 config: "xml".to_string(),
462//!             }
463//!         )
464//!     }
465//! }
466//! ```
467//!
468//! Example of using a linked object via a trait object using [`std::boxed::Box<dyn Xyz>`][5],
469//! for scenarios where the linked object is only sometimes accessed via a trait object:
470//!
471//! ```rust
472//! # trait ConfigSource {}
473//! # impl ConfigSource for XmlConfig {}
474//! #[linked::object]
475//! struct XmlConfig {
476//!     config: String
477//! }
478//!
479//! impl XmlConfig {
480//!     // XmlConfig itself is a regular linked object, nothing special about it.
481//!     pub fn new() -> XmlConfig {
482//!         linked::new!(
483//!             Self {
484//!                 config: "xml".to_string(),
485//!             }
486//!         )
487//!     }
488//!
489//!     // When the caller wants a `dyn ConfigSource`, we can convert this specific instance into
490//!     // one. The trait object loses its linked objects API surface (though remains part of the
491//!     // family).
492//!     pub fn into_config_source(self) -> Box<dyn ConfigSource> {
493//!         Box::new(self)
494//!     }
495//! }
496//! ```
497//!
498//! # Additional examples
499//!
500//! See `examples/linked_*.rs` for more examples of using linked objects in different scenarios.
501//!
502//! [1]: crate::instance_per_access
503//! [2]: crate::instance_per_thread
504//! [3]: crate::PerThread
505//! [4]: https://doc.rust-lang.org/nomicon/send-and-sync.html
506//! [5]: std::boxed::Box
507//! [6]: std::cell::Cell
508//! [7]: std::cell::RefCell
509
510use simple_mermaid::mermaid;
511
512#[doc(hidden)]
513pub mod __private;
514
515mod r#box;
516mod constants;
517mod handle;
518mod object;
519mod per_access_static;
520mod per_thread;
521mod per_thread_static;
522mod thread_id_hash;
523
524pub use r#box::*;
525pub(crate) use constants::*;
526pub use handle::*;
527pub use object::*;
528pub use per_access_static::*;
529pub use per_thread::*;
530pub use per_thread_static::*;
531pub(crate) use thread_id_hash::*;
532
533mod macros;
534
535/// Marks a struct as implementing the [linked object pattern][crate].
536///
537/// See crate-level documentation for a high-level guide.
538///
539/// # Usage
540///
541/// Apply the attribute on a struct block. Then, in constructors, use the
542/// [`linked::new!`][crate::new] macro to create an instance of the struct.
543///
544/// # Example
545///
546/// ```
547/// #[linked::object]
548/// pub struct TokenCache {
549///     some_value: usize,
550/// }
551///
552/// impl TokenCache {
553///     pub fn new(some_value: usize) -> Self {
554///         linked::new!(Self {
555///             some_value,
556///         })
557///     }
558/// }
559/// ```
560///
561/// # Effects
562///
563/// Applying this macro has the following effects:
564///
565/// 1. Generates the necessary wiring to support calling `linked::new!` in constructors to create
566///    instances. This macro disables regular `Self {}` struct-expressions and requires the use
567///    of `linked::new!`.
568/// 2. Implements `Clone` for the struct. All linked objects can be cloned to create new
569///    instances linked to the same family.
570/// 3. Implements the trait [`linked::Object`] for the struct, enabling standard linked object
571///    pattern mechanisms such as calling `.handle()` on instances to obtain [`Handle`]s.
572/// 4. Implements `From<linked::Handle<T>>` for the struct. This allows converting a [`Handle`]
573///    into an instance of the linked object using `.into()`.
574/// 5. Removes `Send` and `Sync` traits implementation for the struct. Linked objects are single-
575///    threaded and require explicit steps to transfer instances across threads (e.g. via handles).
576///
577/// # Constraints
578///
579/// Only structs defined in the named fields form are supported (no tuple structs).
580pub use linked_macros::__macro_linked_object as object;
581
582// This is so procedural macros can produce code which refers to
583// ::linked::* which will work also in the current crate.
584#[doc(hidden)]
585extern crate self as linked;