-
Notifications
You must be signed in to change notification settings - Fork 0
/
spinlock.rs
109 lines (95 loc) · 2.77 KB
/
spinlock.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::{
cell::UnsafeCell,
ops::{Deref, DerefMut},
sync::atomic::{AtomicBool, Ordering},
};
pub struct SpinLock<T> {
locked: AtomicBool,
data: UnsafeCell<T>,
}
unsafe impl<T> Sync for SpinLock<T> where T: Send {}
impl<T> SpinLock<T> {
pub fn new(data: T) -> Self {
Self {
locked: AtomicBool::new(false),
data: UnsafeCell::new(data),
}
}
pub fn lock(&self) -> SpinLockGuard<'_, T> {
while self.locked.swap(true, Ordering::Acquire) {
std::hint::spin_loop();
}
SpinLockGuard { lock: &self }
}
fn unlock(&self) {
self.locked.store(false, Ordering::Release);
}
}
pub struct SpinLockGuard<'a, T> {
lock: &'a SpinLock<T>,
}
impl<'a, T> Deref for SpinLockGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// Safety: The existence of this Guard instance guarantees this is the
// exclusive reference to the data.
unsafe { &*self.lock.data.get() }
}
}
impl<'a, T> DerefMut for SpinLockGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
// Safety: The existence of this Guard instance guarantees this is the
// exclusive reference to the data.
unsafe { &mut *self.lock.data.get() }
}
}
impl<'a, T> Drop for SpinLockGuard<'a, T> {
fn drop(&mut self) {
self.lock.unlock();
}
}
pub mod check {
use super::SpinLock;
use std::{thread, time::Duration};
pub fn run() {
let lock = SpinLock::new(-1);
thread::scope(|s| {
for i in 0..2 {
let lock = &lock;
s.spawn(move || {
println!("Locking from thread {i}");
let mut guard = lock.lock();
println!("Locked from thread {i}");
println!("value from thread {i}: {}", *guard);
*guard = i;
println!("Set value: {i}");
thread::sleep(Duration::from_secs(2));
println!("Unlocking from thread {i}");
drop(guard);
println!("Unlocked from thread {i}");
});
}
});
}
}
#[cfg(test)]
mod test {
use super::SpinLock;
use std::{thread, time::Duration};
/// This is probably not going to be conclusive!
#[test]
fn add() {
let lock = SpinLock::new(0 as usize);
const THREADS: usize = 100;
thread::scope(|s| {
for _ in 0..THREADS {
s.spawn(|| {
let mut guard = lock.lock();
thread::sleep(Duration::from_millis(1));
*guard += 1;
});
}
});
assert_eq!(*lock.lock(), THREADS);
}
}