salsa/tests/parallel/setup.rs

203 lines
5.3 KiB
Rust
Raw Normal View History

2018-10-19 09:59:19 +00:00
use crate::signal::Signal;
2018-10-13 09:22:38 +00:00
use salsa::Database;
use salsa::ParallelDatabase;
2018-11-01 00:06:06 +00:00
use salsa::Snapshot;
2018-10-13 09:22:38 +00:00
use std::sync::Arc;
2021-05-17 16:59:28 +00:00
use std::{
cell::Cell,
panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
};
2018-10-13 09:22:38 +00:00
#[salsa::query_group(Par)]
pub(crate) trait ParDatabase: Knobs {
#[salsa::input]
fn input(&self, key: char) -> usize;
2018-10-13 09:22:38 +00:00
fn sum(&self, key: &'static str) -> usize;
/// Invokes `sum`
fn sum2(&self, key: &'static str) -> usize;
/// Invokes `sum` but doesn't really care about the result.
fn sum2_drop_sum(&self, key: &'static str) -> usize;
/// Invokes `sum2`
fn sum3(&self, key: &'static str) -> usize;
/// Invokes `sum2_drop_sum`
fn sum3_drop_sum(&self, key: &'static str) -> usize;
2018-10-13 09:22:38 +00:00
}
/// Various "knobs" and utilities used by tests to force
/// a certain behavior.
pub(crate) trait Knobs {
fn knobs(&self) -> &KnobsStruct;
2018-10-13 09:22:38 +00:00
fn signal(&self, stage: usize);
2018-10-13 09:22:38 +00:00
fn wait_for(&self, stage: usize);
2018-10-13 09:22:38 +00:00
}
pub(crate) trait WithValue<T> {
fn with_value<R>(&self, value: T, closure: impl FnOnce() -> R) -> R;
}
impl<T> WithValue<T> for Cell<T> {
fn with_value<R>(&self, value: T, closure: impl FnOnce() -> R) -> R {
let old_value = self.replace(value);
2021-05-17 16:59:28 +00:00
let result = catch_unwind(AssertUnwindSafe(|| closure()));
2018-10-13 09:22:38 +00:00
self.set(old_value);
2021-05-17 16:59:28 +00:00
match result {
Ok(r) => r,
Err(payload) => resume_unwind(payload),
}
2018-10-13 09:22:38 +00:00
}
}
2019-01-10 10:32:52 +00:00
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum CancellationFlag {
2019-01-10 10:32:52 +00:00
Down,
Panic,
}
impl Default for CancellationFlag {
fn default() -> CancellationFlag {
CancellationFlag::Down
2019-01-10 10:32:52 +00:00
}
}
/// Various "knobs" that can be used to customize how the queries
/// behave on one specific thread. Note that this state is
/// intentionally thread-local (apart from `signal`).
2018-10-13 09:22:38 +00:00
#[derive(Clone, Default)]
pub(crate) struct KnobsStruct {
/// A kind of flexible barrier used to coordinate execution across
/// threads to ensure we reach various weird states.
pub(crate) signal: Arc<Signal>,
2018-10-23 09:25:09 +00:00
/// When this database is about to block, send a signal.
pub(crate) signal_on_will_block: Cell<usize>,
/// Invocations of `sum` will signal this stage on entry.
pub(crate) sum_signal_on_entry: Cell<usize>,
/// Invocations of `sum` will wait for this stage on entry.
pub(crate) sum_wait_for_on_entry: Cell<usize>,
/// If true, invocations of `sum` will panic before they exit.
pub(crate) sum_should_panic: Cell<bool>,
/// If true, invocations of `sum` will wait for cancellation before
/// they exit.
pub(crate) sum_wait_for_cancellation: Cell<CancellationFlag>,
/// Invocations of `sum` will wait for this stage prior to exiting.
pub(crate) sum_wait_for_on_exit: Cell<usize>,
/// Invocations of `sum` will signal this stage prior to exiting.
pub(crate) sum_signal_on_exit: Cell<usize>,
/// Invocations of `sum3_drop_sum` will panic unconditionally
pub(crate) sum3_drop_sum_should_panic: Cell<bool>,
2018-10-13 09:22:38 +00:00
}
fn sum(db: &dyn ParDatabase, key: &'static str) -> usize {
2018-10-13 09:22:38 +00:00
let mut sum = 0;
db.signal(db.knobs().sum_signal_on_entry.get());
db.wait_for(db.knobs().sum_wait_for_on_entry.get());
2018-10-13 09:22:38 +00:00
if db.knobs().sum_should_panic.get() {
panic!("query set to panic before exit")
}
2018-10-13 09:22:38 +00:00
for ch in key.chars() {
sum += db.input(ch);
}
match db.knobs().sum_wait_for_cancellation.get() {
CancellationFlag::Down => (),
CancellationFlag::Panic => {
log::debug!("waiting for cancellation");
2021-05-17 16:59:28 +00:00
loop {
db.unwind_if_cancelled();
2019-01-10 10:32:52 +00:00
std::thread::yield_now();
}
2018-10-13 09:22:38 +00:00
}
}
db.wait_for(db.knobs().sum_wait_for_on_exit.get());
db.signal(db.knobs().sum_signal_on_exit.get());
2018-10-13 09:22:38 +00:00
sum
}
fn sum2(db: &dyn ParDatabase, key: &'static str) -> usize {
db.sum(key)
}
fn sum2_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize {
let _ = db.sum(key);
22
}
fn sum3(db: &dyn ParDatabase, key: &'static str) -> usize {
db.sum2(key)
}
fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize {
if db.knobs().sum3_drop_sum_should_panic.get() {
panic!("sum3_drop_sum executed")
}
db.sum2_drop_sum(key)
}
#[salsa::database(
Par,
crate::parallel_cycle_all_recover::ParallelCycleAllRecover,
crate::parallel_cycle_none_recover::ParallelCycleNoneRecover,
crate::parallel_cycle_mid_recover::ParallelCycleMidRecovers,
crate::parallel_cycle_one_recovers::ParallelCycleOneRecovers
)]
2018-10-13 09:22:38 +00:00
#[derive(Default)]
pub(crate) struct ParDatabaseImpl {
2020-07-02 10:31:02 +00:00
storage: salsa::Storage<Self>,
2018-10-13 09:22:38 +00:00
knobs: KnobsStruct,
}
impl Database for ParDatabaseImpl {
fn salsa_event(&self, event: salsa::Event) {
2021-06-03 11:12:38 +00:00
if let salsa::EventKind::WillBlockOn { .. } = event.kind {
self.signal(self.knobs().signal_on_will_block.get());
2018-10-23 09:25:09 +00:00
}
}
2018-10-13 09:22:38 +00:00
}
impl ParallelDatabase for ParDatabaseImpl {
2018-11-01 00:06:06 +00:00
fn snapshot(&self) -> Snapshot<Self> {
Snapshot::new(ParDatabaseImpl {
2020-07-02 10:31:02 +00:00
storage: self.storage.snapshot(),
2018-10-13 09:22:38 +00:00
knobs: self.knobs.clone(),
})
2018-10-13 09:22:38 +00:00
}
}
impl Knobs for ParDatabaseImpl {
fn knobs(&self) -> &KnobsStruct {
&self.knobs
2018-10-13 09:22:38 +00:00
}
fn signal(&self, stage: usize) {
self.knobs.signal.signal(stage);
2018-10-13 09:22:38 +00:00
}
fn wait_for(&self, stage: usize) {
self.knobs.signal.wait_for(stage);
2018-10-13 09:22:38 +00:00
}
}