concurrent, realtime write data structure.
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}