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}