futures_intrusive/lib.rs
1//! Synchronization primitives and utilities based on intrusive collections.
2//!
3//! This crate provides a variety of `Futures`-based and `async/await` compatible
4//! types that are based on the idea of intrusive collections:
5//! - Channels in a variety of flavors:
6//! - Oneshot
7//! - Multi-Producer Multi-Consumer (MPMC)
8//! - State Broadcast
9//! - Synchronization Primitives:
10//! - Manual Reset Event
11//! - Mutex
12//! - Semaphore
13//! - A timer
14//!
15//! ## Intrusive collections?
16//!
17//! In an intrusive collection, the elements that want to get stored inside the
18//! collection provide the means to store themselves inside the collection.
19//! E.g. in an intrusive linked list, each element that gets stored inside the
20//! list contains a pointer field that points to the next list element. E.g.
21//!
22//! ```
23//! // The element which is intended to be stored inside an intrusive container
24//! struct ListElement {
25//! data: u32,
26//! next: *mut ListElement,
27//! }
28//!
29//! // The intrusive container
30//! struct List {
31//! head: *mut ListElement,
32//! }
33//! ```
34//!
35//! The advantage here is that the intrusive collection (here: the list) requires
36//! only a fixed amount of memory. In this case it only needs a pointer to the
37//! first element.
38//!
39//! The list container itself has a fixed size of a single pointer independent
40//! of the number of stored elements.
41//!
42//! Intrusive lists are often used in low-level code like in operating system
43//! kernels. E.g. they can be used for storing elements that represent threads
44//! that are blocked and waiting on queue. In that case the stored elements can
45//! be on the call stack of the caller of each blocked thread, since the
46//! call stack won't change as long as the thread is blocked.
47//!
48//! ### Application in Futures
49//!
50//! This library brings this idea into the world of Rusts `Future`s. Due to the
51//! addition of `Pin`ning, the address of a certain `Future` is not allowed to
52//! change between the first call to `poll()` and when the `Future` is dropped.
53//! This means the data inside the `Future` itself can be inserted into an
54//! intrusive container. If the the call to `Future::poll()` is not immedately
55//! ready, some parts of the `Future` itself are registered in the type which
56//! yielded the `Future`. Each `Future` can store a `Waker`. When the original
57//! type becomes ready, it can iterate through the list of registered `Future`s,
58//! wakeup associated tasks, and potentially remove them from its queue.
59//!
60//! The result is that the future-yielding type is not required to copy an
61//! arbitrary number of `Waker` objects into itself, and thereby does not require
62//! dynamic memory for this task.
63//!
64//! When a `Future` gets destructed/dropped, it must make sure to remove itself
65//! from any collections that refer to it to avoid invalid memory accesses.
66//!
67//! This library implements common synchronization primitives for the usage in
68//! asychronous code based on this concept.
69//!
70//! The implementation requires the usage of a fair chunk of `unsafe`
71//! annotations. However the provided user-level API is intended to be fully safe.
72//!
73//! ## Features of this library
74//!
75//! The following types are currently implemented:
76//! - Channels (oneshot and multi-producer-multi-consumer)
77//! - Synchronization primitives (async mutexes and events)
78//! - Timers
79//!
80//! ## Design goals for the library
81//!
82//! - Provide implementations of common synchronization primitives in a platform
83//! independent fashion.
84//! - Support `no-std` environments. As many types as possible are also provided
85//! for `no-std` environments. The library should boost the ability to use
86//! async Rust code in environments like:
87//! - Microcontrollers (RTOS and bare-metal)
88//! - Kernels
89//! - Drivers
90//! - Avoid dynamic memory allocations at runtime. After objects from this
91//! library have been created, they should not require allocation of any
92//! further memory at runtime. E.g. they should not need to allocate memory
93//! for each call to an asynchronous function or each time a new task accesses
94//! the same object in parallel.
95//! - Offer familiar APIs.
96//! The library tries to mimic the APIs of existing Rust libraries like the
97//! standard library and `futures-rs` as closely as possible.
98//!
99//! ## Non goals
100//!
101//! - Provide IO primitives (like sockets), or platform specific implementations.
102//! - Reach the highest possible performance in terms of throughput and latency.
103//! While code in this library is optimized for performance, portability
104//! and deterministic memory usage are more important goals.
105//! - Provide future wrappers for platform-specific APIs.
106//!
107//! ## Local, Non-local and shared flavors
108//!
109//! The library provides types in a variety of flavors:
110//!
111//! - A local flavor (e.g. [`channel::LocalChannel`])
112//! - A non-local flavor (e.g. [`channel::Channel`])
113//! - A shared flavor (e.g. [`channel::shared::Sender`])
114//! - A generic flavor (e.g. [`channel::GenericChannel`] and
115//! [`channel::shared::GenericSender`])
116//!
117//! The difference between these types lie in their thread-safety. The non-local
118//! flavors of types can be accessed from multiple threads (and thereby also
119//! futures tasks) concurrently. This means they implement the `Sync` trait in
120//! addition to the `Send` trait.
121//! The local flavors only implement the `Send` trait.
122//!
123//! ### Local flavor
124//!
125//! The local flavors will require no internal synchronization (e.g. internal
126//! Mutexes) and can therefore be provided for all platforms (including `no-std`).
127//! Due the lack of required synchronization, they are also very fast.
128//!
129//! It might seem counter-intuitive to provide synchronization primitives that
130//! only work within a single task. However there are a variety of applications
131//! where these can be used to coordinate sub-tasks (futures that are polled on
132//! a single task concurrently).
133//!
134//! The following example demonstrates this use-case:
135//!
136//! ```
137//! # use futures::join;
138//! # use futures_intrusive::sync::LocalManualResetEvent;
139//! async fn async_fn() {
140//! let event = LocalManualResetEvent::new(false);
141//! let task_a = async {
142//! // Wait for the event
143//! event.wait().await;
144//! // Do something with the knowledge that task_b reached a certain state
145//! };
146//! let task_b = async {
147//! // Some complex asynchronous workflow here
148//! // ...
149//! // Signal task_a
150//! event.set();
151//! };
152//! join!(task_a, task_b);
153//! }
154//! ```
155//!
156//! ### Non-local flavor
157//!
158//! The non-local flavors can be used between arbitrary tasks and threads. They
159//! use internal synchronization for this in form of an embedded `Mutex` of
160//! [`parking_lot::Mutex`] type.
161//!
162//! The non-local flavors are only available in `alloc` environments.
163//!
164//! ### Shared flavor
165//!
166//! For some types a shared flavor is provided. Non-local flavors of types are
167//! `Sync`, but they still can only be shared by reference between various tasks.
168
169//! Shared flavors are also `Sync`, but the types additionally implement the
170//! `Clone` trait, which allows duplicating the object, and passing ownership of
171//! it to a different task. These types allow avoiding references (and thereby
172//! lifetimes) in some scenarios, which makes them more convenient to use. The
173//! types also return `Future`s which do not have an associated lifetime. This
174//! allows using those types as implementations of traits without the need for
175//! generic associated types (GATs).
176//!
177//! Due to the requirement of atomic reference counting, these types are
178//! currently only available for `alloc` environments.
179//!
180//! ### Generic flavor
181//!
182//! The generic flavors of provided types are parameterized around a
183//! [`lock_api::RawMutex`] type. These form the base for the non-local and shared
184//! flavors which simply parameterize the generic flavor in either a
185//! non-thread-safe or thread-safe fashion.
186//!
187//! Users can directly use the generic flavors to adapt the provided thread-safe
188//! types for use in `no-std` environments.
189//!
190//! E.g. by providing a custom [`lock_api::RawMutex`]
191//! implementation, the following platforms can be supported:
192//!
193//! - For RTOS platforms, RTOS-specific mutexes can be wrapped.
194//! - For kernel development, spinlock based mutexes can be created.
195//! - For embedded development, mutexes which just disable interrupts can be
196//! utilized.
197//!
198//!
199//! ## Relation to types in other libraries
200//!
201//! Other libraries (e.g. `futures-rs` and `tokio`) provide many primitives that
202//! are comparable feature-wise to the types in this library.
203//!
204//! The most important differences are:
205//! - This library has a bigger focus on `no-std` environments, and does not
206//! only try to provide an implementation for `alloc` or `std`.
207//! - The types in this library do not require dynamic memory allocation for
208//! waking up an arbitrary number of tasks waiting on a particular
209//! `Future`. Other libraries typically require heap-allocated nodes of
210//! growing vectors for handling a varying number of tasks.
211//! - The `Future`s produced by this library are all `!Unpin`, which might make
212//! them less ergonomic to use.
213//!
214
215#![cfg_attr(not(feature = "std"), no_std)]
216#![warn(missing_docs, missing_debug_implementations)]
217#![deny(bare_trait_objects)]
218
219#[cfg(feature = "alloc")]
220extern crate alloc;
221
222mod noop_lock;
223use noop_lock::NoopLock;
224
225pub mod buffer;
226
227#[allow(dead_code)]
228mod intrusive_double_linked_list;
229mod intrusive_pairing_heap;
230
231pub mod channel;
232pub mod sync;
233pub mod timer;
234
235mod utils;