rt_write_lock/
lib.rs

1// std::mem::forget creates issues because you can run a lock function without running the unlock function
2// i deal with this by tracking for Reader and Writer if they are locked currently. If a new locked is aquired
3// while it's still locked it panics. If Reader or Writer are dropped while locked it also panics.
4//
5// This could be circumvented by leaking Reader or Writer, but this doesn't lead to issues as then it's impossible
6// to aquire a new Reader or Writer, because then the count also wasn't decremented.
7//
8// This panics more often than UB would occur without this detection, but this exact specs is a implementation detail
9// and users just shouldn't forget the Guards.
10
11#![no_std]
12
13extern crate alloc;
14
15use core::{
16    marker::PhantomData,
17    ops::{Deref, DerefMut},
18    ptr::NonNull,
19};
20
21mod shared;
22
23use shared::{Ptr, Shared};
24
25pub struct Reader<T> {
26    shared: NonNull<Shared<T>>,
27    locked: bool,
28    _owned: PhantomData<T>,
29}
30
31impl<T> Reader<T> {
32    pub fn try_read(&mut self) -> Option<ReadGuard<'_, T>> {
33        if self.locked {
34            self.locked = false; // don't also panic in the destructor
35            panic!("ReadGuard was forgotten");
36        }
37        let shared = self.get_shared();
38        let value = shared.try_read();
39        // SAFETY: try_read returned Some to allowed to get a shared ref
40        let value = value.map(|v| unsafe { &*v.get() });
41        if let Some(value) = value {
42            // only set locked if locking actually was successful
43            self.locked = true;
44            Some(ReadGuard {
45                reader: self,
46                value,
47            })
48        } else {
49            None
50        }
51    }
52
53    fn get_shared(&self) -> &Shared<T> {
54        // SAFETY: the shared is always valid
55        unsafe { self.shared.as_ref() }
56    }
57
58    pub fn get_writer(&mut self) -> Option<Writer<T>> {
59        let shared = self.get_shared();
60        if shared.try_create_other() {
61            Some(Writer {
62                shared: self.shared,
63                locked: false,
64                _owned: PhantomData,
65            })
66        } else {
67            None
68        }
69    }
70}
71
72unsafe impl<T: Send + Sync> Send for Reader<T> {}
73
74impl<T: Clone> Reader<T> {
75    pub fn new(value: T) -> Self {
76        Self {
77            shared: Shared::new(value),
78            _owned: PhantomData,
79            locked: false,
80        }
81    }
82}
83
84impl<T: Default> Default for Reader<T> {
85    fn default() -> Self {
86        Self {
87            shared: Shared::new_default(),
88            _owned: PhantomData,
89            locked: false,
90        }
91    }
92}
93
94impl<T> Drop for Reader<T> {
95    fn drop(&mut self) {
96        // SAFETY: ptr is valid and not used after this
97        unsafe {
98            Shared::drop(self.shared);
99        }
100        assert!(!self.locked, "ReadGuard was forgotten")
101    }
102}
103
104pub struct ReadGuard<'a, T> {
105    reader: &'a mut Reader<T>,
106    value: &'a T,
107}
108
109impl<T> Deref for ReadGuard<'_, T> {
110    type Target = T;
111
112    fn deref(&self) -> &Self::Target {
113        self.value
114    }
115}
116
117impl<T> Drop for ReadGuard<'_, T> {
118    fn drop(&mut self) {
119        self.reader.locked = false;
120        self.reader.get_shared().remove_current_read();
121    }
122}
123
124pub struct Writer<T> {
125    shared: NonNull<Shared<T>>,
126    _owned: PhantomData<T>,
127    locked: bool,
128}
129
130impl<T> Writer<T> {
131    fn get_shared(&self) -> &Shared<T> {
132        // SAFETY: the shared is always valid
133        unsafe { self.shared.as_ref() }
134    }
135
136    pub fn write(&mut self) -> WriteGuard<'_, T> {
137        if self.locked {
138            self.locked = false; // don't also panic in the destructor
139            panic!("WriteGuard was forgotten");
140        }
141        // locking here is always successfull, so we set it unconditionally
142        self.locked = true;
143        let shared = self.get_shared();
144        // SAFETY: won't be called again while the mut ref is still live, as the write method needs a mut ref
145        let pre_res = unsafe { shared.write() };
146        // SAFETY: was just locked
147        let value = unsafe { &mut *pre_res.0.get() };
148        let ptr = pre_res.1;
149
150        WriteGuard {
151            writer: self,
152            value,
153            next_read: ptr,
154        }
155    }
156}
157
158impl<T> Drop for Writer<T> {
159    fn drop(&mut self) {
160        // SAFETY: ptr is valid and not used after this
161        unsafe {
162            Shared::drop(self.shared);
163        }
164        assert!(!self.locked, "WriteGuard was forgotten")
165    }
166}
167
168unsafe impl<T: Send + Sync> Send for Writer<T> {}
169
170/// The data should always be replaced completely. Incremental updates can lead to the reader seeing inconsistent data,
171/// as the data structure uses two copies of the data internally and alternates between writing to both of them.
172/// The copies will start to diverge when doing changes instead of replacing it.
173///
174/// I still offer DerefMut instead of just taking a new T, because this allows reusing allocations inside of T instead of
175/// dropping and reallocing. This is important for realtime usecases.
176pub struct WriteGuard<'a, T> {
177    writer: &'a mut Writer<T>,
178    value: &'a mut T,
179    // if some: set the next_read bit to the ptr value for unlocking
180    // if none: set the read_allowed bit. The next_read bit is already correct
181    next_read: Option<Ptr>,
182}
183
184// i don't really want this. I would prefer to only write, but i need a mut ref to be able to write rt-safe.
185// and for DerefMut i need Deref.
186impl<T> Deref for WriteGuard<'_, T> {
187    type Target = T;
188
189    fn deref(&self) -> &Self::Target {
190        self.value
191    }
192}
193
194impl<T> DerefMut for WriteGuard<'_, T> {
195    fn deref_mut(&mut self) -> &mut Self::Target {
196        self.value
197    }
198}
199
200impl<T> Drop for WriteGuard<'_, T> {
201    fn drop(&mut self) {
202        self.writer.get_shared().unlock_write(self.next_read);
203        self.writer.locked = false;
204    }
205}
206
207#[cfg(test)]
208mod internal_tests {
209    use crate::Reader;
210
211    #[test]
212    fn drop_tests() {
213        let mut reader: Reader<u32> = Reader::default();
214        assert!(reader.get_shared().is_unique());
215        let writer = reader.get_writer().unwrap();
216        assert!(!writer.get_shared().is_unique());
217        assert!(!reader.get_shared().is_unique());
218
219        drop(writer);
220        assert!(reader.get_shared().is_unique());
221
222        let writer = reader.get_writer().unwrap();
223
224        drop(reader);
225        assert!(writer.get_shared().is_unique());
226    }
227}