orx_concurrent_vec/
unsafe_api.rs

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