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:: { process:: ExitCode , time:: Duration } ;
6
+ use std:: { future :: Future , pin :: Pin , process:: ExitCode , time:: Duration } ;
7
7
8
+ use futures_util:: future:: BoxFuture ;
9
+ use mas_handlers:: ActivityTracker ;
10
+ use mas_templates:: Templates ;
8
11
use tokio:: signal:: unix:: { Signal , SignalKind } ;
9
12
use tokio_util:: { sync:: CancellationToken , task:: TaskTracker } ;
10
13
@@ -28,7 +31,30 @@ pub struct ShutdownManager {
28
31
task_tracker : TaskTracker ,
29
32
sigterm : Signal ,
30
33
sigint : Signal ,
34
+ sighup : Signal ,
31
35
timeout : Duration ,
36
+ reload_handlers : Vec < Box < dyn Fn ( ) -> BoxFuture < ' static , ( ) > > > ,
37
+ }
38
+
39
+ pub trait Reloadable : Clone + Send {
40
+ fn reload ( & self ) -> impl Future < Output = ( ) > + Send ;
41
+ }
42
+
43
+ impl Reloadable for ActivityTracker {
44
+ async fn reload ( & self ) {
45
+ self . flush ( ) . await ;
46
+ }
47
+ }
48
+
49
+ impl Reloadable for Templates {
50
+ async fn reload ( & self ) {
51
+ if let Err ( err) = self . reload ( ) . await {
52
+ tracing:: error!(
53
+ error = & err as & dyn std:: error:: Error ,
54
+ "Failed to reload templates"
55
+ ) ;
56
+ }
57
+ }
32
58
}
33
59
34
60
impl ShutdownManager {
@@ -42,6 +68,7 @@ impl ShutdownManager {
42
68
let soft_shutdown_token = hard_shutdown_token. child_token ( ) ;
43
69
let sigterm = tokio:: signal:: unix:: signal ( SignalKind :: terminate ( ) ) ?;
44
70
let sigint = tokio:: signal:: unix:: signal ( SignalKind :: interrupt ( ) ) ?;
71
+ let sighup = tokio:: signal:: unix:: signal ( SignalKind :: hangup ( ) ) ?;
45
72
let timeout = Duration :: from_secs ( 60 ) ;
46
73
let task_tracker = TaskTracker :: new ( ) ;
47
74
@@ -51,10 +78,21 @@ impl ShutdownManager {
51
78
task_tracker,
52
79
sigterm,
53
80
sigint,
81
+ sighup,
54
82
timeout,
83
+ reload_handlers : Vec :: new ( ) ,
55
84
} )
56
85
}
57
86
87
+ /// Add a handler to be called when the server gets a SIGHUP
88
+ pub fn register_reloadable ( & mut self , reloadable : & ( impl Reloadable + ' static ) ) {
89
+ let reloadable = reloadable. clone ( ) ;
90
+ self . reload_handlers . push ( Box :: new ( move || {
91
+ let reloadable = reloadable. clone ( ) ;
92
+ Box :: pin ( async move { reloadable. reload ( ) . await } )
93
+ } ) ) ;
94
+ }
95
+
58
96
/// Get a reference to the task tracker
59
97
#[ must_use]
60
98
pub fn task_tracker ( & self ) -> & TaskTracker {
@@ -76,21 +114,36 @@ impl ShutdownManager {
76
114
/// Run until we finish completely shutting down.
77
115
pub async fn run ( mut self ) -> ExitCode {
78
116
// Wait for a first signal and trigger the soft shutdown
79
- let likely_crashed = tokio:: select! {
80
- ( ) = self . soft_shutdown_token. cancelled( ) => {
81
- tracing:: warn!( "Another task triggered a shutdown, it likely crashed! Shutting down" ) ;
82
- true
83
- } ,
84
-
85
- _ = self . sigterm. recv( ) => {
86
- tracing:: info!( "Shutdown signal received (SIGTERM), shutting down" ) ;
87
- false
88
- } ,
89
-
90
- _ = self . sigint. recv( ) => {
91
- tracing:: info!( "Shutdown signal received (SIGINT), shutting down" ) ;
92
- false
93
- } ,
117
+ let likely_crashed = loop {
118
+ tokio:: select! {
119
+ ( ) = self . soft_shutdown_token. cancelled( ) => {
120
+ tracing:: warn!( "Another task triggered a shutdown, it likely crashed! Shutting down" ) ;
121
+ break true ;
122
+ } ,
123
+
124
+ _ = self . sigterm. recv( ) => {
125
+ tracing:: info!( "Shutdown signal received (SIGTERM), shutting down" ) ;
126
+ break false ;
127
+ } ,
128
+
129
+ _ = self . sigint. recv( ) => {
130
+ tracing:: info!( "Shutdown signal received (SIGINT), shutting down" ) ;
131
+ break false ;
132
+ } ,
133
+
134
+ _ = self . sighup. recv( ) => {
135
+ tracing:: info!( "Reload signal received (SIGHUP), reloading" ) ;
136
+
137
+ // XXX: if the handler takes a long time, it will block the
138
+ // rest of the shutdown process, which is not ideal. We
139
+ // should probably have a timeout here
140
+ for handler in & self . reload_handlers {
141
+ handler( ) . await ;
142
+ }
143
+
144
+ tracing:: info!( "Reloading done" ) ;
145
+ } ,
146
+ }
94
147
} ;
95
148
96
149
self . soft_shutdown_token . cancel ( ) ;
0 commit comments