19
19
//! into the event cache for the actual storage. By default this brings an
20
20
//! in-memory store.
21
21
22
- use std:: { fmt, ops:: Deref , result:: Result as StdResult , str:: Utf8Error , sync:: Arc } ;
22
+ use std:: {
23
+ fmt,
24
+ ops:: { Deref , DerefMut } ,
25
+ result:: Result as StdResult ,
26
+ str:: Utf8Error ,
27
+ sync:: { Arc , Mutex } ,
28
+ } ;
23
29
24
30
#[ cfg( any( test, feature = "testing" ) ) ]
25
31
#[ macro_use]
@@ -28,7 +34,8 @@ mod memory_store;
28
34
mod traits;
29
35
30
36
use matrix_sdk_common:: store_locks:: {
31
- BackingStore , CrossProcessStoreLock , CrossProcessStoreLockGuard , LockStoreError ,
37
+ BackingStore , CrossProcessStoreLock , CrossProcessStoreLockGuard , LockGeneration ,
38
+ LockStoreError , FIRST_LOCK_GENERATION ,
32
39
} ;
33
40
pub use matrix_sdk_store_encryption:: Error as StoreEncryptionError ;
34
41
@@ -71,7 +78,7 @@ impl EventCacheStoreLock {
71
78
72
79
Self {
73
80
cross_process_lock : CrossProcessStoreLock :: new (
74
- LockableEventCacheStore ( store. clone ( ) ) ,
81
+ LockableEventCacheStore :: new ( store. clone ( ) ) ,
75
82
"default" . to_owned ( ) ,
76
83
holder,
77
84
) ,
@@ -168,7 +175,23 @@ pub type Result<T, E = EventCacheStoreError> = StdResult<T, E>;
168
175
/// A type that wraps the [`EventCacheStore`] but implements [`BackingStore`] to
169
176
/// make it usable inside the cross process lock.
170
177
#[ derive( Clone , Debug ) ]
171
- struct LockableEventCacheStore ( Arc < DynEventCacheStore > ) ;
178
+ struct LockableEventCacheStore {
179
+ store : Arc < DynEventCacheStore > ,
180
+ generation_and_is_poisoned : Arc < Mutex < ( LockGeneration , bool ) > > ,
181
+ }
182
+
183
+ impl LockableEventCacheStore {
184
+ fn new ( store : Arc < DynEventCacheStore > ) -> Self {
185
+ Self {
186
+ store,
187
+ generation_and_is_poisoned : Arc :: new ( Mutex :: new ( ( FIRST_LOCK_GENERATION , false ) ) ) ,
188
+ }
189
+ }
190
+
191
+ fn is_poisoned ( & self ) -> bool {
192
+ self . generation_and_is_poisoned . lock ( ) . unwrap ( ) . 1
193
+ }
194
+ }
172
195
173
196
#[ cfg_attr( target_arch = "wasm32" , async_trait:: async_trait( ?Send ) ) ]
174
197
#[ cfg_attr( not( target_arch = "wasm32" ) , async_trait:: async_trait) ]
@@ -181,6 +204,32 @@ impl BackingStore for LockableEventCacheStore {
181
204
key : & str ,
182
205
holder : & str ,
183
206
) -> StdResult < bool , Self :: LockError > {
184
- Ok ( self . 0 . try_take_leased_lock ( lease_duration_ms, key, holder) . await ?. is_some ( ) )
207
+ let lock_generation =
208
+ self . store . try_take_leased_lock ( lease_duration_ms, key, holder) . await ?;
209
+
210
+ Ok ( match lock_generation {
211
+ // Lock hasn't been acquired.
212
+ None => false ,
213
+
214
+ // Lock has been acquired, and we have a generation.
215
+ Some ( generation) => {
216
+ let mut guard = self . generation_and_is_poisoned . lock ( ) . unwrap ( ) ;
217
+ let ( last_generation, is_poisoned) = guard. deref_mut ( ) ;
218
+
219
+ // The lock is considered poisoned if it's been acquired
220
+ // from another holder. If the lock is acquired from another
221
+ // holder, its generation is incremented by one. So, if
222
+ // `lock_generation` is different of `last_generation`, it
223
+ // means it's been acquired from another holder, and it is
224
+ // consequently poisoned; otherwise it is not poisoned.
225
+ //
226
+ // The initial value for `last_generation` **must be**
227
+ // `FIRST_LOCK_GENERATION`.
228
+ * is_poisoned = generation != * last_generation;
229
+ * last_generation = generation;
230
+
231
+ true
232
+ }
233
+ } )
185
234
}
186
235
}
0 commit comments