orx_concurrent_vec/concurrent_slice/
unsafe_api.rs

1use super::ConcurrentSlice;
2use crate::ConcurrentElement;
3use core::sync::atomic::Ordering;
4use orx_pinned_vec::IntoConcurrentPinnedVec;
5
6impl<T, P> ConcurrentSlice<'_, T, P>
7where
8    P: IntoConcurrentPinnedVec<ConcurrentElement<T>>,
9{
10    /// Returns:
11    /// * a raw `*const T` pointer to the underlying data if element at the `i`-th position is pushed,
12    /// * `None` otherwise.
13    ///
14    /// # Safety
15    ///
16    /// Please see below the safety guarantees and potential safety risks using the pointer obtained by this method.
17    ///
18    /// ## Safety Guarantees
19    ///
20    /// Pointer obtained by this method will be valid:
21    ///
22    /// * `ConcurrentVec` prevents access to elements which are not added yet.
23    /// * `ConcurrentOption` wrapper prevents access during initialization, and hence, prevents data race during initialization.
24    /// * `PinnedVec` storage makes sure that memory location of the elements never change.
25    ///
26    /// Therefore, the caller can hold on the obtained pointer throughout the lifetime of the vec.
27    /// It is guaranteed that it will be valid pointing to the correct position with initialized data.
28    ///
29    /// ## Unsafe Bits
30    ///
31    /// However, this method still leaks out a pointer, using which can cause data races as follows:
32    /// * The value of the position can be `replace`d or `set` or `update`d concurrently by another thread.
33    /// * If at the same instant, we attempt to read using this pointer, we would end up with a data-race.
34    ///
35    /// ## Safe Usage
36    ///
37    /// This method can be safely used as long as the caller is able to guarantee that the position will not be being mutated
38    /// while using the pointer to directly access the data.
39    ///
40    /// A common use case to this is the grow-only scenarios where added elements are not mutated:
41    /// * elements can be added to the vector by multiple threads,
42    /// * while already pushed elements can safely be accessed by other threads using `get_raw`.
43    pub fn get_raw(&self, i: usize) -> Option<*const T> {
44        self.idx(i).and_then(|i| self.vec.get_raw(i))
45    }
46
47    /// Returns a reference to the element at the `i`-th position of the vec.
48    /// It returns `None` if index is out of bounds.
49    ///
50    /// # Safety
51    ///
52    /// All methods that return `&T` or `&mut T` references are marked as unsafe.
53    /// Please see the reason and possible scenarios to use it safely below.
54    ///
55    /// ## Safety Guarantees
56    ///
57    /// Reference obtained by this method will be valid:
58    ///
59    /// * `ConcurrentVec` prevents access to elements which are not added yet.
60    /// * `ConcurrentOption` wrapper prevents access during initialization, and hence, prevents data race during initialization.
61    /// * `PinnedVec` storage makes sure that memory location of the elements never change.
62    ///
63    /// Therefore, the caller can hold on the obtained reference throughout the lifetime of the vec.
64    /// It is guaranteed that the reference will be valid pointing to the correct position.
65    ///
66    /// ## Unsafe Bits
67    ///
68    /// However, this method still leaks out a reference, which can cause data races as follows:
69    /// * The value of the position can be `replace`d or `set` or `update`d concurrently by another thread.
70    /// * If at the same instant, we attempt to read using this reference, we would end up with a data-race.
71    ///
72    /// ## Safe Usage
73    ///
74    /// This method can be safely used as long as the caller is able to guarantee that the position will not be being mutated
75    /// while using the reference to directly access the data.
76    ///
77    /// A common use case to this is the grow-only scenarios where added elements are not mutated:
78    /// * elements can be added to the vector by multiple threads,
79    /// * while already pushed elements can safely be accessed by other threads using `get`.
80    ///
81    /// # Examples
82    ///
83    /// As explained above, the following constructs a safe usage example of the unsafe get method.
84    ///
85    /// ```rust
86    /// use orx_concurrent_vec::*;
87    /// use std::time::Duration;
88    ///
89    /// #[derive(Debug, Default)]
90    /// struct Metric {
91    ///     sum: i32,
92    ///     count: i32,
93    /// }
94    ///
95    /// impl Metric {
96    ///     fn aggregate(self, value: &i32) -> Self {
97    ///         Self {
98    ///             sum: self.sum + value,
99    ///             count: self.count + 1,
100    ///         }
101    ///     }
102    /// }
103    ///
104    /// // record measurements in random intervals, roughly every 2ms
105    /// let measurements = ConcurrentVec::new();
106    ///
107    /// // collect metrics every 100 milliseconds
108    /// let metrics = ConcurrentVec::new();
109    ///
110    /// std::thread::scope(|s| {
111    ///     // thread to store measurements as they arrive
112    ///     s.spawn(|| {
113    ///         for i in 0..100 {
114    ///             std::thread::sleep(Duration::from_millis(i % 5));
115    ///
116    ///             // collect measurements and push to measurements vec
117    ///             measurements.push(i as i32);
118    ///         }
119    ///     });
120    ///
121    ///     // thread to collect metrics every 100 milliseconds
122    ///     s.spawn(|| {
123    ///         for _ in 0..10 {
124    ///             // safely read from measurements vec to compute the metric
125    ///             // since pushed elements are not being mutated
126    ///             let len = measurements.len();
127    ///             let mut metric = Metric::default();
128    ///             for i in 0..len {
129    ///                 if let Some(value) = unsafe { measurements.get_ref(i) } {
130    ///                     metric = metric.aggregate(value);
131    ///                 }
132    ///             }
133    ///
134    ///             // push result to metrics
135    ///             metrics.push(metric);
136    ///
137    ///             std::thread::sleep(Duration::from_millis(100));
138    ///         }
139    ///     });
140    /// });
141    ///
142    /// let measurements: Vec<_> = measurements.to_vec();
143    /// let averages: Vec<_> = metrics.to_vec();
144    ///
145    /// assert_eq!(measurements.len(), 100);
146    /// assert_eq!(averages.len(), 10);
147    /// ```
148    pub unsafe fn get_ref(&self, i: usize) -> Option<&T> {
149        self.idx(i).and_then(|i| unsafe { self.vec.get_ref(i) })
150    }
151
152    /// Returns an iterator to references of elements of the vec.
153    ///
154    /// See also [`iter`] and [`iter_cloned`] for thread-safe alternatives of concurrent access to elements.
155    ///
156    /// [`iter`]: crate::ConcurrentVec::iter
157    /// [`iter_cloned`]: crate::ConcurrentVec::iter_cloned
158    ///
159    /// # Safety
160    ///
161    /// All methods that return `&T` or `&mut T` references are marked as unsafe.
162    /// Please see the reason and possible scenarios to use it safely below.
163    ///
164    /// ## Safety Guarantees
165    ///
166    /// References obtained by this method will be valid:
167    ///
168    /// * `ConcurrentVec` prevents access to elements which are not added yet.
169    /// * `ConcurrentOption` wrapper prevents access during initialization, and hence, prevents data race during initialization.
170    /// * `PinnedVec` storage makes sure that memory location of the elements never change.
171    ///
172    /// Therefore, the caller can hold on the obtained references throughout the lifetime of the vec.
173    /// It is guaranteed that the references will be valid pointing to the correct positions.
174    ///
175    /// ## Unsafe Bits
176    ///
177    /// However, this method still leaks out references that can cause data races as follows:
178    /// * Values of elements in the vector can be concurrently mutated by methods such as `replace` or `update` by other threads.
179    /// * If at the same instant, we attempt to read using these references, we would end up with a data-race.
180    ///
181    /// ## Safe Usage
182    ///
183    /// This method can be safely used as long as the caller is able to guarantee that the position will not be being mutated
184    /// while using these references to directly access the data.
185    ///
186    /// A common use case to this is the grow-only scenarios where added elements are not mutated:
187    /// * elements can be added to the vector by multiple threads,
188    /// * while already pushed elements can safely be accessed by other threads using `iter`.
189    ///
190    /// # Examples
191    ///
192    /// As explained above, the following constructs a safe usage example of the unsafe iter method.
193    ///
194    /// ```rust
195    /// use orx_concurrent_vec::*;
196    /// use std::time::Duration;
197    ///
198    /// #[derive(Debug, Default)]
199    /// struct Metric {
200    ///     sum: i32,
201    ///     count: i32,
202    /// }
203    ///
204    /// impl Metric {
205    ///     fn aggregate(self, value: &i32) -> Self {
206    ///         Self {
207    ///             sum: self.sum + value,
208    ///             count: self.count + 1,
209    ///         }
210    ///     }
211    /// }
212    ///
213    /// // record measurements in random intervals, roughly every 2ms
214    /// let measurements = ConcurrentVec::new();
215    ///
216    /// // collect metrics every 100 milliseconds
217    /// let metrics = ConcurrentVec::new();
218    ///
219    /// std::thread::scope(|s| {
220    ///     // thread to store measurements as they arrive
221    ///     s.spawn(|| {
222    ///         for i in 0..100 {
223    ///             std::thread::sleep(Duration::from_millis(i % 5));
224    ///
225    ///             // collect measurements and push to measurements vec
226    ///             measurements.push(i as i32);
227    ///         }
228    ///     });
229    ///
230    ///     // thread to collect metrics every 100 milliseconds
231    ///     s.spawn(|| {
232    ///         for _ in 0..10 {
233    ///             // safely read from measurements vec to compute the metric
234    ///             // since pushed elements are never mutated
235    ///             let metric = unsafe {
236    ///                 measurements
237    ///                     .iter_ref()
238    ///                     .fold(Metric::default(), |x, value| x.aggregate(value))
239    ///             };
240    ///
241    ///             // push result to metrics
242    ///             metrics.push(metric);
243    ///
244    ///             std::thread::sleep(Duration::from_millis(100));
245    ///         }
246    ///     });
247    /// });
248    ///
249    /// let measurements: Vec<_> = measurements.to_vec();
250    /// let averages: Vec<_> = metrics.to_vec();
251    ///
252    /// assert_eq!(measurements.len(), 100);
253    /// assert_eq!(averages.len(), 10);
254    /// ```
255    pub unsafe fn iter_ref(&self) -> impl Iterator<Item = &T> {
256        let b = self.a + self.len;
257        unsafe { self.vec.core.iter_over_range(self.a..b) }
258            .flat_map(|x| unsafe { x.0.as_ref_with_order(Ordering::SeqCst) })
259    }
260
261    // mut
262
263    /// Returns:
264    /// * a raw `*mut T` pointer to the underlying data if element at the `i`-th position is pushed,
265    /// * `None` otherwise.
266    ///
267    /// # Safety
268    ///
269    /// Please see below the safety guarantees and potential safety risks using the pointer obtained by this method.
270    ///
271    /// ## Safety Guarantees
272    ///
273    /// Pointer obtained by this method will be valid:
274    ///
275    /// * `ConcurrentVec` prevents access to elements which are not added yet.
276    /// * `ConcurrentOption` wrapper prevents access during initialization, and hence, prevents data race during initialization.
277    /// * `PinnedVec` storage makes sure that memory location of the elements never change.
278    ///
279    /// Therefore, the caller can hold on the obtained pointer throughout the lifetime of the vec.
280    /// It is guaranteed that it will be valid pointing to the correct position with initialized data.
281    ///
282    /// ## Unsafe Bits
283    ///
284    /// However, this method still leaks out a pointer, using which can cause data races as follows:
285    /// * The value of the position can be `replace`d or `set` or `update`d concurrently by another thread.
286    /// * If at the same instant, we attempt to read using this pointer, we would end up with a data-race.
287    ///
288    /// ## Safe Usage
289    ///
290    /// This method can be safely used as long as the caller is able to guarantee that the position will not be being
291    /// read or written by another thread while using the pointer to directly access the data.
292    pub fn get_raw_mut(&self, i: usize) -> Option<*mut T> {
293        self.idx(i).and_then(|i| self.vec.get_raw_mut(i))
294    }
295
296    /// Returns a mutable reference to the element at the `i`-th position of the vec.
297    /// It returns `None` if index is out of bounds.
298    ///
299    /// See also [`get`] and [`swap`] for thread-safe alternatives of concurrent mutation of elements.
300    ///
301    /// [`get`]: crate::ConcurrentVec::get
302    /// [`swap`]: crate::ConcurrentVec::swap
303    ///
304    /// # Safety
305    ///
306    /// All methods that leak out `&T` or `&mut T` references are marked as unsafe.
307    /// Please see the reason and possible scenarios to use it safely below.
308    ///
309    /// ## Safety Guarantees
310    ///
311    /// Reference obtained by this method will be valid:
312    ///
313    /// * `ConcurrentVec` prevents access to elements which are not added yet.
314    /// * `ConcurrentOption` wrapper prevents access during initialization, and hence, prevents data race during initialization.
315    /// * `PinnedVec` storage makes sure that memory location of the elements never change.
316    ///
317    /// Therefore, the caller can hold on the obtained reference throughout the lifetime of the vec.
318    /// It is guaranteed that the reference will be valid pointing to the correct position.
319    ///
320    /// ## Unsafe Bits
321    ///
322    /// However, this method still leaks out a reference, which can cause data races as follows:
323    /// * The value of the position can be `replace`d or `set` or `update`d concurrently by another thread.
324    /// * And it maybe read by safe access methods such as `map` or `cloned`.
325    /// * If at the same instant, we attempt to read or write using this reference, we would end up with a data-race.
326    ///
327    /// ## Safe Usage
328    ///
329    /// This method can be safely used as long as the caller is able to guarantee that the position will not be being
330    /// read or written by another thread while using the reference to directly access the data.
331    ///
332    /// # Examples
333    ///
334    /// ```rust
335    /// use orx_concurrent_vec::*;
336    ///
337    /// let vec = ConcurrentVec::new();
338    /// vec.extend(['a', 'b', 'c', 'd']);
339    ///
340    /// assert_eq!(unsafe { vec.get_mut(4) }, None);
341    ///
342    /// *unsafe { vec.get_mut(1).unwrap() } = 'x';
343    /// assert_eq!(unsafe { vec.get_ref(1) }, Some(&'x'));
344    ///
345    /// assert_eq!(&vec, &['a', 'x', 'c', 'd']);
346    /// ```
347    #[allow(clippy::mut_from_ref)]
348    pub unsafe fn get_mut(&self, i: usize) -> Option<&mut T> {
349        self.idx(i).and_then(|i| unsafe { self.vec.get_mut(i) })
350    }
351
352    /// Returns an iterator to mutable references of elements of the vec.
353    ///
354    /// See also [`iter`], [`fill`] and [`fill_with`] for thread-safe alternatives of concurrent mutation of elements.
355    ///
356    /// [`iter`]: crate::ConcurrentVec::iter
357    /// [`fill`]: crate::ConcurrentVec::fill
358    /// [`fill_with`]: crate::ConcurrentVec::fill_with
359    ///
360    /// # Safety
361    ///
362    /// All methods that leak out `&T` or `&mut T` references are marked as unsafe.
363    /// Please see the reason and possible scenarios to use it safely below.
364    ///
365    /// ## Safety Guarantees
366    ///
367    /// References obtained by this method will be valid:
368    ///
369    /// * `ConcurrentVec` prevents access to elements which are not added yet.
370    /// * `ConcurrentOption` wrapper prevents access during initialization, and hence, prevents data race during initialization.
371    /// * `PinnedVec` storage makes sure that memory location of the elements never change.
372    ///
373    /// Therefore, the caller can hold on the obtained references throughout the lifetime of the vec.
374    /// It is guaranteed that the references will be valid pointing to the correct position.
375    ///
376    /// ## Unsafe Bits
377    ///
378    /// However, this method still leaks out references, which can cause data races as follows:
379    /// * Values of elements can be concurrently read by other threads.
380    /// * Likewise, they can be concurrently mutated by thread-safe mutation methods.
381    /// * If at the same instant, we attempt to read or write using these references, we would end up with a data-race.
382    ///
383    /// ## Safe Usage
384    ///
385    /// This method can be safely used as long as the caller is able to guarantee that the elements will not be being
386    /// read or written by another thread while using the reference to directly access the data.
387    ///
388    /// # Examples
389    ///
390    /// ```rust
391    /// use orx_concurrent_vec::*;
392    ///
393    /// let vec = ConcurrentVec::from_iter([0, 1, 2, 3]);
394    ///
395    /// let iter = unsafe { vec.iter_mut() };
396    /// for x in iter {
397    ///     *x *= 2;
398    /// }
399    ///
400    /// assert_eq!(&vec, &[0, 2, 4, 6]);
401    /// ```
402    pub unsafe fn iter_mut(&self) -> impl Iterator<Item = &mut T> {
403        let b = self.a + self.len;
404        unsafe { self.vec.core.iter_over_range(self.a..b) }
405            .flat_map(|x| x.0.get_raw_mut().map(|p| unsafe { &mut *p }))
406    }
407}