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
//! This crate implements a generic construction which allows dynamically resizing a wrapped datastructure, while preserving some key properties.
//!
//! ## Property Preservation
//!
//! ### Progress Guarantees:
//!
//! - **Lock Freedom**: if the wrapped collection is lock-free, all corresponding operations on [`Resizable`] are also lock-free.
//! - **Obstruction Freedom**: if the wrapped collection exposes obstruction-free methods, all corresponding operations on [`Resizable`] are also obstruction-free.
//!
//! [`Resizable::resize`] may block on the allocator.
//!
//! ### Ordering and Consistency Guarantees:
//!
//! - **Relaxed FIFO**: if the wrapped collection has FIFO ordering, [`Resizable`] has **k-FIFO** ordering.
//! - **Linearizability**: if the wrapped collection is linearizable, all operations on [`Resizable`] are also linearizable with respect to its relaxed FIFO specification.
//! - **Empty-Linearizability**: if the wrapped collection is linearizale, all operations on [`Resizable`] are empty-linearizable with respect to the wrapped collections specification.
//!
//! Under FIFO ordering the relaxation is bounded as folows:
//!
//! For any item `x`, its rank displacement is bounded by:
//!
//! **rank_error**<sub>x</sub> ≤ *K*
//!
//! And its delay by:
//!
//! **delay**<sub>x</sub> ≤ *P*
//!
//! where
//! - *K* is the number of concurrent calls to `try_push`
//! - *P* is the number of concurren calls to `try_pop` overlapping with the $K$ executions
//!
//! > [!NOTE]
//! > All ordering claims and relaxation bounds are made in the context of collections with FIFO ordering or no ordering.
//! > Wrapping collections with different specifications may result in unbounded relaxation of those specifications.
//!
//! For the reasoning behind this bound consult the document [`docs/Relaxation.md`](https://github.com/lmeller-git/mpmc-resize/tree/main/docs/Relaxation.md).
//!
//! If no call to [`Resizable::resize`] happens, or in steady-state, [`Resizable`] preserves the ordering specification of the wrapped data structure strictly and is strictly linearizable, given the same holds for the wrapped collection.
//!
//! ## Limitations
//!
//! ### Overhead
//!
//! Preserving lock-free progress guarantees and linearizability across dynamic epoch shifts introduces some operational overhead:
//!
//! - **Steady-State:** In a single-producer single-consumer (SPSC) scenario with no active resizes, wrapping a queue (e.g., `Resizable<ArrayQueue>`) achieves roughly **40% of the throughput** of the raw underlying queue.
//! - **Active Resizing:** Dynamically triggering resizes under load reduces throughput by an additional **~30%** during migration bursts. However, the throughput gained from higher buffer capacity can offset this cost over time.
//! - **Reordering in practice:** Item reordering is rare in practice and depends strongly on the number of threads concurrently invoking [`BoundedCollection::try_pop`] and [`BoundedCollection::try_push`] during an active [`Resizable::resize`].
//!
//! Benchmarks can be found in `benches/main_benchmarks.rs`.
//!
//! ### Resizing Frequency
//!
//! The maximum frequency of successful resizes is currently bounded by the speed with which stale accesses get processed.
//! Before a second resize can happen, all items of the old raw collection must be removed and all accesses must be concluded.
//!
//! If a thread gets permanently preempted while accessing a stale raw collection, no further items get removed, then no resize can happen again.
//!
//! ## Usage
//!
//! To use [`Resizable`] on your type, you must implement [`BoundedCollection`] for it.
//!
//! ```rust
//! # use mpmc_resize::{BoundedCollection, Resizable};
//! # use std::sync::Mutex;
//! # use std::collections::VecDeque;
//! #
//! # struct MyQueue<T> { deque: Mutex<VecDeque<T>>, cap: usize }
//! # impl<T> BoundedCollection for MyQueue<T> {
//! # type Item = T;
//! # fn with_capacity(cap: usize) -> Self { Self { deque: Mutex::new(VecDeque::with_capacity(cap)), cap } }
//! # fn try_push(&self, item: T) -> Result<(), T> {
//! # let mut g = self.deque.lock().unwrap();
//! # if g.len() >= self.cap { Err(item) } else { g.push_back(item); Ok(()) }
//! # }
//! # fn try_pop(&self) -> Option<T> { self.deque.lock().unwrap().pop_front() }
//! # fn len(&self) -> usize { self.deque.lock().unwrap().len() }
//! # fn capacity(&self) -> usize { self.cap }
//! # }
//! // Create a Resizable wrapping your datastructure
//! let container = Resizable::<MyQueue<i32>>::with_capacity(2);
//!
//! // It implements all operations of a BoundedCollection
//! assert!(container.try_push(42).is_ok());
//! assert!(container.try_push(10).is_ok());
//! assert!(container.is_full());
//!
//! // The container can now be dynamically resized, even if the wrapped datastructure cannot
//! assert!(container.resize(4));
//! assert!(!container.is_full());
//!
//! assert!(container.try_push(30).is_ok());
//! assert_eq!(container.try_pop(), Some(42));
//! ```
//!
//! For more info on how to implement [`BoundedCollection`], refer to its documentation.
//!
//! ## Platform Support
//!
//! All platforms supporting native atomic operations are supported.
//!
//! The feature `atomic-fallback` may be used, if no native atomic operations are available.
//!
//! ## Feature Flags
//!
//! - `std`: Enables `std` support.
//! - `atomic-fallback`: Uses the `portable-atomic` fallback feature if native atomics are missing. It is discouraged to use this feature, as fallback atomics internally rely on locks.
//! - `default`: None
//!
//! ## Testing
//!
//! Currently testing is based on:
//!
//! - **Miri** - to validate pointer arithmetic and catch undefined behavior.
//! - **Loom and Shuttle** - to test for race conditions and non-blocking invariants.
//! - **ASan** - to check for memory corruption.
extern crate std;
extern crate alloc;
pub use ;
/// This trait is used to describe a collection that is wrapped by a [`Resizable`].
///
/// Fallible operations on this type may spuriously fail.
///
/// `len`, `is_empty` and `is_full` will never be used for synchronization.