simple_left_right/
lib.rs

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