atomiclock_spinlock/lib.rs
1//SPDX-License-Identifier: MIT OR Apache-2.0
2/*!
3A simple spinlock.
4
5
6
7This is a simple spinlock. It is not a fair lock, and it does not provide any way to sleep the current thread if the lock is not available.
8
9 */
10
11use core::ops::{Deref, DerefMut};
12use logwise::interval::PerfwarnInterval;
13
14/**
15A simple spinlock type.
16 */
17#[derive(Debug)]
18pub struct Lock<T> {
19 lock: atomiclock::AtomicLock<T>
20}
21
22/**
23A guard that provides access to the data in the lock.
24 */
25#[derive(Debug)]
26#[must_use]
27pub struct Guard<'a, T>(atomiclock::Guard<'a, T>);
28
29impl <'a, T> Guard<'a, T> {
30 pub fn get_mut(&mut self) -> &mut T {
31 self.0.as_mut()
32 }
33}
34
35//drop - we forward to the atomiclock implementation, duh
36
37impl<T> Lock<T> {
38 /**
39 Creates a new lock.
40*/
41 pub const fn new(data: T) -> Lock<T> {
42 Lock {
43 lock: atomiclock::AtomicLock::new(data)
44 }
45 }
46
47 /**
48 Spins until the lock can be acquired.
49*/
50 pub fn spin_lock(&self) -> Guard<'_,T> {
51 loop {
52 match self.lock.lock() {
53 None => {}
54 Some(guard) => {return Guard(guard)}
55 }
56
57 }
58 }
59
60 /**
61 Spins until the lock can be acquired, issuing a perfwarn if spinning were needed due to contention.
62
63 This function is appropriate to use when:
64 1. A spinlock is correct and easy to write.
65 2. You have the suspicion there's a "better" lock-free algorithm, but the tradeoffs are unclear. Worse cache coherency, more code, etc.
66 3. It would be nice to collect some data that would actually drive the decision to write a lock-free algorithm, but to do that you first have to write a program.
67 */
68 pub fn spin_lock_warn(&self) -> Guard<'_, T> {
69 let mut _warn: Option<PerfwarnInterval>;
70 loop {
71 match self.lock.lock() {
72 None => {
73 _warn = Some(logwise::perfwarn_begin!("spin_lock_warn is spinning; investigate ways to reduce contention"));
74 }
75 Some(guard) => {
76 _warn = None;
77 return Guard(guard);
78 }
79 }
80 }
81 }
82
83 /**
84 Spins until the lock is available, or times out.
85*/
86 pub fn spin_lock_until(&self, deadline: std::time::Instant) -> Option<Guard<'_,T>> {
87 loop {
88 if std::time::Instant::now() > deadline {
89 return None;
90 }
91 match self.lock.lock() {
92 None => {}
93 Some(guard) => {return Some(Guard(guard))}
94 }
95
96 }
97 }
98
99 /**
100 No spin; provides access to the lock if available.
101*/
102 pub fn try_lock(&self) -> Option<Guard<'_,T>> {
103 match self.lock.lock() {
104 None => None,
105 Some(guard) => Some(Guard(guard))
106 }
107 }
108
109 /**
110 Consumes the lock and returns the inner data.
111*/
112 pub fn into_inner(self) -> T {
113 self.lock.into_inner()
114 }
115
116
117 /**
118 Unsafely provides access to the underlying data.
119
120 # Safety
121 This function is unsafe because it allows access to the data without a lock.
122*/
123 pub unsafe fn data(&self) -> &mut T {
124 self.lock.data()
125 }
126}
127
128/*boilerplate
129Locks are not clone, so not copy, Eq, Ord, Hash, etc.
130Can pass-through default for default type
131can support From for the data type
132
133 */
134impl<T: Default> Default for Lock<T> {
135 fn default() -> Lock<T> {
136 Lock::new(Default::default())
137 }
138}
139impl<T> From<T> for Lock<T> {
140 fn from(data: T) -> Lock<T> {
141 Lock::new(data)
142 }
143}
144
145/*
146Guard is not copy/clone, so not eq, ord, hash, etc.
147No default for guard
148I guess it could be created via from
149 */
150
151impl<'a, T> From<&'a Lock<T>> for Guard<'a,T> {
152 /**
153 Implements From by spinning until the lock is acquired.
154 */
155 fn from(lock: &'a Lock<T>) -> Guard<'a,T> {
156 lock.spin_lock()
157 }
158}
159
160impl<T> AsRef<T> for Guard<'_,T> {
161 fn as_ref(&self) -> &T {
162 self.0.as_ref()
163 }
164}
165
166impl<T> AsMut<T> for Guard<'_,T> {
167 fn as_mut(&mut self) -> &mut T {
168 self.0.as_mut()
169 }
170}
171
172impl<T> Deref for Guard<'_,T> {
173 type Target = T;
174 fn deref(&self) -> &T {
175 self.0.deref()
176 }
177}
178
179impl<T> DerefMut for Guard<'_,T> {
180 fn deref_mut(&mut self) -> &mut T {
181 self.0.deref_mut()
182 }
183}
184
185