Skip to content

Commit b3f6bff

Browse files
committed
test(common): Test dirtiness of the cross-process lock.
1 parent f33d80b commit b3f6bff

File tree

1 file changed

+68
-12
lines changed

1 file changed

+68
-12
lines changed

crates/matrix-sdk-common/src/cross_process_lock.rs

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ pub enum CrossProcessLockError {
596596
mod tests {
597597
use std::{
598598
collections::HashMap,
599+
ops::Not,
599600
sync::{Arc, RwLock, atomic},
600601
};
601602

@@ -665,17 +666,23 @@ mod tests {
665666

666667
// The lock plain works when used with a single holder.
667668
let guard = lock.try_lock_once().await?.expect("lock must be obtained successfully");
669+
assert_matches!(guard, CrossProcessLockState::Clean(_));
670+
assert!(lock.is_dirty().not());
668671
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 1);
669672

670673
// Releasing works.
671674
release_lock(guard).await;
675+
assert!(lock.is_dirty().not());
672676
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 0);
673677

674678
// Spin locking on the same lock always works, assuming no concurrent access.
675679
let guard = lock.spin_lock(None).await?.expect("spin lock must be obtained successfully");
680+
assert!(lock.is_dirty().not());
681+
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 1);
676682

677683
// Releasing still works.
678684
release_lock(guard).await;
685+
assert!(lock.is_dirty().not());
679686
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 0);
680687

681688
Ok(())
@@ -686,19 +693,23 @@ mod tests {
686693
let store = TestStore::default();
687694
let lock = CrossProcessLock::new(store.clone(), "key".to_owned(), "first".to_owned());
688695

689-
// When a lock is obtained...
690-
let _guard = lock.try_lock_once().await?.expect("lock must be obtained successfully");
696+
// When a lock is obtained…
697+
let guard = lock.try_lock_once().await?.expect("lock must be obtained successfully");
698+
assert_matches!(guard, CrossProcessLockState::Clean(_));
699+
assert!(lock.is_dirty().not());
691700
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 1);
692701

693-
// But then forgotten... (note: no need to release the guard)
702+
// But then forgotten (note: no need to release the guard)
694703
drop(lock);
695704

696-
// And when rematerializing the lock with the same key/value...
705+
// And when rematerializing the lock with the same key/value
697706
let lock = CrossProcessLock::new(store.clone(), "key".to_owned(), "first".to_owned());
698707

699708
// We still got it.
700-
let _guard =
709+
let guard =
701710
lock.try_lock_once().await?.expect("lock (again) must be obtained successfully");
711+
assert_matches!(guard, CrossProcessLockState::Clean(_));
712+
assert!(lock.is_dirty().not());
702713
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 1);
703714

704715
Ok(())
@@ -711,7 +722,10 @@ mod tests {
711722

712723
// Taking the lock twice…
713724
let guard1 = lock.try_lock_once().await?.expect("lock must be obtained successfully");
725+
assert_matches!(guard1, CrossProcessLockState::Clean(_));
714726
let guard2 = lock.try_lock_once().await?.expect("lock must be obtained successfully");
727+
assert_matches!(guard2, CrossProcessLockState::Clean(_));
728+
assert!(lock.is_dirty().not());
715729

716730
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 2);
717731

@@ -722,6 +736,8 @@ mod tests {
722736
release_lock(guard2).await;
723737
assert_eq!(lock.num_holders.load(atomic::Ordering::SeqCst), 0);
724738

739+
assert!(lock.is_dirty().not());
740+
725741
Ok(())
726742
}
727743

@@ -731,32 +747,72 @@ mod tests {
731747
let lock1 = CrossProcessLock::new(store.clone(), "key".to_owned(), "first".to_owned());
732748
let lock2 = CrossProcessLock::new(store, "key".to_owned(), "second".to_owned());
733749

734-
// When the first process takes the lock...
750+
// `lock1` acquires the lock.
735751
let guard1 = lock1.try_lock_once().await?.expect("lock must be obtained successfully");
752+
assert_matches!(guard1, CrossProcessLockState::Clean(_));
753+
assert!(lock1.is_dirty().not());
736754

737-
// The second can't take it immediately.
738-
let _err2 = lock2.try_lock_once().await?.expect_err("lock must NOT be obtained");
755+
// `lock2` cannot acquire the lock.
756+
let err = lock2.try_lock_once().await?.expect_err("lock must NOT be obtained");
757+
assert_matches!(err, CrossProcessLockUnobtained::Busy);
739758

759+
// `lock2` is waiting in a task.
740760
let lock2_clone = lock2.clone();
741-
let handle = spawn(async move { lock2_clone.spin_lock(Some(1000)).await });
761+
let task = spawn(async move { lock2_clone.spin_lock(Some(500)).await });
742762

743763
sleep(Duration::from_millis(100)).await;
744764

745765
drop(guard1);
746766

747-
// lock2 in the background manages to get the lock at some point.
748-
let _guard2 = handle
767+
// Once `lock1` is released, `lock2` managed to obtain it.
768+
let guard2 = task
749769
.await
750770
.expect("join handle is properly awaited")
751771
.expect("lock is successfully attempted")
752772
.expect("lock must be obtained successfully");
773+
assert_matches!(guard2, CrossProcessLockState::Clean(_));
753774

754-
// Now if lock1 tries to get the lock with a small timeout, it will fail.
775+
// `lock1` and `lock2` are both clean!
776+
assert!(lock1.is_dirty().not());
777+
assert!(lock2.is_dirty().not());
778+
779+
// Now if `lock1` tries to obtain the lock with a small timeout, it will fail.
755780
assert_matches!(
756781
lock1.spin_lock(Some(200)).await,
757782
Ok(Err(CrossProcessLockUnobtained::TimedOut))
758783
);
759784

785+
// Release `lock2` and obtain `lock1` again.
786+
drop(guard2);
787+
788+
// Wait for the spin locks' tasks to expire.
789+
sleep(Duration::from_millis(1000)).await;
790+
791+
let guard1 = lock1.try_lock_once().await?.expect("lock must be obtained successfully");
792+
793+
// Now it's dirty!
794+
assert_matches!(guard1, CrossProcessLockState::Dirty(_));
795+
assert!(lock1.is_dirty());
796+
797+
// Release `lock1`, and obtain it again. Dirty once, dirty again because it has
798+
// not been marked as cleaned.
799+
drop(guard1);
800+
801+
let guard1 = lock1.try_lock_once().await?.expect("lock must be obtained successfully");
802+
assert_matches!(guard1, CrossProcessLockState::Dirty(_));
803+
assert!(lock1.is_dirty());
804+
805+
// Mark as non-dirty.
806+
lock1.clear_dirty();
807+
assert!(lock1.is_dirty().not());
808+
809+
drop(guard1);
810+
811+
// Check it's not dirty.
812+
let guard1 = lock1.try_lock_once().await?.expect("lock must be obtained successfully");
813+
assert_matches!(guard1, CrossProcessLockState::Dirty(_));
814+
assert!(lock1.is_dirty());
815+
760816
Ok(())
761817
}
762818
}

0 commit comments

Comments
 (0)