From 729786a568edf8da965576cd6ff7b905f2e79bc1 Mon Sep 17 00:00:00 2001 From: Avril Date: Thu, 24 Feb 2022 03:09:49 +0000 Subject: [PATCH] Tests: Added non-overlapping multithreaded iteration testing for trivial an non-trivial values. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO: Next, add overlapping multithreaded iteration with trivial and nontrivial values. Fortune for parapop's current commit: Future small blessing − 末小吉 --- src/lib.rs | 40 ++++++++++++++++++++++++++ src/tests.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 6ca4bd6..c886d28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -396,6 +396,21 @@ impl<'a, T> Populator<'a, T> } } + + /// If all values are populated and the `Arc` has no other strong references, then convert it into a boxed slice and return it. + /// + /// Otherwise, return the arc. + /// + /// # If the values are not all populated + /// But the `Arc` is empty, then a new, single strong reference arc is constructed around the value and returned as its `Err`. + pub fn try_complete_owned(self: Arc) -> Result, Arc> + { + match Arc::try_unwrap(self) { + Ok(extracted) => extracted.try_complete().map_err(Arc::new), + Err(e) => Err(e), + } + } + /// If all values are populated, returns a slice of all the elements. /// /// Performs a single atomic load of the number of currently inserted elements to check for completion @@ -460,6 +475,30 @@ impl<'a, T> Populator<'a, T> } } + /// Returns the completed population from an `Arc` that has no more than 1 reference. + /// + /// # Panics + /// * If the `Arc` has more than one strong reference + /// * If the collection is not fully populated + pub fn complete_owned(self: Arc) -> Box<[T]> + { + #[inline(never)] + #[cold] + fn panic_uncomplete_or_shared(values: &Arc) -> ! + { + let sc = Arc::strong_count(values); + if sc > 1 { + panic!("More than one ({}) reference to the `Arc` holding this instance", sc) + } else { + panic!("Not all values had been populated") + } + } + match self.try_complete_owned() { + Ok(v) => v, + Err(ref e) => panic_uncomplete_or_shared(e), + } + } + /// Create an iterator over references to a completed population if it is completed. #[inline] pub fn try_completed_iter(&self) -> Option> @@ -522,6 +561,7 @@ impl<'a, T> Populator<'a, T> iter::Iter::new_range(self, range) } + //TODO: `self: Arc` version of iter_slice()/iter() } impl<'a, T: 'a> FromIterator> for Populator<'a, T> diff --git a/src/tests.rs b/src/tests.rs index e4f8782..2c7a5f4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,6 +1,22 @@ //! Unit tests +use std::sync::Arc; +use std::thread::{ + self, +}; use super::*; +#[macro_export] macro_rules! assert_binds { + ($expr:expr, $pat:pat, $msg:literal) => { + match $expr { + $pat => (), + _ => { + panic!("Pattern matched assertion `{}` failed: {}", stringify!($pat), $msg) + } + } + } +} + + /// Testing iteration mod iteration { use super::*; @@ -30,4 +46,67 @@ mod iteration { .map(|x| x.parse::().unwrap()) .sum::(), "Did not set all elements to 1"); } + + #[test] + fn multi_threaded_trivial() + { + let pop = Arc::new(Populator::::new(10)); + + let t1 = thread::spawn({ + let pop = pop.clone(); + move || { + for (i,r) in (10..).zip(pop.iter_slice(0..5)) { + r.insert(i); + } + } + }); + let t2 = thread::spawn({ + let pop = pop.clone(); + move || { + for (i, r) in (0..).zip(pop.iter_slice(5..10)) { + r.insert(i); + } + } + }); + thread::yield_now(); + + assert_binds!((t1.join(), t2.join()), (Ok(_), Ok(_)), "One or more of the threads panicked"); + + assert_eq!(&pop.complete_owned()[..], &[ + 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, + ], "Bad order of elements"); + } + + + #[test] + fn multi_threaded_nontrivial() + { + let pop = Arc::new(Populator::::new(10)); + + let t1 = thread::spawn({ + let pop = pop.clone(); + move || { + for (i,r) in (10..).zip(pop.iter_slice(0..5)) { + r.insert(i.to_string()); + } + } + }); + let t2 = thread::spawn({ + let pop = pop.clone(); + move || { + for (i, r) in (0..).zip(pop.iter_slice(5..10)) { + r.insert(i.to_string()); + } + } + }); + thread::yield_now(); + + assert_binds!((t1.join(), t2.join()), (Ok(_), Ok(_)), "One or more of the threads panicked"); + + assert_eq!(&pop.complete_owned()[..], &[ + "10", "11", "12", "13", "14", + "0", "1", "2", "3", "4", + ], "Bad order of elements"); + } }