2019-07-02 11:40:43 +00:00
|
|
|
use rand::seq::SliceRandom;
|
2018-10-30 12:09:26 +00:00
|
|
|
use rand::Rng;
|
2019-07-02 11:40:43 +00:00
|
|
|
|
2018-10-30 12:09:26 +00:00
|
|
|
use salsa::Database;
|
|
|
|
use salsa::ParallelDatabase;
|
2018-11-01 00:06:06 +00:00
|
|
|
use salsa::Snapshot;
|
2018-10-30 12:09:26 +00:00
|
|
|
use salsa::SweepStrategy;
|
|
|
|
|
2018-10-31 16:01:36 +00:00
|
|
|
// Number of operations a reader performs
|
|
|
|
const N_MUTATOR_OPS: usize = 100;
|
|
|
|
const N_READER_OPS: usize = 100;
|
|
|
|
|
2018-10-30 12:09:26 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
struct Canceled;
|
|
|
|
type Cancelable<T> = Result<T, Canceled>;
|
|
|
|
|
2019-01-25 15:25:17 +00:00
|
|
|
#[salsa::query_group(Stress)]
|
2019-01-12 10:11:59 +00:00
|
|
|
trait StressDatabase: salsa::Database {
|
|
|
|
#[salsa::input]
|
|
|
|
fn a(&self, key: usize) -> usize;
|
2018-10-30 12:09:26 +00:00
|
|
|
|
2019-01-12 10:11:59 +00:00
|
|
|
fn b(&self, key: usize) -> Cancelable<usize>;
|
2018-10-30 12:09:26 +00:00
|
|
|
|
2019-01-12 10:11:59 +00:00
|
|
|
fn c(&self, key: usize) -> Cancelable<usize>;
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn b(db: &impl StressDatabase, key: usize) -> Cancelable<usize> {
|
|
|
|
if db.salsa_runtime().is_current_revision_canceled() {
|
|
|
|
return Err(Canceled);
|
|
|
|
}
|
|
|
|
Ok(db.a(key))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn c(db: &impl StressDatabase, key: usize) -> Cancelable<usize> {
|
|
|
|
db.b(key)
|
|
|
|
}
|
|
|
|
|
2019-01-25 15:25:17 +00:00
|
|
|
#[salsa::database(Stress)]
|
2018-10-30 12:09:26 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
struct StressDatabaseImpl {
|
2018-10-31 10:03:33 +00:00
|
|
|
runtime: salsa::Runtime<StressDatabaseImpl>,
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl salsa::Database for StressDatabaseImpl {
|
|
|
|
fn salsa_runtime(&self) -> &salsa::Runtime<StressDatabaseImpl> {
|
|
|
|
&self.runtime
|
|
|
|
}
|
2019-09-27 09:49:15 +00:00
|
|
|
|
|
|
|
fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime<StressDatabaseImpl> {
|
|
|
|
&mut self.runtime
|
|
|
|
}
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl salsa::ParallelDatabase for StressDatabaseImpl {
|
2018-11-01 00:06:06 +00:00
|
|
|
fn snapshot(&self) -> Snapshot<StressDatabaseImpl> {
|
|
|
|
Snapshot::new(StressDatabaseImpl {
|
2018-11-01 00:05:31 +00:00
|
|
|
runtime: self.runtime.snapshot(self),
|
2018-10-31 16:01:36 +00:00
|
|
|
})
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
2018-10-31 10:03:33 +00:00
|
|
|
enum Query {
|
|
|
|
A,
|
|
|
|
B,
|
|
|
|
C,
|
|
|
|
}
|
2018-10-30 12:09:26 +00:00
|
|
|
|
2018-10-31 16:01:36 +00:00
|
|
|
enum MutatorOp {
|
|
|
|
WriteOp(WriteOp),
|
|
|
|
LaunchReader {
|
|
|
|
ops: Vec<ReadOp>,
|
|
|
|
check_cancellation: bool,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-10-30 12:09:26 +00:00
|
|
|
#[derive(Debug)]
|
2018-10-31 16:01:36 +00:00
|
|
|
enum WriteOp {
|
2018-10-30 12:09:26 +00:00
|
|
|
SetA(usize, usize),
|
2018-10-31 16:01:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum ReadOp {
|
2018-10-30 12:09:26 +00:00
|
|
|
Get(Query, usize),
|
|
|
|
Gc(Query, SweepStrategy),
|
|
|
|
GcAll(SweepStrategy),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl rand::distributions::Distribution<Query> for rand::distributions::Standard {
|
|
|
|
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Query {
|
2019-07-02 11:40:43 +00:00
|
|
|
*[Query::A, Query::B, Query::C].choose(rng).unwrap()
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 16:01:36 +00:00
|
|
|
impl rand::distributions::Distribution<MutatorOp> for rand::distributions::Standard {
|
|
|
|
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> MutatorOp {
|
2018-10-30 12:09:26 +00:00
|
|
|
if rng.gen_bool(0.5) {
|
2018-10-31 16:01:36 +00:00
|
|
|
MutatorOp::WriteOp(rng.gen())
|
|
|
|
} else {
|
|
|
|
MutatorOp::LaunchReader {
|
|
|
|
ops: (0..N_READER_OPS).map(|_| rng.gen()).collect(),
|
|
|
|
check_cancellation: rng.gen(),
|
|
|
|
}
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
2018-10-31 16:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl rand::distributions::Distribution<WriteOp> for rand::distributions::Standard {
|
|
|
|
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> WriteOp {
|
|
|
|
let key = rng.gen::<usize>() % 10;
|
|
|
|
let value = rng.gen::<usize>() % 10;
|
|
|
|
return WriteOp::SetA(key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl rand::distributions::Distribution<ReadOp> for rand::distributions::Standard {
|
|
|
|
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> ReadOp {
|
2018-10-30 12:09:26 +00:00
|
|
|
if rng.gen_bool(0.5) {
|
2018-10-31 16:01:36 +00:00
|
|
|
let query = rng.gen::<Query>();
|
2018-10-30 12:09:26 +00:00
|
|
|
let key = rng.gen::<usize>() % 10;
|
2018-10-31 16:01:36 +00:00
|
|
|
return ReadOp::Get(query, key);
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
2019-01-27 14:14:57 +00:00
|
|
|
let mut strategy = SweepStrategy::discard_outdated();
|
2018-10-30 12:09:26 +00:00
|
|
|
if rng.gen_bool(0.5) {
|
|
|
|
strategy = strategy.discard_values();
|
|
|
|
}
|
|
|
|
if rng.gen_bool(0.5) {
|
2018-10-31 16:01:36 +00:00
|
|
|
ReadOp::Gc(rng.gen::<Query>(), strategy)
|
2018-10-30 12:09:26 +00:00
|
|
|
} else {
|
2018-10-31 16:01:36 +00:00
|
|
|
ReadOp::GcAll(strategy)
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 16:01:36 +00:00
|
|
|
fn db_reader_thread(db: &StressDatabaseImpl, ops: Vec<ReadOp>, check_cancellation: bool) {
|
2018-10-30 12:09:26 +00:00
|
|
|
for op in ops {
|
2018-10-31 16:01:36 +00:00
|
|
|
if check_cancellation {
|
|
|
|
if db.salsa_runtime().is_current_revision_canceled() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
op.execute(db);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WriteOp {
|
|
|
|
fn execute(self, db: &mut StressDatabaseImpl) {
|
|
|
|
match self {
|
|
|
|
WriteOp::SetA(key, value) => {
|
2019-01-27 21:06:14 +00:00
|
|
|
db.set_a(key, value);
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
2018-10-31 16:01:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ReadOp {
|
|
|
|
fn execute(self, db: &StressDatabaseImpl) {
|
|
|
|
match self {
|
|
|
|
ReadOp::Get(query, key) => match query {
|
2018-10-31 10:03:33 +00:00
|
|
|
Query::A => {
|
|
|
|
db.a(key);
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
2018-10-31 10:03:33 +00:00
|
|
|
Query::B => {
|
|
|
|
let _ = db.b(key);
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
2018-10-31 10:03:33 +00:00
|
|
|
Query::C => {
|
|
|
|
let _ = db.c(key);
|
|
|
|
}
|
|
|
|
},
|
2018-10-31 16:01:36 +00:00
|
|
|
ReadOp::Gc(query, strategy) => match query {
|
2018-10-31 10:03:33 +00:00
|
|
|
Query::A => {
|
2019-01-12 10:11:59 +00:00
|
|
|
db.query(AQuery).sweep(strategy);
|
2018-10-31 10:03:33 +00:00
|
|
|
}
|
|
|
|
Query::B => {
|
2019-01-12 10:11:59 +00:00
|
|
|
db.query(BQuery).sweep(strategy);
|
2018-10-31 10:03:33 +00:00
|
|
|
}
|
|
|
|
Query::C => {
|
2019-01-12 10:11:59 +00:00
|
|
|
db.query(CQuery).sweep(strategy);
|
2018-10-31 10:03:33 +00:00
|
|
|
}
|
|
|
|
},
|
2018-10-31 16:01:36 +00:00
|
|
|
ReadOp::GcAll(strategy) => {
|
2018-10-30 12:09:26 +00:00
|
|
|
db.sweep_all(strategy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn stress_test() {
|
2018-10-31 16:01:36 +00:00
|
|
|
let mut db = StressDatabaseImpl::default();
|
2018-10-30 12:09:26 +00:00
|
|
|
for i in 0..10 {
|
2019-01-27 21:06:14 +00:00
|
|
|
db.set_a(i, i);
|
2018-10-30 12:09:26 +00:00
|
|
|
}
|
2018-10-31 16:01:36 +00:00
|
|
|
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
|
|
|
// generate the ops that the mutator thread will perform
|
|
|
|
let write_ops: Vec<MutatorOp> = (0..N_MUTATOR_OPS).map(|_| rng.gen()).collect();
|
|
|
|
|
2018-11-01 00:05:31 +00:00
|
|
|
// execute the "main thread", which sometimes snapshots off other threads
|
2018-10-31 16:01:36 +00:00
|
|
|
let mut all_threads = vec![];
|
|
|
|
for op in write_ops {
|
|
|
|
match op {
|
|
|
|
MutatorOp::WriteOp(w) => w.execute(&mut db),
|
|
|
|
MutatorOp::LaunchReader {
|
|
|
|
ops,
|
|
|
|
check_cancellation,
|
|
|
|
} => all_threads.push(std::thread::spawn({
|
2018-11-01 00:05:31 +00:00
|
|
|
let db = db.snapshot();
|
2018-10-31 16:01:36 +00:00
|
|
|
move || db_reader_thread(&db, ops, check_cancellation)
|
|
|
|
})),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for thread in all_threads {
|
2018-10-30 12:09:26 +00:00
|
|
|
thread.join().unwrap();
|
|
|
|
}
|
|
|
|
}
|