| 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(); |
| }); |
| } |