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
//! A simple and correct implementation of Mellor-Crummey and Scott
//! contention-free [lock] for mutual exclusion, referred to as MCS lock.
//!
//! MCS lock is a List-Based Queuing Lock that avoids network contention by
//! having threads spin on local memory locations. The main properties of this
//! mechanism are:
//!
//! - guarantees FIFO ordering of lock acquisitions;
//! - spins on locally-accessible flag variables only;
//! - requires a small constant amount of space per lock; and
//! - works equally well (requiring only O(1) network transactions per lock
//! acquisition) on machines with and without coherent caches.
//!
//! This algorithm and serveral others were introduced by
//! [Mellor-Crummey and Scott] paper. And a simpler correctness proof of the
//! MCS lock was proposed by [Johnson and Harathi].
//!
//! ## Spinlock use cases
//!
//! It is noteworthy to mention that [spinlocks are usually not what you want].
//! The majority of use cases are well covered by OS-based mutexes like
//! [`std::sync::Mutex`], [`parking_lot::Mutex`]. These implementations will
//! notify the system that the waiting thread should be parked, freeing the
//! processor to work on something else.
//!
//! Spinlocks are only efficient in very few circunstances where the overhead
//! of context switching or process rescheduling are greater than busy waiting
//! for very short periods. Spinlocks can be useful inside operating-system kernels,
//! on embedded systems or even complement other locking designs. As a reference
//! use case, some [Linux kernel mutexes] run an customized MCS lock specifically
//! tailored for optimistic spinning during contention before actually sleeping.
//! This implementation is `no_std` by default, so it's useful in those environments.
//!
//! ## Locking with a raw MCS spinlock
//!
//! This implementation operates under FIFO. Raw locking APIs require exclusive
//! access to a locally accessible queue node. This node is represented by the
//! [`raw::MutexNode`] type. Callers are responsible for instantiating the queue
//! nodes themselves. This implementation is `no_std` compatible. See the [`raw`]
//! module for more information.
//!
//! ```
//! use std::sync::Arc;
//! use std::thread;
//!
//! // Simply spins during contention.
//! use mcslock::raw::{spins::Mutex, MutexNode};
//!
//! let mutex = Arc::new(Mutex::new(0));
//! let c_mutex = Arc::clone(&mutex);
//!
//! thread::spawn(move || {
//! // A queue node must be mutably accessible.
//! // Critical section must be defined as a closure.
//! let mut node = MutexNode::new();
//! c_mutex.lock_with_then(&mut node, |data| *data = 10);
//! })
//! .join().expect("thread::spawn failed");
//!
//! // A node may also be transparently allocated in the stack.
//! // Critical section must be defined as a closure.
//! assert_eq!(mutex.try_lock_then(|data| *data.unwrap()), 10);
//! ```
//!
//! ## Thread local queue nodes
//!
//! [`raw::Mutex`] supports locking APIs that access queue nodes that are stored
//! in the thread local storage. These locking APIs require a static reference
//! to a [`raw::LocalMutexNode`] key. Keys must be generated by the
//! [`thread_local_node!`] macro. Thread local nodes are not `no_std` compatible
//! and can be enabled through the `thread_local` feature.
//!
//! ```
//! # #[cfg(feature = "thread_local")]
//! # {
//! use std::sync::Arc;
//! use std::thread;
//!
//! // Simply spins during contention.
//! use mcslock::raw::spins::Mutex;
//!
//! // Requires `thread_local` feature.
//! mcslock::thread_local_node!(static NODE);
//!
//! let mutex = Arc::new(Mutex::new(0));
//! let c_mutex = Arc::clone(&mutex);
//!
//! thread::spawn(move || {
//! // Local node handles are provided by reference.
//! // Critical section must be defined as a closure.
//! c_mutex.lock_with_local_then(&NODE, |data| *data = 10);
//! })
//! .join().expect("thread::spawn failed");
//!
//! // A node may also be transparently allocated in the stack.
//! // Critical section must be defined as a closure.
//! assert_eq!(mutex.try_lock_then(|data| *data.unwrap()), 10);
//! # }
//! # #[cfg(not(feature = "thread_local"))]
//! # fn main() {}
//! ```
//!
//! ## Locking with a barging MCS spinlock
//!
//! This implementation will have non-waiting threads race for the lock against
//! the front of the waiting queue thread, which means this it is an unfair lock.
//! This implementation can be enabled through the `barging` feature, it is
//! suitable for `no_std` environments, and the locking APIs are compatible with
//! the `lock_api` crate. See [`barging`] and [`lock_api`] modules for
//! more information.
//!
//! ```
//! # #[cfg(feature = "barging")]
//! # {
//! use std::sync::Arc;
//! use std::thread;
//!
//! // Requires `barging` feature.
//! // Spins with exponential backoff during contention.
//! use mcslock::barging::spins::backoff::Mutex;
//!
//! let mutex = Arc::new(Mutex::new(0));
//! let c_mutex = Arc::clone(&mutex);
//!
//! thread::spawn(move || {
//! *c_mutex.try_lock().unwrap() = 10;
//! })
//! .join().expect("thread::spawn failed");
//!
//! assert_eq!(*mutex.lock(), 10);
//! # }
//! # #[cfg(not(feature = "barging"))]
//! # fn main() {}
//! ```
//!
//! ## Features
//!
//! This crate dos not provide any default features. Features that can be enabled
//! are:
//!
//! ### yield
//!
//! The `yield` feature requires linking to the standard library, so it is not
//! suitable for `no_std` environments. By enabling the `yield` feature, instead
//! of busy-waiting during lock acquisitions and releases, this will call
//! [`std::thread::yield_now`], which cooperatively gives up a timeslice to the
//! OS scheduler. This may cause a context switch, so you may not want to enable
//! this feature if your intention is to to actually do optimistic spinning. The
//! default implementation calls [`core::hint::spin_loop`], which does in fact
//! just simply busy-waits. This feature is not `not_std` compatible.
//!
//! ### thread_local
//!
//! The `thread_local` feature enables [`raw::Mutex`] locking APIs that operate
//! over queue nodes that are stored at the thread local storage. These locking
//! APIs require a static reference to [`raw::LocalMutexNode`] keys. Keys must be
//! generated by the [`thread_local_node!`] macro. This feature also enables memory
//! optimizations for [`barging::Mutex`] locking operations. This feature is not
//! `no_std` compatible.
//!
//! This feature is not `no_std` compatible.
//!
//! ### barging
//!
//! The `barging` feature provides locking APIs that are compatible with the
//! [lock_api] crate. It does not require node allocations from the caller. The
//! [`barging`] module is suitable for `no_std` environments. This implementation
//! is not fair (does not guarantee FIFO), but can improve throughput when the lock
//! is heavily contended.
//!
//! ### lock_api
//!
//! This feature implements the [`RawMutex`] trait from the [lock_api]
//! crate for [`barging::Mutex`]. Aliases are provided by the [`barging::lock_api`]
//! (`no_std`) module.
//!
//! ## Related projects
//!
//! These projects provide MCS lock implementations with different APIs,
//! implementation details or compiler requirements, you can check their
//! repositories:
//!
//! - mcs-rs: <https://github.com/gereeter/mcs-rs>
//! - libmcs: <https://github.com/topecongiro/libmcs>
//!
//! [lock]: https://en.wikipedia.org/wiki/Lock_(computer_science)
//! [Mellor-Crummey and Scott]: https://www.cs.rochester.edu/~scott/papers/1991_TOCS_synch.pdf
//! [Johnson and Harathi]: https://web.archive.org/web/20140411142823/http://www.cise.ufl.edu/tr/DOC/REP-1992-71.pdf
//! [spinlocks are usually not what you want]: https://matklad.github.io/2020/01/02/spinlocks-considered-harmful.html
//! [Linux kernel mutexes]: https://www.kernel.org/doc/html/latest/locking/mutex-design.html
//!
//! [`parking_lot::Mutex`]: https://docs.rs/parking_lot/latest/parking_lot/type.Mutex.html
//! [lock_api]: https://docs.rs/lock_api/latest/lock_api
//! [`RawMutex`]: https://docs.rs/lock_api/latest/lock_api/trait.RawMutex.html
//! [`RawMutexFair`]: https://docs.rs/lock_api/latest/lock_api/trait.RawMutexFair.html
extern crate std;
pub
pub
pub
pub
pub
pub