summaryrefslogtreecommitdiffstats
path: root/vendor/sharded-slab/src/tests/loom_pool.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sharded-slab/src/tests/loom_pool.rs')
-rw-r--r--vendor/sharded-slab/src/tests/loom_pool.rs641
1 files changed, 641 insertions, 0 deletions
diff --git a/vendor/sharded-slab/src/tests/loom_pool.rs b/vendor/sharded-slab/src/tests/loom_pool.rs
new file mode 100644
index 000000000..d7df50552
--- /dev/null
+++ b/vendor/sharded-slab/src/tests/loom_pool.rs
@@ -0,0 +1,641 @@
+use super::util::*;
+use crate::{clear::Clear, sync::alloc, Pack, Pool};
+use loom::{
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ Condvar, Mutex,
+ },
+ thread,
+};
+use std::sync::Arc;
+
+#[derive(Default, Debug)]
+struct State {
+ is_dropped: AtomicBool,
+ is_cleared: AtomicBool,
+ id: usize,
+}
+
+impl State {
+ fn assert_clear(&self) {
+ assert!(!self.is_dropped.load(Ordering::SeqCst));
+ assert!(self.is_cleared.load(Ordering::SeqCst));
+ }
+
+ fn assert_not_clear(&self) {
+ assert!(!self.is_dropped.load(Ordering::SeqCst));
+ assert!(!self.is_cleared.load(Ordering::SeqCst));
+ }
+}
+
+impl PartialEq for State {
+ fn eq(&self, other: &State) -> bool {
+ self.id.eq(&other.id)
+ }
+}
+
+#[derive(Default, Debug)]
+struct DontDropMe(Arc<State>);
+
+impl PartialEq for DontDropMe {
+ fn eq(&self, other: &DontDropMe) -> bool {
+ self.0.eq(&other.0)
+ }
+}
+
+impl DontDropMe {
+ fn new(id: usize) -> (Arc<State>, Self) {
+ let state = Arc::new(State {
+ is_dropped: AtomicBool::new(false),
+ is_cleared: AtomicBool::new(false),
+ id,
+ });
+ (state.clone(), Self(state))
+ }
+}
+
+impl Drop for DontDropMe {
+ fn drop(&mut self) {
+ test_println!("-> DontDropMe drop: dropping data {:?}", self.0.id);
+ self.0.is_dropped.store(true, Ordering::SeqCst)
+ }
+}
+
+impl Clear for DontDropMe {
+ fn clear(&mut self) {
+ test_println!("-> DontDropMe clear: clearing data {:?}", self.0.id);
+ self.0.is_cleared.store(true, Ordering::SeqCst);
+ }
+}
+
+#[test]
+fn dont_drop() {
+ run_model("dont_drop", || {
+ let pool: Pool<DontDropMe> = Pool::new();
+ let (item1, value) = DontDropMe::new(1);
+ test_println!("-> dont_drop: Inserting into pool {}", item1.id);
+ let idx = pool
+ .create_with(move |item| *item = value)
+ .expect("create_with");
+
+ item1.assert_not_clear();
+
+ test_println!("-> dont_drop: clearing idx: {}", idx);
+ pool.clear(idx);
+
+ item1.assert_clear();
+ });
+}
+
+#[test]
+fn concurrent_create_with_clear() {
+ run_model("concurrent_create_with_clear", || {
+ let pool: Arc<Pool<DontDropMe>> = Arc::new(Pool::new());
+ let pair = Arc::new((Mutex::new(None), Condvar::new()));
+
+ let (item1, value) = DontDropMe::new(1);
+ let idx1 = pool
+ .create_with(move |item| *item = value)
+ .expect("create_with");
+ let p = pool.clone();
+ let pair2 = pair.clone();
+ let test_value = item1.clone();
+ let t1 = thread::spawn(move || {
+ let (lock, cvar) = &*pair2;
+ test_println!("-> making get request");
+ assert_eq!(p.get(idx1).unwrap().0.id, test_value.id);
+ let mut next = lock.lock().unwrap();
+ *next = Some(());
+ cvar.notify_one();
+ });
+
+ test_println!("-> making get request");
+ let guard = pool.get(idx1);
+
+ let (lock, cvar) = &*pair;
+ let mut next = lock.lock().unwrap();
+ // wait until we have a guard on the other thread.
+ while next.is_none() {
+ next = cvar.wait(next).unwrap();
+ }
+ // the item should be marked (clear returns true)...
+ assert!(pool.clear(idx1));
+ // ...but the value shouldn't be removed yet.
+ item1.assert_not_clear();
+
+ t1.join().expect("thread 1 unable to join");
+
+ drop(guard);
+ item1.assert_clear();
+ })
+}
+
+#[test]
+fn racy_clear() {
+ run_model("racy_clear", || {
+ let pool = Arc::new(Pool::new());
+ let (item, value) = DontDropMe::new(1);
+
+ let idx = pool
+ .create_with(move |item| *item = value)
+ .expect("create_with");
+ assert_eq!(pool.get(idx).unwrap().0.id, item.id);
+
+ let p = pool.clone();
+ let t2 = thread::spawn(move || p.clear(idx));
+ let r1 = pool.clear(idx);
+ let r2 = t2.join().expect("thread 2 should not panic");
+
+ test_println!("r1: {}, r2: {}", r1, r2);
+
+ assert!(
+ !(r1 && r2),
+ "Both threads should not have cleared the value"
+ );
+ assert!(r1 || r2, "One thread should have removed the value");
+ assert!(pool.get(idx).is_none());
+ item.assert_clear();
+ })
+}
+
+#[test]
+fn clear_local_and_reuse() {
+ run_model("take_remote_and_reuse", || {
+ let pool = Arc::new(Pool::new_with_config::<TinyConfig>());
+
+ let idx1 = pool
+ .create_with(|item: &mut String| {
+ item.push_str("hello world");
+ })
+ .expect("create_with");
+ let idx2 = pool
+ .create_with(|item| item.push_str("foo"))
+ .expect("create_with");
+ let idx3 = pool
+ .create_with(|item| item.push_str("bar"))
+ .expect("create_with");
+
+ assert_eq!(pool.get(idx1).unwrap(), String::from("hello world"));
+ assert_eq!(pool.get(idx2).unwrap(), String::from("foo"));
+ assert_eq!(pool.get(idx3).unwrap(), String::from("bar"));
+
+ let first = idx1 & (!crate::page::slot::Generation::<TinyConfig>::MASK);
+ assert!(pool.clear(idx1));
+
+ let idx1 = pool
+ .create_with(move |item| item.push_str("h"))
+ .expect("create_with");
+
+ let second = idx1 & (!crate::page::slot::Generation::<TinyConfig>::MASK);
+ assert_eq!(first, second);
+ assert!(pool.get(idx1).unwrap().capacity() >= 11);
+ })
+}
+
+#[test]
+fn create_mut_guard_prevents_access() {
+ run_model("create_mut_guard_prevents_access", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let guard = pool.create().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ thread::spawn(move || {
+ assert!(pool2.get(key).is_none());
+ })
+ .join()
+ .unwrap();
+ });
+}
+
+#[test]
+fn create_mut_guard() {
+ run_model("create_mut_guard", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.create().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ guard.push_str("Hello world");
+ drop(guard);
+
+ t1.join().unwrap();
+ });
+}
+
+#[test]
+fn create_mut_guard_2() {
+ run_model("create_mut_guard_2", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.create().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let pool3 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ guard.push_str("Hello world");
+ let t2 = thread::spawn(move || {
+ test_dbg!(pool3.get(key));
+ });
+ drop(guard);
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+ });
+}
+
+#[test]
+fn create_mut_guard_downgrade() {
+ run_model("create_mut_guard_downgrade", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.create().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let pool3 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ guard.push_str("Hello world");
+ let guard = guard.downgrade();
+ let t2 = thread::spawn(move || {
+ test_dbg!(pool3.get(key));
+ });
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+ assert_eq!(guard, "Hello world".to_owned());
+ });
+}
+
+#[test]
+fn create_mut_guard_downgrade_clear() {
+ run_model("create_mut_guard_downgrade_clear", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.create().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+
+ guard.push_str("Hello world");
+ let guard = guard.downgrade();
+ let pool3 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+ let t2 = thread::spawn(move || {
+ test_dbg!(pool3.clear(key));
+ });
+
+ assert_eq!(guard, "Hello world".to_owned());
+ drop(guard);
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+
+ assert!(pool.get(key).is_none());
+ });
+}
+
+#[test]
+fn create_mut_downgrade_during_clear() {
+ run_model("create_mut_downgrade_during_clear", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.create().unwrap();
+ let key: usize = guard.key();
+ guard.push_str("Hello world");
+
+ let pool2 = pool.clone();
+ let guard = guard.downgrade();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.clear(key));
+ });
+
+ t1.join().unwrap();
+
+ assert_eq!(guard, "Hello world".to_owned());
+ drop(guard);
+
+ assert!(pool.get(key).is_none());
+ });
+}
+
+#[test]
+fn ownedref_send_out_of_local() {
+ run_model("ownedref_send_out_of_local", || {
+ let pool = Arc::new(Pool::<alloc::Track<String>>::new());
+ let key1 = pool
+ .create_with(|item| item.get_mut().push_str("hello"))
+ .expect("create item 1");
+ let key2 = pool
+ .create_with(|item| item.get_mut().push_str("goodbye"))
+ .expect("create item 2");
+
+ let item1 = pool.clone().get_owned(key1).expect("get key1");
+ let item2 = pool.clone().get_owned(key2).expect("get key2");
+ let pool2 = pool.clone();
+
+ test_dbg!(pool.clear(key1));
+
+ let t1 = thread::spawn(move || {
+ assert_eq!(item1.get_ref(), &String::from("hello"));
+ drop(item1);
+ });
+ let t2 = thread::spawn(move || {
+ assert_eq!(item2.get_ref(), &String::from("goodbye"));
+ test_dbg!(pool2.clear(key2));
+ drop(item2);
+ });
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+
+ assert!(pool.get(key1).is_none());
+ assert!(pool.get(key2).is_none());
+ });
+}
+
+#[test]
+fn ownedrefs_outlive_pool() {
+ run_model("ownedrefs_outlive_pool", || {
+ let pool = Arc::new(Pool::<alloc::Track<String>>::new());
+ let key1 = pool
+ .create_with(|item| item.get_mut().push_str("hello"))
+ .expect("create item 1");
+ let key2 = pool
+ .create_with(|item| item.get_mut().push_str("goodbye"))
+ .expect("create item 2");
+
+ let item1_1 = pool.clone().get_owned(key1).expect("get key1");
+ let item1_2 = pool.clone().get_owned(key1).expect("get key1 again");
+ let item2 = pool.clone().get_owned(key2).expect("get key2");
+ drop(pool);
+
+ let t1 = thread::spawn(move || {
+ assert_eq!(item1_1.get_ref(), &String::from("hello"));
+ drop(item1_1);
+ });
+
+ let t2 = thread::spawn(move || {
+ assert_eq!(item2.get_ref(), &String::from("goodbye"));
+ drop(item2);
+ });
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+
+ assert_eq!(item1_2.get_ref(), &String::from("hello"));
+ });
+}
+
+#[test]
+fn ownedref_ping_pong() {
+ run_model("ownedref_ping_pong", || {
+ let pool = Arc::new(Pool::<alloc::Track<String>>::new());
+ let key1 = pool
+ .create_with(|item| item.get_mut().push_str("hello"))
+ .expect("create item 1");
+ let key2 = pool
+ .create_with(|item| item.get_mut().push_str("world"))
+ .expect("create item 2");
+
+ let item1 = pool.clone().get_owned(key1).expect("get key1");
+ let pool2 = pool.clone();
+ let pool3 = pool.clone();
+
+ let t1 = thread::spawn(move || {
+ assert_eq!(item1.get_ref(), &String::from("hello"));
+ pool2.clear(key1);
+ item1
+ });
+
+ let t2 = thread::spawn(move || {
+ let item2 = pool3.clone().get_owned(key2).unwrap();
+ assert_eq!(item2.get_ref(), &String::from("world"));
+ pool3.clear(key1);
+ item2
+ });
+
+ let item1 = t1.join().unwrap();
+ let item2 = t2.join().unwrap();
+
+ assert_eq!(item1.get_ref(), &String::from("hello"));
+ assert_eq!(item2.get_ref(), &String::from("world"));
+ });
+}
+
+#[test]
+fn ownedref_drop_from_other_threads() {
+ run_model("ownedref_drop_from_other_threads", || {
+ let pool = Arc::new(Pool::<alloc::Track<String>>::new());
+ let key1 = pool
+ .create_with(|item| item.get_mut().push_str("hello"))
+ .expect("create item 1");
+ let item1 = pool.clone().get_owned(key1).expect("get key1");
+
+ let pool2 = pool.clone();
+
+ let t1 = thread::spawn(move || {
+ let pool = pool2.clone();
+ let key2 = pool
+ .create_with(|item| item.get_mut().push_str("goodbye"))
+ .expect("create item 1");
+ let item2 = pool.clone().get_owned(key2).expect("get key1");
+ let t2 = thread::spawn(move || {
+ assert_eq!(item2.get_ref(), &String::from("goodbye"));
+ test_dbg!(pool2.clear(key1));
+ drop(item2)
+ });
+ assert_eq!(item1.get_ref(), &String::from("hello"));
+ test_dbg!(pool.clear(key2));
+ drop(item1);
+ (t2, key2)
+ });
+
+ let (t2, key2) = t1.join().unwrap();
+ test_dbg!(pool.get(key1));
+ test_dbg!(pool.get(key2));
+
+ t2.join().unwrap();
+
+ assert!(pool.get(key1).is_none());
+ assert!(pool.get(key2).is_none());
+ });
+}
+
+#[test]
+fn create_owned_mut_guard() {
+ run_model("create_owned_mut_guard", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ guard.push_str("Hello world");
+ drop(guard);
+
+ t1.join().unwrap();
+ });
+}
+
+#[test]
+fn create_owned_mut_guard_send() {
+ run_model("create_owned_mut_guard", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ let t2 = thread::spawn(move || {
+ guard.push_str("Hello world");
+ drop(guard);
+ });
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+ });
+}
+
+#[test]
+fn create_owned_mut_guard_2() {
+ run_model("create_owned_mut_guard_2", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let pool3 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ guard.push_str("Hello world");
+ let t2 = thread::spawn(move || {
+ test_dbg!(pool3.get(key));
+ });
+ drop(guard);
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+ });
+}
+
+#[test]
+fn create_owned_mut_guard_downgrade() {
+ run_model("create_owned_mut_guard_downgrade", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ guard.push_str("Hello world");
+
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+ let pool3 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+
+ let guard = guard.downgrade();
+ let t2 = thread::spawn(move || {
+ assert_eq!(pool3.get(key).unwrap(), "Hello world".to_owned());
+ });
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+ assert_eq!(guard, "Hello world".to_owned());
+ });
+}
+
+#[test]
+fn create_owned_mut_guard_downgrade_then_clear() {
+ run_model("create_owned_mut_guard_downgrade_then_clear", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ let key: usize = guard.key();
+
+ let pool2 = pool.clone();
+
+ guard.push_str("Hello world");
+ let guard = guard.downgrade();
+ let pool3 = pool.clone();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.get(key));
+ });
+ let t2 = thread::spawn(move || {
+ test_dbg!(pool3.clear(key));
+ });
+
+ assert_eq!(guard, "Hello world".to_owned());
+ drop(guard);
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+
+ assert!(pool.get(key).is_none());
+ });
+}
+
+#[test]
+fn create_owned_mut_downgrade_during_clear() {
+ run_model("create_owned_mut_downgrade_during_clear", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ let key: usize = guard.key();
+ guard.push_str("Hello world");
+
+ let pool2 = pool.clone();
+ let guard = guard.downgrade();
+ let t1 = thread::spawn(move || {
+ test_dbg!(pool2.clear(key));
+ });
+
+ t1.join().unwrap();
+
+ assert_eq!(guard, "Hello world".to_owned());
+ drop(guard);
+
+ assert!(pool.get(key).is_none());
+ });
+}
+
+#[test]
+fn create_mut_downgrade_during_clear_by_other_thead() {
+ run_model("create_mut_downgrade_during_clear_by_other_thread", || {
+ let pool = Arc::new(Pool::<String>::new());
+ let mut guard = pool.clone().create_owned().unwrap();
+ let key: usize = guard.key();
+ guard.push_str("Hello world");
+
+ let pool2 = pool.clone();
+ let t1 = thread::spawn(move || {
+ let guard = guard.downgrade();
+ assert_eq!(guard, "Hello world".to_owned());
+ drop(guard);
+ });
+
+ let t2 = thread::spawn(move || {
+ test_dbg!(pool2.clear(key));
+ });
+
+ test_dbg!(pool.get(key));
+
+ t1.join().unwrap();
+ t2.join().unwrap();
+ });
+}