@@ -42,6 +42,7 @@ use std::{
42
42
time:: Duration ,
43
43
} ;
44
44
45
+ use matrix_sdk_common:: executor:: JoinHandle ;
45
46
use tokio:: { sync:: Mutex , time:: sleep} ;
46
47
use tracing:: { instrument, trace} ;
47
48
@@ -91,6 +92,9 @@ pub struct CryptoStoreLock {
91
92
/// reentrant.
92
93
locking_attempt : Arc < Mutex < ( ) > > ,
93
94
95
+ /// Current renew task spawned by `try_lock_once`.
96
+ renew_task : Arc < Mutex < Option < JoinHandle < ( ) > > > > ,
97
+
94
98
/// The key used in the key/value mapping for the lock entry.
95
99
lock_key : String ,
96
100
@@ -135,6 +139,7 @@ impl CryptoStoreLock {
135
139
backoff : Arc :: new ( Mutex :: new ( WaitingTime :: Some ( Self :: INITIAL_BACKOFF_MS ) ) ) ,
136
140
num_holders : Arc :: new ( 0 . into ( ) ) ,
137
141
locking_attempt : Arc :: new ( Mutex :: new ( ( ) ) ) ,
142
+ renew_task : Default :: default ( ) ,
138
143
}
139
144
}
140
145
@@ -170,52 +175,59 @@ impl CryptoStoreLock {
170
175
// Clone data to be owned by the task.
171
176
let this = self . clone ( ) ;
172
177
173
- matrix_sdk_common:: executor:: spawn ( async move {
174
- loop {
175
- {
176
- // First, check if there are still users of this lock.
177
- //
178
- // This is not racy, because:
179
- // - the `locking_attempt` mutex makes sure we don't have unexpected
180
- // interactions with the non-atomic sequence above in `try_lock_once`
181
- // (check > 0, then add 1).
182
- // - other entities holding onto the `num_holders` atomic will only
183
- // decrease it over time.
184
- let _guard = this. locking_attempt . lock ( ) . await ;
185
-
186
- // If there are no more users, we can quit.
187
- if this. num_holders . load ( atomic:: Ordering :: SeqCst ) == 0 {
188
- tracing:: info!( "exiting the lease extension loop" ) ;
189
-
190
- // Cancel the lease with another 0ms lease.
191
- // If we don't get the lock, that's (weird but) fine.
192
- let _ = this
193
- . store
194
- . try_take_leased_lock ( 0 , & this. lock_key , & this. lock_holder )
195
- . await ;
178
+ let mut renew_task = self . renew_task . lock ( ) . await ;
179
+
180
+ // Only spawn the task if it's missing or done.
181
+ let spawn = renew_task. as_ref ( ) . map_or ( true , |join_handle| join_handle. is_finished ( ) ) ;
182
+
183
+ if spawn {
184
+ * renew_task = Some ( matrix_sdk_common:: executor:: spawn ( async move {
185
+ loop {
186
+ {
187
+ // First, check if there are still users of this lock.
188
+ //
189
+ // This is not racy, because:
190
+ // - the `locking_attempt` mutex makes sure we don't have unexpected
191
+ // interactions with the non-atomic sequence above in `try_lock_once`
192
+ // (check > 0, then add 1).
193
+ // - other entities holding onto the `num_holders` atomic will only
194
+ // decrease it over time.
195
+ let _guard = this. locking_attempt . lock ( ) . await ;
196
+
197
+ // If there are no more users, we can quit.
198
+ if this. num_holders . load ( atomic:: Ordering :: SeqCst ) == 0 {
199
+ tracing:: info!( "exiting the lease extension loop" ) ;
200
+
201
+ // Cancel the lease with another 0ms lease.
202
+ // If we don't get the lock, that's (weird but) fine.
203
+ let _ = this
204
+ . store
205
+ . try_take_leased_lock ( 0 , & this. lock_key , & this. lock_holder )
206
+ . await ;
207
+
208
+ // Exit the loop.
209
+ break ;
210
+ }
211
+ }
196
212
213
+ sleep ( Duration :: from_millis ( Self :: EXTEND_LEASE_EVERY_MS ) ) . await ;
214
+
215
+ if let Err ( err) = this
216
+ . store
217
+ . try_take_leased_lock (
218
+ Self :: LEASE_DURATION_MS ,
219
+ & this. lock_key ,
220
+ & this. lock_holder ,
221
+ )
222
+ . await
223
+ {
224
+ tracing:: error!( "error when extending lock lease: {err:#}" ) ;
197
225
// Exit the loop.
198
226
break ;
199
227
}
200
228
}
201
-
202
- sleep ( Duration :: from_millis ( Self :: EXTEND_LEASE_EVERY_MS ) ) . await ;
203
-
204
- if let Err ( err) = this
205
- . store
206
- . try_take_leased_lock (
207
- Self :: LEASE_DURATION_MS ,
208
- & this. lock_key ,
209
- & this. lock_holder ,
210
- )
211
- . await
212
- {
213
- tracing:: error!( "error when extending lock lease: {err:#}" ) ;
214
- // Exit the loop.
215
- break ;
216
- }
217
- }
218
- } ) ;
229
+ } ) ) ;
230
+ }
219
231
220
232
let guard = CryptoStoreLockGuard { num_holders : self . num_holders . clone ( ) } ;
221
233
Ok ( Some ( guard) )
0 commit comments