@@ -4,6 +4,17 @@ use tokio::time::{self, Duration, Instant};
4
4
use bytes:: Bytes ;
5
5
use std:: collections:: { BTreeMap , HashMap } ;
6
6
use std:: sync:: { Arc , Mutex } ;
7
+ use tracing:: debug;
8
+
9
+ /// A wrapper around a `Db` instance. This exists to allow orderly cleanup
10
+ /// of the `Db` by signalling the background purge task to shut down when
11
+ /// this struct is dropped.
12
+ #[ derive( Debug ) ]
13
+ pub ( crate ) struct DbDropGuard {
14
+ /// The `Db` instance that will be shut down when this `DbHolder` struct
15
+ /// is dropped.
16
+ db : Db ,
17
+ }
7
18
8
19
/// Server state shared across all connections.
9
20
///
@@ -92,6 +103,27 @@ struct Entry {
92
103
expires_at : Option < Instant > ,
93
104
}
94
105
106
+ impl DbDropGuard {
107
+ /// Create a new `DbHolder`, wrapping a `Db` instance. When this is dropped
108
+ /// the `Db`'s purge task will be shut down.
109
+ pub ( crate ) fn new ( ) -> DbDropGuard {
110
+ DbDropGuard { db : Db :: new ( ) }
111
+ }
112
+
113
+ /// Get the shared database. Internally, this is an
114
+ /// `Arc`, so a clone only increments the ref count.
115
+ pub ( crate ) fn db ( & self ) -> Db {
116
+ self . db . clone ( )
117
+ }
118
+ }
119
+
120
+ impl Drop for DbDropGuard {
121
+ fn drop ( & mut self ) {
122
+ // Signal the 'Db' instance to shut down the task that purges expired keys
123
+ self . db . shutdown_purge_task ( ) ;
124
+ }
125
+ }
126
+
95
127
impl Db {
96
128
/// Create a new, empty, `Db` instance. Allocates shared state and spawns a
97
129
/// background task to manage key expiration.
@@ -244,28 +276,20 @@ impl Db {
244
276
// subscribers. In this case, return `0`.
245
277
. unwrap_or ( 0 )
246
278
}
247
- }
248
279
249
- impl Drop for Db {
250
- fn drop ( & mut self ) {
251
- // If this is the last active `Db` instance, the background task must be
252
- // notified to shut down.
253
- //
254
- // First, determine if this is the last `Db` instance. This is done by
255
- // checking `strong_count`. The count will be 2. One for this `Db`
256
- // instance and one for the handle held by the background task.
257
- if Arc :: strong_count ( & self . shared ) == 2 {
258
- // The background task must be signaled to shutdown. This is done by
259
- // setting `State::shutdown` to `true` and signalling the task.
260
- let mut state = self . shared . state . lock ( ) . unwrap ( ) ;
261
- state. shutdown = true ;
262
-
263
- // Drop the lock before signalling the background task. This helps
264
- // reduce lock contention by ensuring the background task doesn't
265
- // wake up only to be unable to acquire the mutex.
266
- drop ( state) ;
267
- self . shared . background_task . notify_one ( ) ;
268
- }
280
+ /// Signals the purge background task to shut down. This is called by the
281
+ /// `DbShutdown`s `Drop` implementation.
282
+ fn shutdown_purge_task ( & self ) {
283
+ // The background task must be signaled to shut down. This is done by
284
+ // setting `State::shutdown` to `true` and signalling the task.
285
+ let mut state = self . shared . state . lock ( ) . unwrap ( ) ;
286
+ state. shutdown = true ;
287
+
288
+ // Drop the lock before signalling the background task. This helps
289
+ // reduce lock contention by ensuring the background task doesn't
290
+ // wake up only to be unable to acquire the mutex.
291
+ drop ( state) ;
292
+ self . shared . background_task . notify_one ( ) ;
269
293
}
270
294
}
271
295
@@ -349,4 +373,6 @@ async fn purge_expired_tasks(shared: Arc<Shared>) {
349
373
shared. background_task . notified ( ) . await ;
350
374
}
351
375
}
376
+
377
+ debug ! ( "Purge background task shut down" )
352
378
}
0 commit comments