@@ -4,6 +4,17 @@ use tokio::time::{self, Duration, Instant};
44use bytes:: Bytes ;
55use std:: collections:: { BTreeMap , HashMap } ;
66use 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+ }
718
819/// Server state shared across all connections.
920///
@@ -92,6 +103,27 @@ struct Entry {
92103 expires_at : Option < Instant > ,
93104}
94105
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+
95127impl Db {
96128 /// Create a new, empty, `Db` instance. Allocates shared state and spawns a
97129 /// background task to manage key expiration.
@@ -244,28 +276,20 @@ impl Db {
244276 // subscribers. In this case, return `0`.
245277 . unwrap_or ( 0 )
246278 }
247- }
248279
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 ( ) ;
269293 }
270294}
271295
@@ -349,4 +373,6 @@ async fn purge_expired_tasks(shared: Arc<Shared>) {
349373 shared. background_task . notified ( ) . await ;
350374 }
351375 }
376+
377+ debug ! ( "Purge background task shut down" )
352378}
0 commit comments