# Safe Concurrent Grow & Read & Update `ConcurrentVec` provides safe api for the following three sets of concurrent operations: **growth** & **read** & **update**. ## Growth ConcurrentVec is designed with having high performance growth in focus. * `push` allows to push elements one at a time, returning the index in the vector that the value is written to. * `extend` allows pushing all values that an iterator yields next to each other, returning the index in the vector that the first value is written to. Note that `extend` is a great tool to deal with "false sharing" and might provide significant performance improvements when high-performance growth is required (see the [benchmarks](https://docs.rs/orx-concurrent-vec/latest/orx_concurrent_vec/#benchmarks)). ## Read Safe api of `ConcurrentVec` and `ConcurrentSlice` does not provide out `&T` or `&mut T` references to guarantee a safe access to the elements. Instead, it provides a reference to a concurrent element: `vec.get[i]` and `vec[i]` returns `Option<&ConcurrentElement>` and `&ConcurrentElement`, respectively. [`ConcurrentElement`](https://docs.rs/orx-concurrent-vec/latest/orx_concurrent_vec/struct.ConcurrentElement.html) provides thread-safe means to work with the data, such as: * `map(f)` where `f: FnOnce(&T) -> U`, returns the result of the map operation. Of course, `U` can be the unit type when we only need to access the data but not a result. * `cloned()` returns a clone of value of the element. Alternatively, when we only need the clone of the value, we can directly get it from the vector via `vec.get_cloned(i)`. ## Update We can also concurrently update the values of elements. Similarly, mutation is not through providing `&mut T` but instead using the thread-safe methods of `ConcurrentElement`: * `update(f)` where `f: FnMut(&mut T)` applies the update function on the value; this can be considered as the thread-safe counter-part of `get_mut`. * `set(value)` overwrites the element's value. * `replace(value)` is similar to *set* except that it additionally returns the prior value. ## Example - #1 We can execute all these three sets of operations concurrently from multiple threads in the absence of race conditions and locks. This is demonstrated in the [random_actions](https://github.com/orxfun/orx-concurrent-vec/blob/main/examples/random_actions.rs) example. Here, we spawn a number of threads, each of which continuously randomly picks an action and applies it concurrently. It is a toy example, however, demonstrates possible thread-safe concurrent actions and how to use them. ``` cargo run --example random_actions -- --help ``` ```rust ignore fn apply_random_concurrent_operations( vec: &ConcurrentVec, final_vec_len: usize, mut r: ChaCha8Rng, ) { while vec.len() < final_vec_len { match ConAction::new(&mut r, vec.len()) { ConAction::Push(value) => { vec.push(value); } ConAction::Extend(iter) => { vec.extend(iter); } ConAction::Map(i) => { // when we know i is in-bounds let _num_chars = vec[i].map(|x: &String| x.len()); } ConAction::Clone(i) => { let _clone: Option = vec.get_cloned(i); } ConAction::Replace(i, new_value) => { // when we are not sure if i is in or out of bounds if let Some(elem) = vec.get(i) { let old_value = elem.replace(new_value); assert!(old_value.parse::().is_ok()); } } ConAction::Set(i, new_value) => { // when we know i is in-bounds vec[i].set(new_value); } ConAction::Update(i, c) => { if let Some(elem) = vec.get(i) { elem.update(|x| x.push(c)); } } ConAction::IterMapReduce => { let sum: usize = vec .iter() .map(|elem| elem.map(|x| x.parse::().unwrap())) .sum(); assert!(sum > 0); } ConAction::IterCloned(range) => { let _collected: Vec<_> = vec.slice(range).iter_cloned().collect(); } ConAction::IterMutate => { vec.iter().for_each(|elem| { elem.update(|x: &mut String| { if x.len() > 1 { x.pop(); } }); }); } } } } ``` ## Example - #2 In the second example, each thread have their own responsibilities: some of them add elements to the vector, some update existing elements and some read data. This is demonstrated in the [concurrent_grow_read_update](https://github.com/orxfun/orx-concurrent-vec/blob/main/examples/concurrent_grow_read_update.rs) example. ```rust ignore use orx_concurrent_vec::*; let con_vec = ConcurrentVec::new(); std::thread::scope(|s| { for _ in 0..args.num_pushers { s.spawn(|| push(&con_vec, num_items_per_thread, args.lag)); } for _ in 0..args.num_extenders { s.spawn(|| extend(&con_vec, num_items_per_thread, 64, args.lag)); } for _ in 0..args.num_readers { s.spawn(|| read(&con_vec, final_len, args.lag)); } for _ in 0..args.num_updaters { s.spawn(|| update(&con_vec, final_len, args.lag)); } for _ in 0..args.num_iterators { s.spawn(|| iterate(&con_vec, final_len)); } }); // convert into "non-concurrent" vec let vec = con_vec.into_inner(); for (i, x) in vec.iter().enumerate() { assert_eq!(x, &i.to_string()); } ``` You may find the complete example here: [concurrent_grow_read_update](https://github.com/orxfun/orx-concurrent-vec/blob/main/examples/concurrent_grow_read_update.rs) or try out: ``` cargo run --example concurrent_grow_read_update -- --help ```