simple_left_right/
lib.rs

1// SPDX-FileCopyrightText: 2025 Lucas Baumann
2// SPDX-FileCopyrightText: Lucas Baumann
3//
4// SPDX-License-Identifier: Apache-2.0
5// SPDX-License-Identifier: MIT
6
7//! Simpler version of the left-right from Jon Gjengset library.
8//!
9//! Uses two copies of the value to allow doing small changes, while still allowing non-blocking reading.
10//! Writing can block, while reading doesn't.
11#![no_std]
12
13extern crate alloc;
14
15use core::{marker::PhantomData, ops::Deref, ptr::NonNull};
16
17use alloc::collections::vec_deque::VecDeque;
18
19mod shared;
20
21use shared::{Ptr, Shared};
22
23/// Should be implemented on structs that want to be shared with this library
24pub trait Absorb<O> {
25    /// has to be deterministic. Operations will be applied in the same order to both buffers
26    fn absorb(&mut self, operation: O);
27}
28
29/// Dropping the Reader isn't realtime safe, because if dropped after the Writer, it deallocates.
30/// Should only get dropped, when closing the real-time thread
31///
32/// Reader will be able to read data even if Writer has been dropped. Obviously that data won't change anymore
33/// When there is no Reader the Writer is able to create a new one. The other way around doesn't work.
34///
35/// Isn't Sync as there is no methos that takes &self, so it is useless anyways.
36#[derive(Debug)]
37pub struct Reader<T> {
38    shared: NonNull<Shared<T>>,
39    /// for drop check
40    _own: PhantomData<Shared<T>>,
41}
42
43impl<T> Reader<T> {
44    fn shared_ref(&self) -> &Shared<T> {
45        // SAFETY: Reader always has a valid Shared<T>, a mut ref to a shared is never created,
46        // only to the UnsafeCell<T>s inside of it
47        unsafe { self.shared.as_ref() }
48    }
49
50    /// this function never blocks. (`fetch_update` loop doesn't count)
51    pub fn lock(&mut self) -> ReadGuard<'_, T> {
52        let shared_ref = self.shared_ref();
53
54        ReadGuard {
55            shared: shared_ref,
56            value: shared_ref.lock_read(),
57            reader: PhantomData,
58        }
59    }
60}
61
62/// SAFETY: Owns a T
63unsafe impl<T: Send> Send for Reader<T> {}
64
65impl<T> Drop for Reader<T> {
66    fn drop(&mut self) {
67        // SAFETY: self.shared is valid and not used after this.
68        unsafe { Shared::drop(self.shared) };
69    }
70}
71
72/// Data won't change while holding the Guard. This also means the Writer can only issue one swap, while Guard is being held
73/// If T: !Sync this is guaranteed to be the only ref to this T
74///
75/// Doesn't implement Clone as that would require refcounting to know when to unlock.
76#[derive(Debug)]
77pub struct ReadGuard<'a, T> {
78    shared: &'a Shared<T>,
79    value: Ptr,
80    /// `PhantomData` makes the borrow checker prove that there only ever is one `ReadGuard`.
81    /// This allows resetting the readstate without some kind of counter
82    reader: PhantomData<&'a mut Reader<T>>,
83}
84
85impl<T> Deref for ReadGuard<'_, T> {
86    type Target = T;
87
88    fn deref(&self) -> &Self::Target {
89        // SAFETY: ReadGuard was created, so the Writer knows not to write in this spot
90        unsafe { self.shared.get_value_ref(self.value) }
91    }
92}
93
94impl<T, E> AsRef<E> for ReadGuard<'_, T>
95where
96    E: ?Sized,
97    T: AsRef<E>,
98{
99    fn as_ref(&self) -> &E {
100        self.deref().as_ref()
101    }
102}
103
104// /// SAFETY: behaves like a ref to T. https://doc.rust-lang.org/std/marker/trait.Sync.html
105// unsafe impl<T: Sync> Send for ReadGuard<'_, T> {}
106// /// SAFETY: behaves like a ref to T. https://doc.rust-lang.org/std/marker/trait.Sync.html
107// unsafe impl<T: Sync> Sync for ReadGuard<'_, T> {}
108
109impl<T> Drop for ReadGuard<'_, T> {
110    fn drop(&mut self) {
111        // release the read lock
112        self.shared.release_read_lock();
113    }
114}
115
116/// Not realtime safe object which can change the internal T value.
117#[derive(Debug)]
118pub struct Writer<T, O> {
119    shared: NonNull<Shared<T>>,
120    // sets which buffer the next write is applied to
121    // write_ptr doesn't need to be Atomics as it only changes, when the Writer itself swaps
122    write_ptr: Ptr,
123    // buffer is pushed at the back and popped at the front.
124    op_buffer: VecDeque<O>,
125    // needed for drop_check
126    _own: PhantomData<Shared<T>>,
127}
128
129impl<T, O> Writer<T, O> {
130    fn shared_ref(&self) -> &Shared<T> {
131        // SAFETY: Reader always has a valid Shared<T>, the only possibility to get a &mut Shared requires &mut self
132        unsafe { self.shared.as_ref() }
133    }
134
135    /// if no Reader exists this gives a mut ref to Shared.
136    fn shared_mut(&mut self) -> Option<&mut Shared<T>> {
137        self.shared_ref()
138            .is_unique()
139            // SAFETY: No `Reader` exists, as `is_unique` returns true
140            .then(|| unsafe { &mut *self.shared.as_ptr() })
141    }
142
143    /// swaps the read and write values. If no changes were made since the last swap nothing happens. Never blocks
144    /// not public as swapping without creating a before `WriteGuard` is pretty useless
145    fn swap(&mut self) {
146        if self.op_buffer.is_empty() {
147            return;
148        }
149
150        self.shared_ref().set_read_ptr(self.write_ptr);
151
152        self.write_ptr.switch();
153    }
154
155    /// get a Reader if none exists
156    pub fn build_reader(&mut self) -> Option<Reader<T>> {
157        let shared_ref = self.shared_ref();
158        // SAFETY: all is_unique_with_increase requirements are satisfied.
159        unsafe {
160            shared_ref.is_unique().then(|| {
161                shared_ref.set_shared();
162                Reader {
163                    shared: self.shared,
164                    _own: PhantomData,
165                }
166            })
167        }
168    }
169}
170
171impl<T: Absorb<O>, O> Writer<T, O> {
172    /// doesn't block. Returns None if the Reader has a `ReadGuard` pointing to the old value.
173    pub fn try_lock(&mut self) -> Option<WriteGuard<'_, T, O>> {
174        self.shared_ref()
175            .lock_write(self.write_ptr)
176            .ok()
177            // locking was successful
178            .map(|()| {
179                // WriteGuard::new(self)
180                let mut guard = WriteGuard { writer: self };
181                while let Some(operation) = guard.writer.op_buffer.pop_front() {
182                    guard.get_data_mut().absorb(operation);
183                }
184                guard
185            })
186    }
187}
188
189impl<T: Clone, O> Writer<T, O> {
190    /// Creates a new Writer by cloning the value once to get two values
191    /// `T::clone()` shoulnd't give a different value, as that would make this library pretty useless
192    pub fn new(value: T) -> Self {
193        let (shared, write_ptr) = Shared::new(value, |value_1| value_1.clone());
194        Self {
195            shared,
196            write_ptr,
197            op_buffer: VecDeque::new(),
198            _own: PhantomData,
199        }
200    }
201}
202
203impl<T: Default, O> Default for Writer<T, O> {
204    /// Creates a new Writer by calling `T::default()` twice to create the two values
205    ///
206    /// Default impl of T needs to give the same result every time. Not upholding this doens't lead to UB, but turns the library basically useless
207    fn default() -> Self {
208        let (shared, write_ptr) = Shared::new(T::default(), |_| T::default());
209        Self {
210            shared,
211            write_ptr,
212            op_buffer: VecDeque::new(),
213            _own: PhantomData,
214        }
215    }
216}
217
218impl<T: Sync, O> Writer<T, O> {
219    /// The Value returned may be newer than the version the reader is currently seeing.
220    /// This value will be written to next.
221    /// If this is called after swapping the write_lock this will return an older value.
222    /// To get the newest value lock the writer and call `Writeguard::read`.
223    ///
224    /// Needs T: Sync because maybe this is the value the reader is curently reading
225    pub fn read(&self) -> &T {
226        // SAFETY: Only the WriteGuard can write to the values / create mut refs to them.
227        // The WriteGuard holds a mut ref to the writer so this function can't be called while a writeguard exists
228        // This means that reading them / creating refs is safe to do
229        unsafe { self.shared_ref().get_value_ref(self.write_ptr) }
230    }
231}
232
233/// SAFETY: owns T and O
234unsafe impl<T: Send, O: Send> Send for Writer<T, O> {}
235/// SAFETY: &self fn can only create a &T and never gives shared access to O
236unsafe impl<T: Sync, O> Sync for Writer<T, O> {}
237
238impl<T, O> Drop for Writer<T, O> {
239    fn drop(&mut self) {
240        // SAFETY: self.shared is valid and not used after this.
241        unsafe { Shared::drop(self.shared) };
242    }
243}
244
245// Don't create a WriteGuard directly, as that wouldn't sync with old Operations
246/// Can be used to write to the Data structure.
247///
248/// When this structure exists the Reader already switched to the other value
249///
250/// Dropping this makes all changes available to the Reader.
251#[derive(Debug)]
252pub struct WriteGuard<'a, T, O> {
253    writer: &'a mut Writer<T, O>,
254}
255
256impl<T, O> WriteGuard<'_, T, O> {
257    /// Makes the changes available to the reader. Equivalent to `std::mem::drop(self)`
258    pub fn swap(self) {}
259
260    /// Gets the value currently being written to.
261    pub fn read(&self) -> &T {
262        // SAFETY: Only the WriteGuard can write to the values / create mut refs to them.
263        // The WriteGuard holds a mut ref to the writer so this function can't be called while a writeguard exists
264        // This means that reading them / creating refs is safe to do
265        unsafe {
266            self.writer
267                .shared_ref()
268                .get_value_ref(self.writer.write_ptr)
269        }
270    }
271
272    /// Isn't public as this could easily create disconnects between the two versions.
273    /// While that wouldn't lead to UB it goes against the purpose of this library
274    fn get_data_mut(&mut self) -> &mut T {
275        // SAFETY: When creating the writeguad it is checked that the reader doesnt have access to the same data
276        // This function requires &mut self so there also isn't any ref created by writeguard.
277        unsafe {
278            &mut *self
279                .writer
280                .shared_ref()
281                .get_value(self.writer.write_ptr)
282                .get()
283        }
284    }
285}
286
287impl<T: Absorb<O>, O: Clone> WriteGuard<'_, T, O> {
288    /// applies operation to the current write Value and stores it to apply to the other later.
289    /// If there is no reader the operation is applied to both values immediately and not stored.
290    pub fn apply_op(&mut self, operation: O) {
291        if let Some(shared) = self.writer.shared_mut() {
292            shared.value_1.get_mut().absorb(operation.clone());
293            shared.value_2.get_mut().absorb(operation);
294        } else {
295            self.writer.op_buffer.push_back(operation.clone());
296            self.get_data_mut().absorb(operation);
297        }
298    }
299}
300
301// /// SAFETY: behaves like a &mut T and &mut Vec<O>. https://doc.rust-lang.org/stable/std/marker/trait.Sync.html
302// unsafe impl<T: Send, O: Send> Send for WriteGuard<'_, T, O> {}
303
304// /// Safety: can only create shared refs to T, not to O. https://doc.rust-lang.org/stable/std/marker/trait.Sync.html
305// unsafe impl<T: Sync, O> Sync for WriteGuard<'_, T, O> {}
306
307impl<T, O> Drop for WriteGuard<'_, T, O> {
308    fn drop(&mut self) {
309        self.writer.swap();
310    }
311}
312
313#[cfg(test)]
314mod internal_test {
315    use core::cell::Cell;
316
317    use crate::{Absorb, Writer};
318
319    #[derive(Clone, Copy, Debug)]
320    pub struct CounterAddOp(i32);
321
322    impl Absorb<CounterAddOp> for i32 {
323        fn absorb(&mut self, operation: CounterAddOp) {
324            *self += operation.0;
325        }
326    }
327
328    impl Absorb<CounterAddOp> for Cell<i32> {
329        fn absorb(&mut self, operation: CounterAddOp) {
330            self.set(self.get() + operation.0);
331        }
332    }
333
334    #[test]
335    fn drop_reader() {
336        let mut writer: Writer<i32, CounterAddOp> = Writer::default();
337        let reader = writer.build_reader().unwrap();
338
339        assert!(!writer.shared_ref().is_unique());
340        drop(reader);
341        assert!(writer.shared_ref().is_unique());
342    }
343
344    #[test]
345    fn drop_writer() {
346        let mut writer: Writer<i32, CounterAddOp> = Writer::default();
347        let reader = writer.build_reader().unwrap();
348
349        assert!(!reader.shared_ref().is_unique());
350        drop(writer);
351        assert!(reader.shared_ref().is_unique());
352    }
353}