atomiclock_spinlock/
lib.rs

1//SPDX-License-Identifier: MIT OR Apache-2.0
2/*!
3A simple spinlock.
4
5![logo](art/logo.png)
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