@@ -596,6 +596,7 @@ pub enum CrossProcessLockError {
596596mod 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