From c94db8494589cd8528884acf11d70a6d57be9d8b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 19 Oct 2018 05:59:30 -0400 Subject: [PATCH] add a test for the revision lock --- tests/parallel/main.rs | 1 + tests/parallel/revision_lock.rs | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/parallel/revision_lock.rs diff --git a/tests/parallel/main.rs b/tests/parallel/main.rs index 5ae83ddb..c1bb0004 100644 --- a/tests/parallel/main.rs +++ b/tests/parallel/main.rs @@ -3,5 +3,6 @@ mod setup; mod cancellation; mod independent; mod race; +mod revision_lock; mod signal; mod true_parallel; diff --git a/tests/parallel/revision_lock.rs b/tests/parallel/revision_lock.rs new file mode 100644 index 00000000..fc634340 --- /dev/null +++ b/tests/parallel/revision_lock.rs @@ -0,0 +1,72 @@ +use crate::setup::{Input, ParDatabase, ParDatabaseImpl}; +use crate::signal::Signal; +use salsa::{Database, ParallelDatabase}; +use std::sync::Arc; + +/// Add test where a call to `sum` is cancelled by a simultaneous +/// write. Check that we recompute the result in next revision, even +/// though none of the inputs have changed. +#[test] +fn in_par_get_set_cancellation() { + let db = ParDatabaseImpl::default(); + + db.query(Input).set('a', 1); + + let signal = Arc::new(Signal::default()); + + let lock = db.salsa_runtime().lock_revision(); + let thread1 = std::thread::spawn({ + let db = db.fork(); + let signal = signal.clone(); + move || { + // Check that cancellation flag is not yet set, because + // `set` cannot have been called yet. + assert!(!db.salsa_runtime().is_current_revision_canceled()); + + // Signal other thread to proceed. + signal.signal(1); + + // Wait for other thread to signal cancellation + while !db.salsa_runtime().is_current_revision_canceled() { + std::thread::yield_now(); + } + + // Since we have not yet released revision lock, we should + // see 1 here. + let v = db.input('a'); + + // Release the lock. + std::mem::drop(lock); + + // This could come before or after the `set` in the other + // thread. + let w = db.input('a'); + + (v, w) + } + }); + + let thread2 = std::thread::spawn({ + let db = db.fork(); + let signal = signal.clone(); + move || { + // Wait until thread 1 has asserted that they are not cancelled + // before we invoke `set.` + signal.wait_for(1); + + // This will block until thread1 drops the revision lock. + db.query(Input).set('a', 2); + + db.input('a') + } + }); + + // The first read is done with the revision lock, so it *must* see + // `1`; the second read could see either `1` or `2`. + let (a, b) = thread1.join().unwrap(); + assert_eq!(a, 1); + assert!(b == 1 || b == 2, "saw unexpected value for b: {}", b); + + let c = thread2.join().unwrap(); + assert_eq!(c, 2); +}