seqlock/
lib.rs

1//! This library provides the `SeqLock` type, which is a form of reader-writer
2//! lock that is heavily optimized for readers.
3//!
4//! In certain situations, `SeqLock` can be two orders of magnitude faster than
5//! the standard library `RwLock` type. Another advantage is that readers cannot
6//! starve writers: a writer will never block even if there are readers
7//! currently accessing the `SeqLock`.
8//!
9//! The only downside of `SeqLock` is that it only works on types that are
10//! `Copy`. This means that it is unsuitable for types that contains pointers
11//! to owned data.
12//!
13//! You should instead use a `RwLock` if you need
14//! a reader-writer lock for types that are not `Copy`.
15//!
16//! # Implementation
17//!
18//! The implementation is based on the [Linux seqlock type](http://lxr.free-electrons.com/source/include/linux/seqlock.h).
19//! Each `SeqLock` contains a sequence counter which tracks modifications to the
20//! locked data. The basic idea is that a reader will perform the following
21//! operations:
22//!
23//! 1. Read the sequence counter.
24//! 2. Read the data in the lock.
25//! 3. Read the sequence counter again.
26//! 4. If the two sequence counter values differ, loop back and try again.
27//! 5. Otherwise return the data that was read in step 2.
28//!
29//! Similarly, a writer will increment the sequence counter before and after
30//! writing to the data in the lock. Once a reader sees that the sequence
31//! counter has not changed while it was reading the data, it can safely return
32//! that data to the caller since it is known to be in a consistent state.
33//!
34//! # Examples
35//!
36//! ```
37//! use seqlock::SeqLock;
38//!
39//! let lock = SeqLock::new(5);
40//!
41//! {
42//!     // Writing to the data involves a lock
43//!     let mut w = lock.lock_write();
44//!     *w += 1;
45//!     assert_eq!(*w, 6);
46//! }
47//!
48//! {
49//!     // Reading the data is a very fast operation
50//!     let r = lock.read();
51//!     assert_eq!(r, 6);
52//! }
53//! ```
54
55#![warn(missing_docs, rust_2018_idioms)]
56
57use parking_lot::{Mutex, MutexGuard};
58use std::cell::UnsafeCell;
59use std::fmt;
60use std::mem::MaybeUninit;
61use std::ops::{Deref, DerefMut};
62use std::ptr;
63use std::sync::atomic::{fence, AtomicUsize, Ordering};
64use std::thread;
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                thread::yield_now();
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}