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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
//! Synchronization primitives and utilities based on intrusive collections.
//!
//! This crate provides a variety of `Futures`-based and `async/await` compatible
//! types that are based on the idea of intrusive collections:
//! - Channels in a variety of flavors:
//!   - Oneshot
//!   - Multi-Producer Multi-Consumer (MPMC)
//!   - State Broadcast
//! - Synchronization Primitives:
//!   - Manual Reset Event
//!   - Mutex
//!   - Semaphore
//! - A timer
//!
//! ## Intrusive collections?
//!
//! In an intrusive collection, the elements that want to get stored inside the
//! collection are-providing the means to store themselves inside the collection.
//! E.g. in an intrusive linked list, each element that gets stored inside the
//! list contains pointer field that points to the next list element. E.g.
//!
//! ```
//! // The element which is intended to be stored inside an intrusive container
//! struct ListElement {
//!    data: u32,
//!    next: *mut ListElement,
//! }
//!
//! // The intrusive container
//! struct List {
//!     head: *mut ListElement,
//! }
//! ```
//!
//! The advantage here is that the intrusive collection (here: the list) requires
//! only a fixed amount of memory. In this case it only needs a pointer to the
//! first element.
//!
//! The list container itself has a fixed size of a single pointer, independent
//! of the number of stored elements.
//!
//! Intrusive lists are often used low-level code like in operating system kernels.
//! E.g. they can be used for storing elements that represent threads that are
//! blocked and waiting on queue.
//! In that case the stored elements can be on the call-stack of the
//! caller of each blocked thread, since the call-stack won't change as long as
//! the thread is blocked.
//!
//! ### Application in Futures
//!
//! This library brings this idea into the world of Rusts `Future`s. Due to the
//! addition of `Pin`ning, the address of a certain `Future` is not allowed to
//! change between the first call to `poll()` until the `Future` is dropped.
//! This means the data inside the `Future` itself can be inserted into an
//! intrusive container. If the the call to `Future::poll()` is not immedately
//! ready, some parts of the `Future` itself are registered at the type which
//! yielded the `Future`. Each `Future` can store a `Waker`. When the original
//! type gets ready, it can iterate through the list of registered `Future`s,
//! wakeup associated tasks, and potentially remove them from it's queue.
//!
//! The result is that the future-yielding type is not required to copy an
//! arbitrary amount of `Waker` objects into itself, and thereby does not require
//! dynamic memory for this task.
//!
//! When a `Future` gets destructed/dropped, it must make sure to remove itself
//! from any collections that refer to it, to avoid invalid memory accesses.
//!
//! This library implements common synchronization primitives for the usage in
//! asychronous code based on this concept.
//!
//! The implementation requires the usage of a fair chunk of of `unsafe`
//! annotations. However the provided user-level API is intended to be fully safe.
//!
//! ## Features of this library
//!
//! The following types are currently implemented:
//! - Channels (oneshot and multi-producer-multi-consumer)
//! - Synchronization primitives (async mutexes and events)
//! - Timers
//!
//! ## Design goals for the library
//!
//! - Providing implementations of common synchronization primitives in a
//!   platform independent fashion.
//! - Support for `no-std` environments. As many types as possible are also
//!   provided for `no-std` environments. The library should boost the ability
//!   to use async Rust code in environments like
//!   - Microcontrollers (RTOS and bare-metal)
//!   - Kernels
//!   - Drivers
//! - Avoidance of dynamic memory allocations during runtime.
//!   After objects from this library have been created, they should not require
//!   the allocation of any further memory during runtime.
//!   E.g. they should not require to allocate memory for each call to an
//!   asynchronous function, or each time a new task accesses the same object
//!   in parallel.
//! - Familiar APIs.
//!   The library tries to mimic the APIs of existing Rust libraries like the
//!   standard library and and `futures-rs` as closely as possible.
//!
//! ## Non goals
//!
//! - Providing IO primitives (like) sockets, or platform specific implementations
//! - Reaching the highest possible performance in terms of throughput and latency.
//!   While code inside this library is optimized for performance, portability
//!   and deterministic memory usage are more important goals.
//! - Providing future wrappers for platform-specific APIs.
//!
//! ## Local, Non-local and shared flavors
//!
//! The library provides types in a variety of flavors:
//!
//! - A local flavor (e.g. [`channel::LocalChannel`])
//! - A non-local flavor (e.g. [`channel::Channel`])
//! - A shared flavor (e.g. [`channel::shared::Sender`])
//! - A generic flavor (e.g. [`channel::GenericChannel`] and
//!   [`channel::shared::GenericSender`])
//!
//! The difference between the types lie in their thread-safety. The non-local
//! flavors of types can be accessed from multiple threads (and thereby also
//! futures tasks) concurrently. This means they implement the `Sync` trait in
//! addition to the `Send` trait.
//! The local flavors only implement the `Send` trait.
//!
//! ### Local flavor
//!
//! The local flavors will require no internal synchronization (e.g. internal
//! Mutexes) and can therefore be provided for all platforms (including `no-std`).
//! Due the lack of required synchronization, they are also very fast.
//!
//! It might seem counter-intuitive to provide synchronization primitives that
//! only work within a single task. However there is a variety of applications
//! where those can be used to coordinate sub-tasks (futures that are polled on
//! a single task concurrently).
//!
//! The following example demonstrates this use-case:
//!
//! ```
//! # #![feature(async_await)]
//! # use futures::join;
//! # use futures_intrusive::sync::LocalManualResetEvent;
//! async fn async_fn() {
//!     let event = LocalManualResetEvent::new(false);
//!     let task_a = async {
//!         // Wait for the event
//!         event.wait().await;
//!         // Do something with the knowledge that task_b reached a certain state
//!     };
//!     let task_b = async {
//!         // Some complex asynchronous workflow here
//!         // ...
//!         // Signal task_a
//!         event.set();
//!     };
//!     join!(task_a, task_b);
//! }
//! ```
//!
//! ### Non-local flavor
//!
//! The non-local flavors can be used between arbitrary tasks and threads.
//! They use internal synchronization for this in form of an embedded `Mutex` in
//! the form of a [`parking_lot::Mutex`] type.
//!
//! The non-local flavors are only available in `std` environments.
//!
//! ### Shared flavor
//!
//! For some types also a shared flavor of the type is provided. Non-local
//! flavors of types are `Sync`, but still can only be shared by reference
//! between various tasks.
//! Shared flavors are also `Sync`, but the types do also implement the `Copy`
//! trait, which allows to duplicate the object, and pass the ownership of it
//! to a different task. These types allow to avoid references (and thereby
//! lifetimes) in some scenarios, which makes them more convenient to use.
//! The types also return `Future`s which do not have an associated lifetime.
//! This allows to use those types as implementations of traits, without the
//! need for generic associated types (GATs).
//!
//! Due to the requirement of atomic reference counting, those types are
//! currently only available for `std` environments.
//!
//! ### Generic flavor
//!
//! The generic flavors of provided types are parameterized around a
//! [`lock_api::RawMutex`] type. The form the base for the non-local and shared
//! flavors, which just parameterize the generic flavor in either a non
//! thread-safe or thread-safe fashion.
//!
//! Users have the opportunity to directly make use generic flavors, in order to
//! adapt the provided thread-safe types for the use in `no-std` environments.
//!
//! E.g. by providing a custom [`lock_api::RawMutex`]
//! implementation, the following platforms can be supported:
//!
//! - For RTOS platform, RTOS-specific mutexes can be wrapped.
//! - For Kernel development, spinlock based mutexes can be created.
//! - For embedded development, mutexes which just disable interrupts can be
//!   utilized.
//!
//!
//! ## Relation to types in other libraries
//!
//! Other libraries (e.g. `futures-rs` and `tokio`) are already providing
//! a lot of primitives that are comparable feature-wise to the types in this
//! library.
//!
//! The most important differences are:
//! - This library has a bigger focus on `no-std` environments, and does not
//!   only try to provide an implementation for `std`.
//! - The types in this library do not require dynamic memory allocation for
//!   waking up an arbitrary amount of tasks that are waiting on a particular
//!   `Future`. Other libraries typically require heap-allocated nodes for
//!   growing vectors for handling a varying amount of tasks.
//! - The `Future`s produced by this library are all `!Unpin`, which might make
//!   them less ergonomic to use.
//!

#![cfg_attr(not(feature = "std"), no_std)]

#![warn(missing_docs, missing_debug_implementations)]
#![deny(bare_trait_objects)]

mod noop_lock;
use noop_lock::NoopLock;

pub mod buffer;

mod intrusive_singly_linked_list;
#[allow(dead_code)]
mod intrusive_double_linked_list;

pub mod sync;
pub mod channel;
pub mod timer;