atomiclock/
lib.rs

1//SPDX-License-Identifier: MIT OR Apache-2.0
2#![no_std]
3
4
5/*! A Rust atomic lock type.
6
7![logo](art/logo.png)
8
9This is a simple atomic lock.
10
11There is no way to sleep the current thread if the lock is not available, what you do about that is up to you.
12*/
13
14use core::cell::UnsafeCell;
15use core::fmt::{Debug, Display};
16use core::sync::atomic::{AtomicBool, Ordering};
17
18/**
19An atomic lock type.
20
21*/
22pub struct AtomicLock<T> {
23    lock: AtomicBool,
24    data: UnsafeCell<T>,
25}
26
27impl<T> AtomicLock<T> {
28    /**
29    Creates a new lock
30*/
31    pub const fn new(data: T) -> Self {
32        AtomicLock {
33            lock: AtomicBool::new(false),
34            data: UnsafeCell::new(data),
35        }
36    }
37    /**
38    Locks the lock and accesses the data if available.
39    If the lock is unavailable, will return None.
40
41    There is intentionally nothing 'to do' about this, as far as this crate is concerned.
42    Other crates may wrap this type and implement some other behavior.  For example:
43    * You could spin, creating a spinlock
44    * You could sleep, creating an OS lock somehow
45    * You could yield, creating a cooperative async lock
46
47    It's up to you!
48    */
49    pub fn lock(&self) -> Option<Guard<T>> {
50        match self.lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed) {
51            Ok(_) => Some(
52                Guard {
53                    lock: self,
54                    data: unsafe { &mut *self.data.get() },
55                }
56
57            ),
58            Err(_) => None,
59        }
60    }
61
62    /**
63    Unlocks the current lock.
64*/
65    pub fn unlock(&self) {
66        let old = self.lock.swap(false, Ordering::Release);
67        assert_eq!(old, true);
68    }
69
70    /** Unsafely access the underlying data.
71
72    # Safety
73    You must ensure that no other readers or writers are accessing the lock.
74    */
75    pub unsafe fn data(&self) -> &mut T {
76        &mut *self.data.get()
77    }
78
79    /**
80    Conumes the lock, returning the inner data.
81    */
82    pub fn into_inner(self) -> T {
83        self.data.into_inner()
84    }
85
86}
87
88
89impl<T: Debug> Debug for AtomicLock<T> {
90    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91        let guard = self.lock();
92        match guard {
93            None => {
94                f.debug_struct("AtomicLock")
95                    .field("locked", &true)
96                    .field("data", &"<Locked>")
97                    .finish()
98            }
99            Some(data) => {
100                f.debug_struct("AtomicLock")
101                    .field("locked", &false)
102                    .field("data", &data)
103                    .finish()
104            }
105        }
106    }
107}
108
109/**
110A guard for [AtomicLock].
111
112Unlocks when dropped.
113*/
114
115#[derive(Debug)]
116#[must_use]
117pub struct Guard<'a, T> {
118    lock: &'a AtomicLock<T>,
119    data: &'a mut T,
120}
121
122impl<'a, T> Drop for Guard<'a, T> {
123    fn drop(&mut self) {
124        self.lock.unlock();
125    }
126}
127
128//boilerplate
129/*
130I think we don't want to derive Clone, reading the data would involve acquiring the lock...
131same for PartialEq, PartialOrd, etc.
132Same for hash...
133default maybe?
134 */
135
136impl <T> Default for AtomicLock<T> where T: Default {
137    fn default() -> Self {
138        AtomicLock::new(T::default())
139    }
140}
141
142
143//from is probably fine
144
145impl <T> From<T> for AtomicLock<T> {
146    fn from(data: T) -> Self {
147        AtomicLock::new(data)
148    }
149}
150
151
152//asref/mut requires owning the data, so nogo
153//same for deref / derefmut
154
155//send and sync are ok
156
157unsafe impl<T> Send for AtomicLock<T> {}
158unsafe impl<T> Sync for AtomicLock<T> {}
159
160/*now let's examine the guard boilerplate.
161
162Guard cannot be cloned because doing so would involve locking a second time.  Similarly, not copy.
163
164 */
165
166
167//default makes no sense and should not be done
168
169//display,
170
171impl <'a, T> Display for Guard<'a, T> where T: Display {
172    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173        self.data.fmt(f)
174    }
175}
176
177//from and into make no sense.
178
179//asref/mut should be fine since we have the lock...
180
181impl <'a, T> AsRef<T> for Guard<'a, T> {
182    fn as_ref(&self) -> &T {
183        self.data
184    }
185}
186
187impl <'a, T> AsMut<T> for Guard<'a, T> {
188    fn as_mut(&mut self) -> &mut T {
189        self.data
190    }
191}
192
193//deref/derefmut should be fine since we have the lock...
194
195impl <'a, T> core::ops::Deref for Guard<'a, T> {
196    type Target = T;
197    fn deref(&self) -> &T {
198        self.data
199    }
200}
201
202impl <'a, T> core::ops::DerefMut for Guard<'a, T> {
203    fn deref_mut(&mut self) -> &mut T {
204        self.data
205    }
206}
207
208/*
209Send is ok.
210
211MutexGuard does not implement Send, due to OS constraints on unlocking from the same thread
212as locked.
213
214We don't have those issues, so.
215 */
216unsafe impl<'a, T> Send for Guard<'a, T> {}