Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/salsa/tests/panic_safely.rs')
-rw-r--r--crates/salsa/tests/panic_safely.rs93
1 files changed, 93 insertions, 0 deletions
diff --git a/crates/salsa/tests/panic_safely.rs b/crates/salsa/tests/panic_safely.rs
new file mode 100644
index 0000000000..c11ae9c214
--- /dev/null
+++ b/crates/salsa/tests/panic_safely.rs
@@ -0,0 +1,93 @@
+use salsa::{Database, ParallelDatabase, Snapshot};
+use std::panic::{self, AssertUnwindSafe};
+use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
+
+#[salsa::query_group(PanicSafelyStruct)]
+trait PanicSafelyDatabase: salsa::Database {
+ #[salsa::input]
+ fn one(&self) -> usize;
+
+ fn panic_safely(&self) -> ();
+
+ fn outer(&self) -> ();
+}
+
+fn panic_safely(db: &dyn PanicSafelyDatabase) {
+ assert_eq!(db.one(), 1);
+}
+
+static OUTER_CALLS: AtomicU32 = AtomicU32::new(0);
+
+fn outer(db: &dyn PanicSafelyDatabase) {
+ OUTER_CALLS.fetch_add(1, SeqCst);
+ db.panic_safely();
+}
+
+#[salsa::database(PanicSafelyStruct)]
+#[derive(Default)]
+struct DatabaseStruct {
+ storage: salsa::Storage<Self>,
+}
+
+impl salsa::Database for DatabaseStruct {}
+
+impl salsa::ParallelDatabase for DatabaseStruct {
+ fn snapshot(&self) -> Snapshot<Self> {
+ Snapshot::new(DatabaseStruct { storage: self.storage.snapshot() })
+ }
+}
+
+#[test]
+fn should_panic_safely() {
+ let mut db = DatabaseStruct::default();
+ db.set_one(0);
+
+ // Invoke `db.panic_safely() without having set `db.one`. `db.one` will
+ // return 0 and we should catch the panic.
+ let result = panic::catch_unwind(AssertUnwindSafe({
+ let db = db.snapshot();
+ move || db.panic_safely()
+ }));
+ assert!(result.is_err());
+
+ // Set `db.one` to 1 and assert ok
+ db.set_one(1);
+ let result = panic::catch_unwind(AssertUnwindSafe(|| db.panic_safely()));
+ assert!(result.is_ok());
+
+ // Check, that memoized outer is not invalidated by a panic
+ {
+ assert_eq!(OUTER_CALLS.load(SeqCst), 0);
+ db.outer();
+ assert_eq!(OUTER_CALLS.load(SeqCst), 1);
+
+ db.set_one(0);
+ let result = panic::catch_unwind(AssertUnwindSafe(|| db.outer()));
+ assert!(result.is_err());
+ assert_eq!(OUTER_CALLS.load(SeqCst), 1);
+
+ db.set_one(1);
+ db.outer();
+ assert_eq!(OUTER_CALLS.load(SeqCst), 2);
+ }
+}
+
+#[test]
+fn storages_are_unwind_safe() {
+ fn check_unwind_safe<T: std::panic::UnwindSafe>() {}
+ check_unwind_safe::<&DatabaseStruct>();
+}
+
+#[test]
+fn panics_clear_query_stack() {
+ let db = DatabaseStruct::default();
+
+ // Invoke `db.panic_if_not_one() without having set `db.input`. `db.input`
+ // will default to 0 and we should catch the panic.
+ let result = panic::catch_unwind(AssertUnwindSafe(|| db.panic_safely()));
+ assert!(result.is_err());
+
+ // The database has been poisoned and any attempt to increment the
+ // revision should panic.
+ assert_eq!(db.salsa_runtime().active_query(), None);
+}