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 ,
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,25 @@ 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
+ last_generation : Arc < Mutex < LockGeneration > > ,
181
+ is_poisoned : Arc < Mutex < bool > > ,
182
+ }
183
+
184
+ impl LockableEventCacheStore {
185
+ fn new ( store : Arc < DynEventCacheStore > ) -> Self {
186
+ Self {
187
+ store,
188
+ last_generation : Arc :: new ( Mutex :: new ( FIRST_LOCK_GENERATION ) ) ,
189
+ is_poisoned : Arc :: new ( Mutex :: new ( false ) ) ,
190
+ }
191
+ }
192
+
193
+ fn is_poisoned ( & self ) -> bool {
194
+ * self . is_poisoned . lock ( ) . unwrap ( )
195
+ }
196
+ }
172
197
173
198
#[ cfg_attr( target_arch = "wasm32" , async_trait:: async_trait( ?Send ) ) ]
174
199
#[ cfg_attr( not( target_arch = "wasm32" ) , async_trait:: async_trait) ]
@@ -181,6 +206,32 @@ impl BackingStore for LockableEventCacheStore {
181
206
key : & str ,
182
207
holder : & str ,
183
208
) -> StdResult < bool , Self :: LockError > {
184
- Ok ( self . 0 . try_take_leased_lock ( lease_duration_ms, key, holder) . await ?. is_some ( ) )
209
+ let lock_generation =
210
+ self . store . try_take_leased_lock ( lease_duration_ms, key, holder) . await ?;
211
+
212
+ Ok ( match lock_generation {
213
+ // Lock hasn't been acquired.
214
+ None => false ,
215
+
216
+ // Lock has been acquired, and we have a generation.
217
+ Some ( generation) => {
218
+ let mut last_generation = self . last_generation . lock ( ) . unwrap ( ) ;
219
+ let mut is_poisoned = self . is_poisoned . lock ( ) . unwrap ( ) ;
220
+
221
+ // The lock is considered poisoned if it's been acquired
222
+ // from another holder. If the lock is acquired from another
223
+ // holder, its generation is incremented by one. So, if
224
+ // `lock_generation` is different of `last_generation`, it
225
+ // means it's been acquired from another holder, and it is
226
+ // consequently poisoned; otherwise it is not poisoned.
227
+ //
228
+ // The initial value for `last_generation` **must be**
229
+ // `FIRST_LOCK_GENERATION`.
230
+ * is_poisoned = generation != * last_generation;
231
+ * last_generation = generation;
232
+
233
+ true
234
+ }
235
+ } )
185
236
}
186
237
}
0 commit comments