@@ -17,10 +17,20 @@ use pin_project_lite::pin_project;
17
17
use tokio:: sync:: watch;
18
18
19
19
/// A graceful shutdown utility
20
+ // Purposefully not `Clone`, see `watcher()` method for why.
20
21
pub struct GracefulShutdown {
21
22
tx : watch:: Sender < ( ) > ,
22
23
}
23
24
25
+ /// A watcher side of the graceful shutdown.
26
+ ///
27
+ /// This type can only watch a connection, it cannot trigger a shutdown.
28
+ ///
29
+ /// Call [`GracefulShutdown::watcher()`] to construct one of these.
30
+ pub struct Watcher {
31
+ rx : watch:: Receiver < ( ) > ,
32
+ }
33
+
24
34
impl GracefulShutdown {
25
35
/// Create a new graceful shutdown helper.
26
36
pub fn new ( ) -> Self {
@@ -30,12 +40,20 @@ impl GracefulShutdown {
30
40
31
41
/// Wrap a future for graceful shutdown watching.
32
42
pub fn watch < C : GracefulConnection > ( & self , conn : C ) -> impl Future < Output = C :: Output > {
33
- let mut rx = self . tx . subscribe ( ) ;
34
- GracefulConnectionFuture :: new ( conn, async move {
35
- let _ = rx. changed ( ) . await ;
36
- // hold onto the rx until the watched future is completed
37
- rx
38
- } )
43
+ self . watcher ( ) . watch ( conn)
44
+ }
45
+
46
+ /// Create an owned type that can watch a connection.
47
+ ///
48
+ /// This method allows created an owned type that can be sent onto another
49
+ /// task before calling [`Watcher::watch()`].
50
+ // Internal: this function exists because `Clone` allows footguns.
51
+ // If the `tx` were cloned (or the `rx`), race conditions can happens where
52
+ // one task starting a shutdown is scheduled and interwined with a task
53
+ // starting to watch a connection, and the "watch version" is one behind.
54
+ pub fn watcher ( & self ) -> Watcher {
55
+ let rx = self . tx . subscribe ( ) ;
56
+ Watcher { rx }
39
57
}
40
58
41
59
/// Signal shutdown for all watched connections.
@@ -64,6 +82,24 @@ impl Default for GracefulShutdown {
64
82
}
65
83
}
66
84
85
+ impl Watcher {
86
+ /// Wrap a future for graceful shutdown watching.
87
+ pub fn watch < C : GracefulConnection > ( self , conn : C ) -> impl Future < Output = C :: Output > {
88
+ let Watcher { mut rx } = self ;
89
+ GracefulConnectionFuture :: new ( conn, async move {
90
+ let _ = rx. changed ( ) . await ;
91
+ // hold onto the rx until the watched future is completed
92
+ rx
93
+ } )
94
+ }
95
+ }
96
+
97
+ impl Debug for Watcher {
98
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
99
+ f. debug_struct ( "GracefulWatcher" ) . finish ( )
100
+ }
101
+ }
102
+
67
103
pin_project ! {
68
104
struct GracefulConnectionFuture <C , F : Future > {
69
105
#[ pin]
0 commit comments