Coverage Report

Created: 2021-01-22 16:54

crossbeam-utils/src/atomic/seq_lock.rs
Line
Count
Source (jump to first uncovered line)
1
use core::mem;
2
use core::sync::atomic::{self, AtomicUsize, Ordering};
3
4
use crate::Backoff;
5
6
/// A simple stamped lock.
7
pub(crate) struct SeqLock {
8
    /// The current state of the lock.
9
    ///
10
    /// All bits except the least significant one hold the current stamp. When locked, the state
11
    /// equals 1 and doesn't contain a valid stamp.
12
    state: AtomicUsize,
13
}
14
15
impl SeqLock {
16
    pub(crate) const fn new() -> Self {
17
        Self {
18
            state: AtomicUsize::new(0),
19
        }
20
    }
21
22
    /// If not locked, returns the current stamp.
23
    ///
24
    /// This method should be called before optimistic reads.
25
    #[inline]
26
3
    pub(crate) fn optimistic_read(&self) -> Option<usize> {
27
3
        let state = self.state.load(Ordering::Acquire);
28
3
        if state == 1 {
29
0
            None
30
        } else {
31
3
            Some(state)
32
        }
33
3
    }
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
<crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Line
Count
Source
26
2
    pub(crate) fn optimistic_read(&self) -> Option<usize> {
27
2
        let state = self.state.load(Ordering::Acquire);
28
2
        if state == 1 {
29
0
            None
30
        } else {
31
2
            Some(state)
32
        }
33
2
    }
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
<crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Line
Count
Source
26
1
    pub(crate) fn optimistic_read(&self) -> Option<usize> {
27
1
        let state = self.state.load(Ordering::Acquire);
28
1
        if state == 1 {
29
0
            None
30
        } else {
31
1
            Some(state)
32
        }
33
1
    }
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read
34
35
    /// Returns `true` if the current stamp is equal to `stamp`.
36
    ///
37
    /// This method should be called after optimistic reads to check whether they are valid. The
38
    /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
39
    #[inline]
40
1
    pub(crate) fn validate_read(&self, stamp: usize) -> bool {
41
1
        atomic::fence(Ordering::Acquire);
42
1
        self.state.load(Ordering::Relaxed) == stamp
43
1
    }
44
45
    /// Grabs the lock for writing.
46
    #[inline]
47
2
    pub(crate) fn write(&'static self) -> SeqLockWriteGuard {
48
2
        let backoff = Backoff::new();
49
        loop {
50
2
            let previous = self.state.swap(1, Ordering::Acquire);
51
2
52
2
            if previous != 1 {
53
2
                atomic::fence(Ordering::Release);
54
2
55
2
                return SeqLockWriteGuard {
56
2
                    lock: self,
57
2
                    state: previous,
58
2
                };
59
0
            }
60
0
61
0
            backoff.snooze();
62
        }
63
2
    }
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
<crossbeam_utils::atomic::seq_lock::SeqLock>::write
Line
Count
Source
47
1
    pub(crate) fn write(&'static self) -> SeqLockWriteGuard {
48
1
        let backoff = Backoff::new();
49
        loop {
50
1
            let previous = self.state.swap(1, Ordering::Acquire);
51
1
52
1
            if previous != 1 {
53
1
                atomic::fence(Ordering::Release);
54
1
55
1
                return SeqLockWriteGuard {
56
1
                    lock: self,
57
1
                    state: previous,
58
1
                };
59
0
            }
60
0
61
0
            backoff.snooze();
62
        }
63
1
    }
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
<crossbeam_utils::atomic::seq_lock::SeqLock>::write
Line
Count
Source
47
1
    pub(crate) fn write(&'static self) -> SeqLockWriteGuard {
48
1
        let backoff = Backoff::new();
49
        loop {
50
1
            let previous = self.state.swap(1, Ordering::Acquire);
51
1
52
1
            if previous != 1 {
53
1
                atomic::fence(Ordering::Release);
54
1
55
1
                return SeqLockWriteGuard {
56
1
                    lock: self,
57
1
                    state: previous,
58
1
                };
59
0
            }
60
0
61
0
            backoff.snooze();
62
        }
63
1
    }
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write
64
}
65
66
/// An RAII guard that releases the lock and increments the stamp when dropped.
67
pub(crate) struct SeqLockWriteGuard {
68
    /// The parent lock.
69
    lock: &'static SeqLock,
70
71
    /// The stamp before locking.
72
    state: usize,
73
}
74
75
impl SeqLockWriteGuard {
76
    /// Releases the lock without incrementing the stamp.
77
    #[inline]
78
1
    pub(crate) fn abort(self) {
79
1
        self.lock.state.store(self.state, Ordering::Release);
80
1
81
1
        // We specifically don't want to call drop(), since that's
82
1
        // what increments the stamp.
83
1
        mem::forget(self);
84
1
    }
85
}
86
87
impl Drop for SeqLockWriteGuard {
88
    #[inline]
89
1
    fn drop(&mut self) {
90
1
        // Release the lock and increment the stamp.
91
1
        self.lock
92
1
            .state
93
1
            .store(self.state.wrapping_add(2), Ordering::Release);
94
1
    }
95
}
96
97
#[cfg(test)]
98
mod tests {
99
    use super::SeqLock;
100
101
    #[test]
102
1
    fn test_abort() {
crossbeam_utils::atomic::seq_lock::tests::test_abort::{closure#0}
Line
Count
Source
102
1
    fn test_abort() {
103
        static LK: SeqLock = SeqLock::new();
104
1
        let before = LK.optimistic_read().unwrap();
105
1
        {
106
1
            let guard = LK.write();
107
1
            guard.abort();
108
1
        }
109
1
        let after = LK.optimistic_read().unwrap();
110
0
        assert_eq!(before, after, "aborted write does not update the stamp");
111
1
    }
crossbeam_utils::atomic::seq_lock::tests::test_abort
Line
Count
Source
102
1
    fn test_abort() {
103
        static LK: SeqLock = SeqLock::new();
104
1
        let before = LK.optimistic_read().unwrap();
105
1
        {
106
1
            let guard = LK.write();
107
1
            guard.abort();
108
1
        }
109
1
        let after = LK.optimistic_read().unwrap();
110
0
        assert_eq!(before, after, "aborted write does not update the stamp");
111
1
    }
112
}