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
//! A construction that elastically relaxes a given collection.
//!
//! `Kasino` aims to improve performance of concurrent datastructures by sharding operations into multiple subqueues.
//! This process introduces a relaxation of the wrapped datastructure, the specifics depending on the used strategy.
//!
//! Strategies optimize for performance and relaxation bounds, but can be implemented to optimize for other properties.
//!
//! Multiple strategies, amenable to different kinds of datastructures and requirements are provided.
//!
//! Additionally an interface for defining custom strategies is available.
//!
//! ## Usage
//!
//! ```rust
//! # use kasino::{Collection, WithCapacity, Signature, components::{TryPushSignature, PopSignature}};
//! # use std::sync::Mutex;
//! # use std::collections::VecDeque;
//! # use std::marker::PhantomData;
//! # struct QueuePushSignature<T>(PhantomData<T>);
//! # struct MyQueue<T> { deque: Mutex<VecDeque<T>>, cap: usize }
//! # impl<T> Collection for MyQueue<T> {
//! # type PollSignature = PopSignature<T>;
//! # type OfferSignature = TryPushSignature<T>;
//! # fn offer<'input, 'arm>(
//! # &'arm self,
//! # item: <Self::OfferSignature as Signature>::Input<'input>,
//! # ) -> Result<
//! # <Self::OfferSignature as Signature>::Output<'input, 'arm>,
//! # <Self::OfferSignature as Signature>::Error<'input, 'arm>,
//! # > {
//! # let mut g = self.deque.lock().unwrap();
//! # if g.len() >= self.cap { Err(item) } else { g.push_back(item); Ok(()) }
//! # }
//! # fn poll<'input, 'arm>(
//! # &'arm self,
//! # input: <Self::PollSignature as Signature>::Input<'input>,
//! # ) -> Result<
//! # <Self::PollSignature as Signature>::Output<'input, 'arm>,
//! # <Self::PollSignature as Signature>::Error<'input, 'arm>,
//! # > {
//! # self.deque.lock().unwrap().pop_front().ok_or(())
//! # }
//! # fn len(&self) -> usize { self.deque.lock().unwrap().len() }
//! # fn capacity(&self) -> usize { self.cap }
//! # }
//! # impl<T, const N: usize> WithCapacity<N> for MyQueue<T> {
//! # fn with_capacity() -> Self { Self { deque: Mutex::new(VecDeque::with_capacity(N)), cap: N } }
//! # }
//! use kasino::{InlineBandit, strategy::DCBO};
//!
//! let bandit = InlineBandit::<MyQueue<i32>, DCBO, 8>::new();
//!
//! let mut handle = bandit.buy_in();
//! let mut handle2 = handle.fork();
//!
//! assert!(handle.offer(42).is_ok());
//! assert!(handle2.offer(10).is_ok());
//! assert!(handle.poll(()).is_ok());
//! ```
//!
//! ## Property preservation
//!
//! ### Progress Guarantees:
//!
//! - **Lock Freedom**: if the wrapped collection is lock-free, `Bandits` are also lock-free.
//! - **Obstruction Freedom**: if the wrapped collection exposes obstruction-free methods, all corresponding operations on `Bandits` are also obstruction-free.
//!
//! ### Ordering and Consistency Guarantees:
//!
//! - **Relaxed Specification**: if the wrapped collection has some specification, `Bandits` relax that specification based on the chosen strategy.
//! - **Linearizability**: if the wrapped collection is linearizable, all operations on `Bandits` are also linearizable with respect to their relaxed specification.
//!
//! ### Relaxation
//!
//! The rank error and delay are in general unbounded. However, the rank error and delay of some strategies are bounded with high probability.
//! The exact bounds here are differing across different strategies.
//!
//! For more information refer to the strategies documentation and the reference papers.
//!
//! For an empirical analysis of the rank errors, refer to [relaxed-queue-simulations](https://github.com/lmeller-git/relaxed-queue-simulations).
//!
//! ## Performance
//!
//! Sharding operations to multiple sub-collections incurs both memory cost, as well as additional overhead. Under low contention `Kasino` is slower than the raw collection.
//!
//! However, scheduling thread access across multiple sub-collections allows to reduce cache-line invalidation at high contention, improving performance as thread count increases.
//!
//! ## Limitations
//!
//! - Currently an instantiated `Bandit` cannot be resized. Its capacity is fixed at construction time.
//! - The capacity of each sub-collection is fixed statically. The total capacity of a `Bandit` is constrained to a multiple of this.
//!
//! ## Advanced Usage
//!
//! The interfaces for [`Collection`], [`strategy::Strategy`] and `Bandit` are general enough to support the implementation of a large set of datastructures. For examples of this consult `examples/`.
//!
//! ## 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.
//! - `instrumented`: Adds telemetry collection to strategies
//! - `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.
//!
//! ## References
//!
//! - Performance, Scalability, and Semantics of Concurrent FIFO Queues, Kirsch et al.
//! - Balanced Allocations over Efficient Queues: A Fast Relaxed FIFO Queue, Geijer et al.
extern crate std;
extern crate alloc;
pub use *;
pub use BanditHandle;
pub use *;
/// Description about the signature of a failable method
/// The interface for a generic data structure.
/// A collection that may be created with a static initial capacity N