use clap::Parser;
use orx_concurrent_vec::*;
use orx_iterable::*;
use rand::prelude::*;
use rand_chacha::ChaCha8Rng;
fn draw<'a, T>(r: &mut ChaCha8Rng, candidates: &'a [T]) -> &'a T {
&candidates[r.random_range(0..candidates.len())]
}
fn push(vec: &ConcurrentVec<String>, num_items_to_add: usize, lag: u64) {
for _ in 0..num_items_to_add {
std::thread::sleep(std::time::Duration::from_micros(lag));
vec.push_for_idx(|idx| idx.to_string());
}
}
fn extend(vec: &ConcurrentVec<String>, num_items_to_add: usize, batch_size: usize, lag: u64) {
let mut num_added = 0;
while num_added < num_items_to_add {
std::thread::sleep(std::time::Duration::from_micros(lag));
let extend_len = match num_items_to_add - num_added > batch_size {
true => batch_size,
false => num_items_to_add - num_added,
};
let iter = |begin_idx: usize| (begin_idx..(begin_idx + extend_len)).map(|j| j.to_string());
vec.extend_for_idx(iter, extend_len);
num_added += extend_len;
}
}
fn read(vec: &ConcurrentVec<String>, final_len: usize, lag: u64) {
enum Read {
Map,
GetCloned,
Eq,
}
const READ_OPS: [Read; 3] = [Read::Map, Read::GetCloned, Read::Eq];
let mut rng = ChaCha8Rng::seed_from_u64(5648);
while vec.len() < final_len {
let slice = vec.as_slice();
for _ in 0..1000 {
std::thread::sleep(std::time::Duration::from_micros(lag));
let idx = rng.random_range(0..slice.len());
match draw(&mut rng, &READ_OPS) {
Read::Map => {
let mapped = slice[idx].map(|x| format!("{}!", x));
assert_eq!(mapped, format!("{}!", idx));
}
Read::GetCloned => {
let clone: Option<String> = slice.get_cloned(idx);
assert_eq!(clone, Some(idx.to_string()));
}
Read::Eq => {
assert_eq!(slice[idx], idx.to_string())
}
}
}
}
}
fn iterate(vec: &ConcurrentVec<String>, final_len: usize) {
while vec.len() < final_len {
let slice = vec.as_slice();
let sum1: usize = slice
.iter()
.map(|elem| elem.map(|x| x.parse::<usize>().unwrap()))
.sum();
let sum2: usize = slice
.iter_cloned()
.map(|x| x.parse::<usize>().unwrap())
.sum();
assert_eq!(sum1, sum2);
}
let final_sum: usize = vec
.iter()
.map(|elem| elem.map(|x| x.parse::<usize>().unwrap()))
.sum();
let expected_final_sum = final_len * (final_len - 1) / 2;
assert_eq!(final_sum, expected_final_sum);
}
fn update(vec: &ConcurrentVec<String>, final_len: usize, lag: u64) {
enum Update {
Update,
Set,
Replace,
}
const UPDATE_OPS: [Update; 3] = [Update::Update, Update::Set, Update::Replace];
let mut rng = ChaCha8Rng::seed_from_u64(47);
while vec.len() < final_len {
let slice = vec.as_slice();
for _ in 0..1000 {
std::thread::sleep(std::time::Duration::from_micros(lag));
let idx = rng.random_range(0..slice.len());
match draw(&mut rng, &UPDATE_OPS) {
Update::Update => {
slice[idx].update(|x| {
let number: usize = x.parse().unwrap();
*x = number.to_string();
});
}
Update::Set => slice[idx].set(idx.to_string()),
Update::Replace => {
let new_value = idx.to_string();
let old_value = slice[idx].replace(new_value);
assert_eq!(old_value, idx.to_string());
}
}
}
}
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(long, default_value_t = 0)]
lag: u64,
#[arg(long, default_value_t = 1000)]
len: usize,
#[arg(long, default_value_t = 2)]
num_pushers: usize,
#[arg(long, default_value_t = 2)]
num_extenders: usize,
#[arg(long, default_value_t = 2)]
num_readers: usize,
#[arg(long, default_value_t = 2)]
num_updaters: usize,
#[arg(long, default_value_t = 2)]
num_iterators: usize,
}
fn main() {
let args = Args::parse();
assert!(args.num_pushers + args.num_extenders > 0);
let num_items_per_thread = args.len / (args.num_pushers + args.num_extenders);
let final_len = num_items_per_thread * (args.num_pushers + args.num_extenders);
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));
}
});
assert_eq!(con_vec.len(), final_len);
let vec = con_vec.into_inner();
for (i, x) in vec.iter().enumerate() {
assert_eq!(x, &i.to_string());
}
}