wasm/core/
rw_spinlock.rs

1//! Naive implementation of spin based locking mechanisms
2//!
3//! This module provides implementations for locking mechanisms required.
4//!
5//! # Acknowledgement
6//!
7//! This implementation is largely inspired by the book
8//! ["Rust Atomics and Locks" by Mara Bos](https://marabos.nl/atomics/).
9
10use core::cell::UnsafeCell;
11use core::hint::{self};
12use core::ops::{Deref, DerefMut};
13use core::sync::atomic::{AtomicU32, Ordering};
14
15/// A spinlock based, read-write lock which favours writers over readers
16///
17/// # Properties
18///
19/// - Read-write semantics allow for multiple readers at the same time, but require exclusive access
20///   for a writer
21/// - Spin based, e.g. waiting for the lock wastes CPU cycles in a busy loop
22///    - This design however enables an implementation independent of operating system features like
23///      condition variables, the only requirement are 32 bit atomics in the ISA
24/// - Biased towards writes: once a writer waits for the lock, all new reader wait until that writer
25///   got access
26pub struct RwSpinLock<T> {
27    /// The inner data protected by this lock
28    inner: UnsafeCell<T>,
29
30    /// Lock state (on ambiguity, the state closer to the top takes precedence)
31    ///
32    /// - `0` means there are no readers nor any writer
33    /// - `u32::MAX` means there is a single active writer
34    /// - `state % 2 == 0` means there are `state / 2` active readers
35    /// - `state % 2 != 0` means there are `(state - 1) / 2` active readers and at least one waiting
36    ///   writer
37    state: AtomicU32,
38}
39
40impl<T> RwSpinLock<T> {
41    /// Create a new instance of self, wrapping the `value` of type `T`
42    pub fn new(value: T) -> Self {
43        Self {
44            inner: UnsafeCell::new(value),
45            state: AtomicU32::new(0),
46        }
47    }
48
49    // Get read access to the value wrapped in this [`RwSpinLock`]
50    pub fn read(&self) -> ReadLockGuard<T> {
51        // get the current state
52        let mut s = self.state.load(Ordering::Relaxed); // ordering by the book
53
54        loop {
55            // s is even, so there are maybe active readers but no active or waiting writer
56            // -> reader can acquire read guard (as long as an overflow is avoided)
57            if s % 2 == 0 && s < u32::MAX - 2 {
58                match self.state.compare_exchange_weak(
59                    s,
60                    s + 2,
61                    Ordering::Acquire,
62                    Ordering::Relaxed,
63                ) {
64                    Ok(_) => return ReadLockGuard { lock: self },
65                    Err(update_s) => s = update_s,
66                }
67            }
68
69            // there is one active (`s == u32::MAX`) or at least one waiting (otherwise) writer
70            // -> spin, re-load s and try again
71            if s % 2 == 1 {
72                hint::spin_loop();
73                s = self.state.load(Ordering::Relaxed); // ordering by the book
74            }
75        }
76    }
77
78    // Get write access to the value wrapped in this [`RwSpinLock`]
79    pub fn write(&self) -> WriteLockGuard<T> {
80        let mut s = self.state.load(Ordering::Relaxed);
81
82        loop {
83            // there is no active reader (`s >= 2 && s % 2 == 0`) or writer (`s == u32::MAX`)
84            if s <= 1 {
85                match self
86                    .state
87                    .compare_exchange(s, u32::MAX, Ordering::Acquire, Ordering::Relaxed)
88                {
89                    Ok(_) => return WriteLockGuard { lock: self },
90                    Err(updated_s) => {
91                        s = updated_s;
92                        continue;
93                    }
94                }
95            }
96
97            // announce that a writer is waiting if this is not yet announced
98            if s % 2 == 0 {
99                match self
100                    .state
101                    // ordering by the book
102                    .compare_exchange(s, s + 1, Ordering::Relaxed, Ordering::Relaxed)
103                {
104                    Ok(_) => {}
105                    Err(updated_s) => {
106                        s = updated_s;
107                        continue;
108                    }
109                }
110            }
111
112            // wait was announced, there are still active readers
113            // -> spin, re-load s, continue from the start of the lop
114            hint::spin_loop();
115            s = self.state.load(Ordering::Relaxed);
116        }
117    }
118}
119
120unsafe impl<T> Sync for RwSpinLock<T> where T: Send + Sync {}
121
122/// Read guard for the [`RwSpinLock`]
123pub struct ReadLockGuard<'a, T> {
124    lock: &'a RwSpinLock<T>,
125}
126
127impl<T> Deref for ReadLockGuard<'_, T> {
128    type Target = T;
129    fn deref(&self) -> &T {
130        unsafe { &*self.lock.inner.get() }
131    }
132}
133
134impl<T> Drop for ReadLockGuard<'_, T> {
135    fn drop(&mut self) {
136        self.lock.state.fetch_sub(2, Ordering::Release); // ordering by the book
137    }
138}
139
140/// Write guard for the [`RwSpinLock`]
141pub struct WriteLockGuard<'a, T> {
142    lock: &'a RwSpinLock<T>,
143}
144
145impl<T> Deref for WriteLockGuard<'_, T> {
146    type Target = T;
147    fn deref(&self) -> &T {
148        unsafe { &*self.lock.inner.get() }
149    }
150}
151
152impl<T> DerefMut for WriteLockGuard<'_, T> {
153    fn deref_mut(&mut self) -> &mut T {
154        unsafe { &mut *self.lock.inner.get() }
155    }
156}
157
158impl<T> Drop for WriteLockGuard<'_, T> {
159    fn drop(&mut self) {
160        self.lock.state.store(0, Ordering::Release); // ordering by the book
161    }
162}