Skip to content

Commit 420d606

Browse files
committed
Add: SpinMutex for Rust
1 parent 3394386 commit 420d606

File tree

1 file changed

+261
-0
lines changed

1 file changed

+261
-0
lines changed

rust/lib.rs

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,270 @@ extern crate std;
1717
#[cfg(feature = "std")]
1818
use std::ffi::CStr;
1919

20+
use core::cell::UnsafeCell;
2021
use core::ffi::{c_char, c_int, c_void};
2122
use core::ptr::NonNull;
2223
use core::slice;
24+
use core::sync::atomic::{AtomicBool, Ordering};
25+
26+
/// A generic spin mutex that uses CPU-specific pause instructions for efficient busy-waiting.
27+
///
28+
/// This is a low-level synchronization primitive that spins on a busy loop rather than
29+
/// blocking the thread. It's most appropriate for very short critical sections where
30+
/// the cost of context switching would be higher than busy-waiting.
31+
///
32+
/// The generic parameter `P` allows customization of the pause behavior:
33+
/// - `true` enables CPU-specific pause instructions (recommended for most use cases)
34+
/// - `false` disables pause instructions (may be useful in some specialized scenarios)
35+
///
36+
/// # Examples
37+
///
38+
/// ```rust
39+
/// use fork_union::*;
40+
///
41+
/// // Create a spin mutex with pause instructions enabled
42+
/// let mutex = BasicSpinMutex::<i32, true>::new(42);
43+
///
44+
/// // Lock, access data, and unlock
45+
/// {
46+
/// let mut guard = mutex.lock();
47+
/// *guard = 100;
48+
/// } // Lock is automatically released when guard goes out of scope
49+
///
50+
/// // Verify the value was changed
51+
/// assert_eq!(*mutex.lock(), 100);
52+
/// ```
53+
///
54+
/// # Performance Characteristics
55+
///
56+
/// - **Very fast for short critical sections** - no syscalls or context switches
57+
/// - **CPU-efficient busy-waiting** - uses pause instructions when `P = true`
58+
/// - **Memory efficient** - only requires a single atomic bool plus the protected data
59+
/// - **Can cause high CPU usage** - spins continuously until lock is acquired
60+
/// - **Not fair** - no guarantee of acquisition order
61+
///
62+
/// # When to Use
63+
///
64+
/// Use `BasicSpinMutex` when:
65+
/// - Critical sections are very short (microseconds)
66+
/// - Lock contention is low
67+
/// - You need the absolute minimum latency
68+
/// - You're in a no_std environment
69+
///
70+
/// Avoid `BasicSpinMutex` when:
71+
/// - Critical sections are long (milliseconds or more)
72+
/// - Lock contention is high
73+
/// - You need fairness guarantees
74+
/// - Power consumption is a concern
75+
pub struct BasicSpinMutex<T, const PAUSE: bool> {
76+
locked: AtomicBool,
77+
data: UnsafeCell<T>,
78+
}
79+
80+
impl<T, const PAUSE: bool> BasicSpinMutex<T, PAUSE> {
81+
/// Creates a new spin mutex in the unlocked state.
82+
///
83+
/// # Arguments
84+
///
85+
/// * `data` - The value to be protected by the mutex
86+
///
87+
/// # Examples
88+
///
89+
/// ```rust
90+
/// use fork_union::*;
91+
///
92+
/// let mutex = BasicSpinMutex::<i32, true>::new(0);
93+
/// ```
94+
pub const fn new(data: T) -> Self {
95+
Self {
96+
locked: AtomicBool::new(false),
97+
data: UnsafeCell::new(data),
98+
}
99+
}
100+
101+
/// Acquires the lock, returning a guard that provides access to the protected data.
102+
///
103+
/// This method will spin until the lock is acquired. If the lock is already held,
104+
/// it will busy-wait using CPU-specific pause instructions (if `PAUSE = true`).
105+
///
106+
/// # Examples
107+
///
108+
/// ```rust
109+
/// use fork_union::*;
110+
///
111+
/// let mutex = BasicSpinMutex::<i32, true>::new(0);
112+
/// let mut guard = mutex.lock();
113+
/// *guard = 42;
114+
/// ```
115+
pub fn lock(&self) -> BasicSpinMutexGuard<T, PAUSE> {
116+
while self
117+
.locked
118+
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
119+
.is_err()
120+
{
121+
// Busy-wait with pause instructions if enabled
122+
if PAUSE {
123+
core::hint::spin_loop();
124+
}
125+
}
126+
BasicSpinMutexGuard { mutex: self }
127+
}
128+
129+
/// Attempts to acquire the lock without blocking.
130+
///
131+
/// Returns `Some(guard)` if the lock was successfully acquired, or `None` if
132+
/// the lock is currently held by another thread.
133+
///
134+
/// # Examples
135+
///
136+
/// ```rust
137+
/// use fork_union::*;
138+
///
139+
/// let mutex = BasicSpinMutex::<i32, true>::new(0);
140+
///
141+
/// if let Some(mut guard) = mutex.try_lock() {
142+
/// *guard = 42;
143+
/// println!("Lock acquired and value set");
144+
/// } else {
145+
/// println!("Lock is currently held by another thread");
146+
/// };
147+
/// ```
148+
pub fn try_lock(&self) -> Option<BasicSpinMutexGuard<T, PAUSE>> {
149+
if self
150+
.locked
151+
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
152+
.is_ok()
153+
{
154+
Some(BasicSpinMutexGuard { mutex: self })
155+
} else {
156+
None
157+
}
158+
}
159+
160+
/// Checks if the mutex is currently locked.
161+
///
162+
/// This method provides a non-blocking way to check the lock state, but should
163+
/// be used carefully as the state can change immediately after this call returns.
164+
///
165+
/// # Examples
166+
///
167+
/// ```rust
168+
/// use fork_union::*;
169+
///
170+
/// let mutex = BasicSpinMutex::<i32, true>::new(0);
171+
/// assert!(!mutex.is_locked());
172+
///
173+
/// {
174+
/// let _guard = mutex.lock();
175+
/// assert!(mutex.is_locked());
176+
/// }
177+
///
178+
/// assert!(!mutex.is_locked());
179+
/// ```
180+
pub fn is_locked(&self) -> bool {
181+
self.locked.load(Ordering::Acquire)
182+
}
183+
184+
/// Consumes the mutex and returns the protected data.
185+
///
186+
/// This method bypasses the locking mechanism entirely since we have exclusive
187+
/// ownership of the mutex.
188+
///
189+
/// # Examples
190+
///
191+
/// ```rust
192+
/// use fork_union::*;
193+
///
194+
/// let mutex = BasicSpinMutex::<i32, true>::new(42);
195+
/// let data = mutex.into_inner();
196+
/// assert_eq!(data, 42);
197+
/// ```
198+
pub fn into_inner(self) -> T {
199+
self.data.into_inner()
200+
}
201+
202+
/// Gets a mutable reference to the protected data.
203+
///
204+
/// Since this requires a mutable reference to the mutex, no locking is needed
205+
/// as we have exclusive access.
206+
///
207+
/// # Examples
208+
///
209+
/// ```rust
210+
/// use fork_union::*;
211+
///
212+
/// let mut mutex = BasicSpinMutex::<i32, true>::new(0);
213+
/// *mutex.get_mut() = 42;
214+
/// assert_eq!(*mutex.lock(), 42);
215+
/// ```
216+
pub fn get_mut(&mut self) -> &mut T {
217+
self.data.get_mut()
218+
}
219+
}
220+
221+
// Safety: BasicSpinMutex can be sent between threads if T can be sent
222+
unsafe impl<T: Send, const PAUSE: bool> Send for BasicSpinMutex<T, PAUSE> {}
223+
// Safety: BasicSpinMutex can be shared between threads if T can be sent
224+
unsafe impl<T: Send, const PAUSE: bool> Sync for BasicSpinMutex<T, PAUSE> {}
225+
226+
/// A guard providing access to the data protected by a `BasicSpinMutex`.
227+
///
228+
/// The lock is automatically released when this guard is dropped.
229+
pub struct BasicSpinMutexGuard<'a, T, const PAUSE: bool> {
230+
mutex: &'a BasicSpinMutex<T, PAUSE>,
231+
}
232+
233+
impl<'a, T, const PAUSE: bool> BasicSpinMutexGuard<'a, T, PAUSE> {
234+
/// Returns a reference to the protected data.
235+
///
236+
/// This method is rarely needed since the guard implements `Deref`.
237+
pub fn get(&self) -> &T {
238+
unsafe { &*self.mutex.data.get() }
239+
}
240+
241+
/// Returns a mutable reference to the protected data.
242+
///
243+
/// This method is rarely needed since the guard implements `DerefMut`.
244+
pub fn get_mut(&mut self) -> &mut T {
245+
unsafe { &mut *self.mutex.data.get() }
246+
}
247+
}
248+
249+
impl<'a, T, const PAUSE: bool> core::ops::Deref for BasicSpinMutexGuard<'a, T, PAUSE> {
250+
type Target = T;
251+
252+
fn deref(&self) -> &Self::Target {
253+
unsafe { &*self.mutex.data.get() }
254+
}
255+
}
256+
257+
impl<'a, T, const PAUSE: bool> core::ops::DerefMut for BasicSpinMutexGuard<'a, T, PAUSE> {
258+
fn deref_mut(&mut self) -> &mut Self::Target {
259+
unsafe { &mut *self.mutex.data.get() }
260+
}
261+
}
262+
263+
impl<'a, T, const PAUSE: bool> Drop for BasicSpinMutexGuard<'a, T, PAUSE> {
264+
fn drop(&mut self) {
265+
self.mutex.locked.store(false, Ordering::Release);
266+
}
267+
}
268+
269+
/// A type alias for the most commonly used spin mutex configuration.
270+
///
271+
/// This is equivalent to `BasicSpinMutex<T, true>`, which enables CPU-specific
272+
/// pause instructions for efficient busy-waiting.
273+
///
274+
/// # Examples
275+
///
276+
/// ```rust
277+
/// use fork_union::*;
278+
///
279+
/// let mutex = SpinMutex::new(42);
280+
/// let mut guard = mutex.lock();
281+
/// *guard = 100;
282+
/// ```
283+
pub type SpinMutex<T> = BasicSpinMutex<T, true>;
23284

24285
/// A "prong" - the tip of a "fork" - pinning a "task" to a "thread" and "memory" location.
25286
///

0 commit comments

Comments
 (0)