usesuper::util::*; usecrate::sync::alloc; usecrate::Slab; use loom::sync::{Condvar, Mutex}; use loom::thread; use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
let idx = slab.insert(1).expect("insert");
assert_eq!(slab.get(idx).unwrap(), 1);
let s1 = slab.clone(); let s2 = slab.clone();
let t1 = thread::spawn(move || s1.take(idx)); let t2 = thread::spawn(move || s2.take(idx));
let r1 = t1.join().expect("thread 1 should not panic"); let r2 = t2.join().expect("thread 2 should not panic");
assert!(
r1.is_none() || r2.is_none(), "both threads should not have removed the value"
);
assert_eq!(
r1.or(r2),
Some(1), "one thread should have removed the value"
);
assert!(slab.get(idx).is_none());
});
}
let idx = slab.insert(1).expect("insert");
assert_eq!(slab.get(idx).unwrap(), 1);
let s = slab.clone(); let t2 = thread::spawn(move || s.take(idx)); let r1 = slab.take(idx); let r2 = t2.join().expect("thread 2 should not panic");
assert!(
r1.is_none() || r2.is_none(), "both threads should not have removed the value"
);
assert!(
r1.or(r2).is_some(), "one thread should have removed the value"
);
assert!(slab.get(idx).is_none());
});
}
#[test] fn concurrent_insert_take() {
run_model("concurrent_insert_remove", || { let slab = Arc::new(Slab::new()); let pair = Arc::new((Mutex::new(None), Condvar::new()));
let slab2 = slab.clone(); let pair2 = pair.clone(); let remover = thread::spawn(move || { let (lock, cvar) = &*pair2; for i in0..2 {
test_println!("--- remover i={} ---", i); letmut next = lock.lock().unwrap(); while next.is_none() {
next = cvar.wait(next).unwrap();
} let key = next.take().unwrap();
assert_eq!(slab2.take(key), Some(i));
cvar.notify_one();
}
});
let (lock, cvar) = &*pair; for i in0..2 {
test_println!("--- inserter i={} ---", i); let key = slab.insert(i).expect("insert");
letmut next = lock.lock().unwrap();
*next = Some(key);
cvar.notify_one();
// Wait for the item to be removed. while next.is_some() {
next = cvar.wait(next).unwrap();
}
let idx1 = slab.insert(1).expect("insert"); let idx2 = slab.insert(2).expect("insert"); let idx3 = slab.insert(3).expect("insert"); let idx4 = slab.insert(4).expect("insert");
let s = slab.clone(); let t1 = thread::spawn(move || {
s.insert(1).expect("insert");
s.insert(2).expect("insert");
});
let s = slab.clone(); let t2 = thread::spawn(move || {
s.insert(3).expect("insert");
s.insert(4).expect("insert");
});
t1.join().expect("thread 1 should not panic");
t2.join().expect("thread 2 should not panic");
let slab = Arc::get_mut(&mut slab).expect("other arcs should be dropped"); let items: Vec<_> = slab.unique_iter().map(|&i| i).collect();
assert!(items.contains(&1), "items: {:?}", items);
assert!(items.contains(&2), "items: {:?}", items);
assert!(items.contains(&3), "items: {:?}", items);
assert!(items.contains(&4), "items: {:?}", items);
});
}
#[test] fn custom_page_sz() { letmut model = loom::model::Builder::new();
model.max_branches = 100000;
model.check(|| { let slab = Slab::<usize>::new_with_config::<TinyConfig>();
for i in0..1024usize {
test_println!("{}", i); let k = slab.insert(i).expect("insert"); let v = slab.get(k).expect("get");
assert_eq!(v, i, "slab: {:#?}", slab);
}
});
}
#[test] fn max_refs() { struct LargeGenConfig;
// Configure the slab with a very large number of bits for the generation // counter. That way, there will be very few bits for the ref count left // over, and this test won't have to malloc millions of references. implcrate::cfg::Config for LargeGenConfig { const INITIAL_PAGE_SIZE: usize = 2; const MAX_THREADS: usize = 32; const MAX_PAGES: usize = 2;
}
letmut model = loom::model::Builder::new();
model.max_branches = 100000;
model.check(|| { let slab = Slab::new_with_config::<LargeGenConfig>(); let key = slab.insert("hello world").unwrap(); let max = crate::page::slot::RefCount::<LargeGenConfig>::MAX;
// Create the maximum number of concurrent references to the entry. letmut refs = (0..max)
.map(|_| slab.get(key).unwrap()) // Store the refs in a vec so they don't get dropped immediately.
.collect::<Vec<_>>();
assert!(slab.get(key).is_none());
// After dropping a ref, we should now be able to access the slot again.
drop(refs.pop()); let ref1 = slab.get(key);
assert!(ref1.is_some());
// Ref1 should max out the number of references again.
assert!(slab.get(key).is_none());
})
}
mod free_list_reuse { usesuper::*; struct TinyConfig;
implcrate::cfg::Config for TinyConfig { const INITIAL_PAGE_SIZE: usize = 2;
}
let t1 = slab.insert("hello").expect("insert"); let t2 = slab.insert("world").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t1).1, 0, "1st slot should be on 0th page"
);
assert_eq!( crate::page::indices::<TinyConfig>(t2).1, 0, "2nd slot should be on 0th page"
); let t3 = slab.insert("earth").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t3).1, 1, "3rd slot should be on 1st page"
);
slab.remove(t2); let t4 = slab.insert("universe").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t4).1, 0, "2nd slot should be reused (0th page)"
);
slab.remove(t1); let _ = slab.insert("goodbye").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t4).1, 0, "1st slot should be reused (0th page)"
);
});
}
let t1 = slab.insert("hello").expect("insert"); let t2 = slab.insert("world").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t1).1, 0, "1st slot should be on 0th page"
);
assert_eq!( crate::page::indices::<TinyConfig>(t2).1, 0, "2nd slot should be on 0th page"
); let t3 = slab.insert("earth").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t3).1, 1, "3rd slot should be on 1st page"
);
assert_eq!(slab.take(t2), Some("world")); let t4 = slab.insert("universe").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t4).1, 0, "2nd slot should be reused (0th page)"
);
assert_eq!(slab.take(t1), Some("hello")); let _ = slab.insert("goodbye").expect("insert");
assert_eq!( crate::page::indices::<TinyConfig>(t4).1, 0, "1st slot should be reused (0th page)"
);
});
}
}
#[test] fn vacant_entry() {
run_model("vacant_entry", || { let slab = Arc::new(Slab::new()); let entry = slab.vacant_entry().unwrap(); let key: usize = entry.key();
let slab2 = slab.clone(); let t1 = thread::spawn(move || {
test_dbg!(slab2.get(key));
});
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.