spin_seq/
lib.rs

1#![no_std]
2//! This library provides the `SeqLock` type, which is a form of reader-writer
3//! lock that is heavily optimized for readers.
4//!
5//! In certain situations, `SeqLock` can be two orders of magnitude faster than
6//! the standard library `RwLock` type. Another advantage is that readers cannot
7//! starve writers: a writer will never block even if there are readers
8//! currently accessing the `SeqLock`.
9//!
10//! The only downside of `SeqLock` is that it only works on types that are
11//! `Copy`. This means that it is unsuitable for types that contains pointers
12//! to owned data.
13//!
14//! You should instead use a `RwLock` if you need
15//! a reader-writer lock for types that are not `Copy`.
16//!
17//! # Implementation
18//!
19//! The implementation is based on the [Linux seqlock type](http://lxr.free-electrons.com/source/include/linux/seqlock.h).
20//! Each `SeqLock` contains a sequence counter which tracks modifications to the
21//! locked data. The basic idea is that a reader will perform the following
22//! operations:
23//!
24//! 1. Read the sequence counter.
25//! 2. Read the data in the lock.
26//! 3. Read the sequence counter again.
27//! 4. If the two sequence counter values differ, loop back and try again.
28//! 5. Otherwise return the data that was read in step 2.
29//!
30//! Similarly, a writer will increment the sequence counter before and after
31//! writing to the data in the lock. Once a reader sees that the sequence
32//! counter has not changed while it was reading the data, it can safely return
33//! that data to the caller since it is known to be in a consistent state.
34//!
35//! # Examples
36//!
37//! ```
38//! use seqlock::SeqLock;
39//!
40//! let lock = SeqLock::new(5);
41//!
42//! {
43//!     // Writing to the data involves a lock
44//!     let mut w = lock.lock_write();
45//!     *w += 1;
46//!     assert_eq!(*w, 6);
47//! }
48//!
49//! {
50//!     // Reading the data is a very fast operation
51//!     let r = lock.read();
52//!     assert_eq!(r, 6);
53//! }
54//! ```
55
56#![warn(missing_docs, rust_2018_idioms)]
57
58use spin::{Mutex, MutexGuard};
59use core::cell::UnsafeCell;
60use core::fmt;
61use core::mem::MaybeUninit;
62use core::ops::{Deref, DerefMut};
63use core::ptr;
64use core::sync::atomic::{fence, AtomicUsize, Ordering};
65
66/// A sequential lock
67pub struct SeqLock<T> {
68    seq: AtomicUsize,
69    data: UnsafeCell<T>,
70    mutex: Mutex<()>,
71}
72
73unsafe impl<T: Send> Send for SeqLock<T> {}
74unsafe impl<T: Send> Sync for SeqLock<T> {}
75
76/// RAII structure used to release the exclusive write access of a `SeqLock`
77/// when dropped.
78pub struct SeqLockGuard<'a, T> {
79    _guard: MutexGuard<'a, ()>,
80    seqlock: &'a SeqLock<T>,
81    seq: usize,
82}
83
84impl<T> SeqLock<T> {
85    #[inline]
86    fn end_write(&self, seq: usize) {
87        // Increment the sequence number again, which will make it even and
88        // allow readers to access the data. The release ordering ensures that
89        // all writes to the data are done before writing the sequence number.
90        self.seq.store(seq.wrapping_add(1), Ordering::Release);
91    }
92}
93
94impl<T: Copy> SeqLock<T> {
95    /// Creates a new SeqLock with the given initial value.
96    #[inline]
97    pub const fn new(val: T) -> SeqLock<T> {
98        SeqLock {
99            seq: AtomicUsize::new(0),
100            data: UnsafeCell::new(val),
101            mutex: Mutex::new(()),
102        }
103    }
104
105    /// Reads the value protected by the `SeqLock`.
106    ///
107    /// This operation is extremely fast since it only reads the `SeqLock`,
108    /// which allows multiple readers to read the value without interfering with
109    /// each other.
110    ///
111    /// If a writer is currently modifying the contained value then the calling
112    /// thread will block until the writer thread releases the lock.
113    ///
114    /// Attempting to read from a `SeqLock` while already holding a write lock
115    /// in the current thread will result in a deadlock.
116    #[inline]
117    pub fn read(&self) -> T {
118        loop {
119            // Load the first sequence number. The acquire ordering ensures that
120            // this is done before reading the data.
121            let seq1 = self.seq.load(Ordering::Acquire);
122
123            // If the sequence number is odd then it means a writer is currently
124            // modifying the value.
125            if seq1 & 1 != 0 {
126                // Yield to give the writer a chance to finish. Writing is
127                // expected to be relatively rare anyways so this isn't too
128                // performance critical.
129                core::hint::spin_loop();
130                continue;
131            }
132
133            // We need to use a volatile read here because the data may be
134            // concurrently modified by a writer. We also use MaybeUninit in
135            // case we read the data in the middle of a modification.
136            let result = unsafe { ptr::read_volatile(self.data.get() as *mut MaybeUninit<T>) };
137
138            // Make sure the seq2 read occurs after reading the data. What we
139            // ideally want is a load(Release), but the Release ordering is not
140            // available on loads.
141            fence(Ordering::Acquire);
142
143            // If the sequence number is the same then the data wasn't modified
144            // while we were reading it, and can be returned.
145            let seq2 = self.seq.load(Ordering::Relaxed);
146            if seq1 == seq2 {
147                return unsafe { result.assume_init() };
148            }
149        }
150    }
151
152    #[inline]
153    fn begin_write(&self) -> usize {
154        // Increment the sequence number. At this point, the number will be odd,
155        // which will force readers to spin until we finish writing.
156        let seq = self.seq.load(Ordering::Relaxed).wrapping_add(1);
157        self.seq.store(seq, Ordering::Relaxed);
158
159        // Make sure any writes to the data happen after incrementing the
160        // sequence number. What we ideally want is a store(Acquire), but the
161        // Acquire ordering is not available on stores.
162        fence(Ordering::Release);
163
164        seq
165    }
166
167    #[inline]
168    fn lock_guard<'a>(&'a self, guard: MutexGuard<'a, ()>) -> SeqLockGuard<'a, T> {
169        let seq = self.begin_write();
170        SeqLockGuard {
171            _guard: guard,
172            seqlock: self,
173            seq: seq,
174        }
175    }
176
177    /// Locks this `SeqLock` with exclusive write access, blocking the current
178    /// thread until it can be acquired.
179    ///
180    /// This function does not block while waiting for concurrent readers.
181    /// Instead, readers will detect the concurrent write and retry the read.
182    ///
183    /// Returns an RAII guard which will drop the write access of this `SeqLock`
184    /// when dropped.
185    #[inline]
186    pub fn lock_write(&self) -> SeqLockGuard<'_, T> {
187        self.lock_guard(self.mutex.lock())
188    }
189
190    /// Attempts to lock this `SeqLock` with exclusive write access.
191    ///
192    /// If the lock could not be acquired at this time, then `None` is returned.
193    /// Otherwise, an RAII guard is returned which will release the lock when
194    /// it is dropped.
195    ///
196    /// This function does not block.
197    #[inline]
198    pub fn try_lock_write(&self) -> Option<SeqLockGuard<'_, T>> {
199        self.mutex.try_lock().map(|g| self.lock_guard(g))
200    }
201
202    /// Consumes this `SeqLock`, returning the underlying data.
203    #[inline]
204    pub fn into_inner(self) -> T {
205        self.data.into_inner()
206    }
207
208    /// Returns a mutable reference to the underlying data.
209    ///
210    /// Since this call borrows the `SeqLock` mutably, no actual locking needs
211    /// to take place---the mutable borrow statically guarantees no locks exist.
212    #[inline]
213    pub fn get_mut(&mut self) -> &mut T {
214        unsafe { &mut *self.data.get() }
215    }
216}
217
218impl<T: Copy + Default> Default for SeqLock<T> {
219    #[inline]
220    fn default() -> SeqLock<T> {
221        SeqLock::new(Default::default())
222    }
223}
224
225impl<T: Copy + fmt::Debug> fmt::Debug for SeqLock<T> {
226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227        write!(f, "SeqLock {{ data: {:?} }}", &self.read())
228    }
229}
230
231impl<'a, T: Copy + 'a> Deref for SeqLockGuard<'a, T> {
232    type Target = T;
233    #[inline]
234    fn deref(&self) -> &T {
235        unsafe { &*self.seqlock.data.get() }
236    }
237}
238
239impl<'a, T: Copy + 'a> DerefMut for SeqLockGuard<'a, T> {
240    #[inline]
241    fn deref_mut(&mut self) -> &mut T {
242        unsafe { &mut *self.seqlock.data.get() }
243    }
244}
245
246impl<T> Drop for SeqLockGuard<'_, T> {
247    #[inline]
248    fn drop(&mut self) {
249        self.seqlock.end_write(self.seq);
250    }
251}