Skip to content

Commit a27575f

Browse files
authored
signal: fix CTRL_CLOSE, CTRL_LOGOFF, CTRL_SHUTDOWN on windows (#7122)
1 parent 13fbdac commit a27575f

File tree

1 file changed

+39
-6
lines changed
  • tokio/src/signal/windows

1 file changed

+39
-6
lines changed

tokio/src/signal/windows/sys.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ fn new(signum: u32) -> io::Result<RxFuture> {
3333
Ok(RxFuture::new(rx))
3434
}
3535

36+
fn event_requires_infinite_sleep_in_handler(signum: u32) -> bool {
37+
// Returning from the handler function of those events immediately terminates the process.
38+
// So for async systems, the easiest solution is to simply never return from
39+
// the handler function.
40+
//
41+
// For more information, see:
42+
// https://learn.microsoft.com/en-us/windows/console/handlerroutine#remarks
43+
match signum {
44+
console::CTRL_CLOSE_EVENT => true,
45+
console::CTRL_LOGOFF_EVENT => true,
46+
console::CTRL_SHUTDOWN_EVENT => true,
47+
_ => false,
48+
}
49+
}
50+
3651
#[derive(Debug)]
3752
pub(crate) struct OsStorage {
3853
ctrl_break: EventInfo,
@@ -114,7 +129,15 @@ unsafe extern "system" fn handler(ty: u32) -> BOOL {
114129
// the handler routine is always invoked in a new thread, thus we don't
115130
// have the same restrictions as in Unix signal handlers, meaning we can
116131
// go ahead and perform the broadcast here.
117-
if globals.broadcast() {
132+
let event_was_handled = globals.broadcast();
133+
134+
if event_was_handled && event_requires_infinite_sleep_in_handler(ty) {
135+
loop {
136+
std::thread::park();
137+
}
138+
}
139+
140+
if event_was_handled {
118141
1
119142
} else {
120143
// No one is listening for this notification any more
@@ -130,6 +153,16 @@ mod tests {
130153

131154
use tokio_test::{assert_ok, assert_pending, assert_ready_ok, task};
132155

156+
unsafe fn raise_event(signum: u32) {
157+
if event_requires_infinite_sleep_in_handler(signum) {
158+
// Those events will enter an infinite loop in `handler`, so
159+
// we need to run them on a separate thread
160+
std::thread::spawn(move || super::handler(signum));
161+
} else {
162+
super::handler(signum);
163+
}
164+
}
165+
133166
#[test]
134167
fn ctrl_c() {
135168
let rt = rt();
@@ -143,7 +176,7 @@ mod tests {
143176
// like sending signals on Unix, so we'll stub out the actual OS
144177
// integration and test that our handling works.
145178
unsafe {
146-
super::handler(console::CTRL_C_EVENT);
179+
raise_event(console::CTRL_C_EVENT);
147180
}
148181

149182
assert_ready_ok!(ctrl_c.poll());
@@ -160,7 +193,7 @@ mod tests {
160193
// like sending signals on Unix, so we'll stub out the actual OS
161194
// integration and test that our handling works.
162195
unsafe {
163-
super::handler(console::CTRL_BREAK_EVENT);
196+
raise_event(console::CTRL_BREAK_EVENT);
164197
}
165198

166199
ctrl_break.recv().await.unwrap();
@@ -178,7 +211,7 @@ mod tests {
178211
// like sending signals on Unix, so we'll stub out the actual OS
179212
// integration and test that our handling works.
180213
unsafe {
181-
super::handler(console::CTRL_CLOSE_EVENT);
214+
raise_event(console::CTRL_CLOSE_EVENT);
182215
}
183216

184217
ctrl_close.recv().await.unwrap();
@@ -196,7 +229,7 @@ mod tests {
196229
// like sending signals on Unix, so we'll stub out the actual OS
197230
// integration and test that our handling works.
198231
unsafe {
199-
super::handler(console::CTRL_SHUTDOWN_EVENT);
232+
raise_event(console::CTRL_SHUTDOWN_EVENT);
200233
}
201234

202235
ctrl_shutdown.recv().await.unwrap();
@@ -214,7 +247,7 @@ mod tests {
214247
// like sending signals on Unix, so we'll stub out the actual OS
215248
// integration and test that our handling works.
216249
unsafe {
217-
super::handler(console::CTRL_LOGOFF_EVENT);
250+
raise_event(console::CTRL_LOGOFF_EVENT);
218251
}
219252

220253
ctrl_logoff.recv().await.unwrap();

0 commit comments

Comments
 (0)