1
- // Copyright 2024 New Vector Ltd.
1
+ // Copyright 2024, 2025 New Vector Ltd.
2
2
//
3
3
// SPDX-License-Identifier: AGPL-3.0-only
4
4
// Please see LICENSE in the repository root for full details.
5
5
6
- use std:: { future:: Future , pin :: Pin , process:: ExitCode , time:: Duration } ;
6
+ use std:: { future:: Future , process:: ExitCode , time:: Duration } ;
7
7
8
8
use futures_util:: future:: BoxFuture ;
9
9
use mas_handlers:: ActivityTracker ;
@@ -36,6 +36,7 @@ pub struct ShutdownManager {
36
36
reload_handlers : Vec < Box < dyn Fn ( ) -> BoxFuture < ' static , ( ) > > > ,
37
37
}
38
38
39
+ /// Represents a thing that can be reloaded with a SIGHUP
39
40
pub trait Reloadable : Clone + Send {
40
41
fn reload ( & self ) -> impl Future < Output = ( ) > + Send ;
41
42
}
@@ -57,6 +58,16 @@ impl Reloadable for Templates {
57
58
}
58
59
}
59
60
61
+ /// A wrapper around [`sd_notify::notify`] that logs any errors
62
+ fn notify ( states : & [ sd_notify:: NotifyState ] ) {
63
+ if let Err ( e) = sd_notify:: notify ( false , states) {
64
+ tracing:: error!(
65
+ error = & e as & dyn std:: error:: Error ,
66
+ "Failed to notify service manager"
67
+ ) ;
68
+ }
69
+ }
70
+
60
71
impl ShutdownManager {
61
72
/// Create a new shutdown manager, installing the signal handlers
62
73
///
@@ -113,7 +124,13 @@ impl ShutdownManager {
113
124
114
125
/// Run until we finish completely shutting down.
115
126
pub async fn run ( mut self ) -> ExitCode {
116
- // Wait for a first signal and trigger the soft shutdown
127
+ notify ( & [ sd_notify:: NotifyState :: Ready ] ) ;
128
+
129
+ let mut watchdog_usec = 0 ;
130
+ let watchdog_enabled = sd_notify:: watchdog_enabled ( false , & mut watchdog_usec) ;
131
+ let mut watchdog_interval = tokio:: time:: interval ( Duration :: from_micros ( watchdog_usec / 2 ) ) ;
132
+
133
+ // Wait for a first shutdown signal and trigger the soft shutdown
117
134
let likely_crashed = loop {
118
135
tokio:: select! {
119
136
( ) = self . soft_shutdown_token. cancelled( ) => {
@@ -131,21 +148,37 @@ impl ShutdownManager {
131
148
break false ;
132
149
} ,
133
150
151
+ _ = watchdog_interval. tick( ) , if watchdog_enabled => {
152
+ notify( & [
153
+ sd_notify:: NotifyState :: Watchdog ,
154
+ ] ) ;
155
+ } ,
156
+
134
157
_ = self . sighup. recv( ) => {
135
158
tracing:: info!( "Reload signal received (SIGHUP), reloading" ) ;
136
159
160
+ notify( & [
161
+ sd_notify:: NotifyState :: Reloading ,
162
+ sd_notify:: NotifyState :: monotonic_usec_now( )
163
+ . expect( "Failed to read monotonic clock" )
164
+ ] ) ;
165
+
137
166
// XXX: if the handler takes a long time, it will block the
138
167
// rest of the shutdown process, which is not ideal. We
139
168
// should probably have a timeout here
140
169
for handler in & self . reload_handlers {
141
170
handler( ) . await ;
142
171
}
143
172
173
+ notify( & [ sd_notify:: NotifyState :: Ready ] ) ;
174
+
144
175
tracing:: info!( "Reloading done" ) ;
145
176
} ,
146
177
}
147
178
} ;
148
179
180
+ notify ( & [ sd_notify:: NotifyState :: Stopping ] ) ;
181
+
149
182
self . soft_shutdown_token . cancel ( ) ;
150
183
self . task_tracker . close ( ) ;
151
184
0 commit comments