stele/append/writer.rs
1use core::marker::PhantomData;
2
3use crate::{sync::Arc, ReadHandle, Stele};
4
5/// The writer for a [`Stele`]
6///
7/// This is the only type capable of writing to the underlying [`Stele`] and so some limitaions are in place:
8///
9/// - ## Why is [`WriteHandle`] Send but !Sync?
10///
11/// This must be `!Sync` because while you can safely reserve a slot to avoid write-write conflicts
12/// in any one memory location using [`fetch_add`](core::sync::atomic::AtomicUsize::fetch_add),
13/// there can still be a race where a concurrent push while a previous push is still allocating can
14/// segfault as readers can see the new length before memory is written.
15///
16/// - ## Why can I only append?
17///
18/// Append-only concurrent data structures avoid a major problem in the concurrent data structure space:
19/// Memory Reclamation. By only allowing appending elements and not mutation or removal, there cannot be a read-write data race,
20/// and all data is reclaimed if and only if there are no more handles left,
21/// at which point there cannot be any way to access the data inside and therefore we leave no dangling references.
22#[derive(Debug)]
23pub struct WriteHandle<T> {
24 pub(crate) handle: Arc<Stele<T>>,
25 pub(crate) _unsync: PhantomData<*mut T>,
26}
27
28//SAFETY: WriteHandle only provides immutable references to its contents and uses atomic operations internally
29//so as long as the type of its items are both Send and Sync it is safe to implement Send
30unsafe impl<T> Send for WriteHandle<T> where T: Send + Sync {}
31
32impl<T> WriteHandle<T> {
33 /// Pushes a new item on to the end of the [`Stele`], allocating a new block of memory if necessary
34 pub fn push(&self, val: T) {
35 //SAFETY: WriteHandle is neither Sync nor Clone so only one exists at a time
36 //and can only be used by one thread at a time
37 unsafe {
38 self.handle.push(val);
39 };
40 }
41
42 /// Creates a new [`ReadHandle`]
43 #[must_use]
44 pub fn new_read_handle(&self) -> ReadHandle<T> {
45 ReadHandle::from(&self.handle)
46 }
47
48 /// Reads the value at the given index
49 ///
50 /// # Panic
51 ///
52 /// This function panics in debug if the given index is out of bounds.
53 #[must_use]
54 pub fn read(&self, idx: usize) -> &T {
55 self.handle.read(idx)
56 }
57
58 /// Attempts to read the value at the index and returns [`Some`] if the value exists, and [`None`] otherwise
59 #[must_use]
60 pub fn try_read(&self, idx: usize) -> Option<&T> {
61 self.handle.try_read(idx)
62 }
63
64 /// Returns the current length of the underlying [`Stele`]
65 ///
66 /// Note:
67 /// By calling this through the [`WriteHandle`], you hold the only handle that can change the
68 /// length and therefore this information is accurate until the next call to [`push`](WriteHandle::push)
69 #[must_use]
70 pub fn len(&self) -> usize {
71 self.handle.len()
72 }
73
74 /// Returns whether the underlying [`Stele`] is empty or not
75 ///
76 /// Note:
77 /// By calling this through the [`WriteHandle`], you hold the only handle that can change the
78 /// length and therefore this information is accurate until the first call to [`push`](WriteHandle::push) if it
79 /// returned `true`, and will remain accurate again after that as a [`Stele`] cannot remove elements
80 #[must_use]
81 pub fn is_empty(&self) -> bool {
82 self.handle.is_empty()
83 }
84}
85
86impl<T: Copy> WriteHandle<T> {
87 /// Get provides a way to get an owned copy of a value inside a [`Stele`]
88 /// provided the type `T` implements [`Copy`]
89 ///
90 /// # Panic
91 ///
92 /// This function panics in debug if the given index is out of bounds
93 #[must_use]
94 pub fn get(&self, idx: usize) -> T {
95 self.handle.get(idx)
96 }
97}