1 use std::io;
2 use std::sync::Once;
3
4 use crate::signal::registry::{globals, EventId, EventInfo, Init, Storage};
5 use crate::signal::RxFuture;
6
7 use windows_sys::Win32::Foundation::BOOL;
8 use windows_sys::Win32::System::Console as console;
9
ctrl_break() -> io::Result<RxFuture>10 pub(super) fn ctrl_break() -> io::Result<RxFuture> {
11 new(console::CTRL_BREAK_EVENT)
12 }
13
ctrl_close() -> io::Result<RxFuture>14 pub(super) fn ctrl_close() -> io::Result<RxFuture> {
15 new(console::CTRL_CLOSE_EVENT)
16 }
17
ctrl_c() -> io::Result<RxFuture>18 pub(super) fn ctrl_c() -> io::Result<RxFuture> {
19 new(console::CTRL_C_EVENT)
20 }
21
ctrl_logoff() -> io::Result<RxFuture>22 pub(super) fn ctrl_logoff() -> io::Result<RxFuture> {
23 new(console::CTRL_LOGOFF_EVENT)
24 }
25
ctrl_shutdown() -> io::Result<RxFuture>26 pub(super) fn ctrl_shutdown() -> io::Result<RxFuture> {
27 new(console::CTRL_SHUTDOWN_EVENT)
28 }
29
new(signum: u32) -> io::Result<RxFuture>30 fn new(signum: u32) -> io::Result<RxFuture> {
31 global_init()?;
32 let rx = globals().register_listener(signum as EventId);
33 Ok(RxFuture::new(rx))
34 }
35
36 #[derive(Debug)]
37 pub(crate) struct OsStorage {
38 ctrl_break: EventInfo,
39 ctrl_close: EventInfo,
40 ctrl_c: EventInfo,
41 ctrl_logoff: EventInfo,
42 ctrl_shutdown: EventInfo,
43 }
44
45 impl Init for OsStorage {
init() -> Self46 fn init() -> Self {
47 Self {
48 ctrl_break: Default::default(),
49 ctrl_close: Default::default(),
50 ctrl_c: Default::default(),
51 ctrl_logoff: Default::default(),
52 ctrl_shutdown: Default::default(),
53 }
54 }
55 }
56
57 impl Storage for OsStorage {
event_info(&self, id: EventId) -> Option<&EventInfo>58 fn event_info(&self, id: EventId) -> Option<&EventInfo> {
59 match u32::try_from(id) {
60 Ok(console::CTRL_BREAK_EVENT) => Some(&self.ctrl_break),
61 Ok(console::CTRL_CLOSE_EVENT) => Some(&self.ctrl_close),
62 Ok(console::CTRL_C_EVENT) => Some(&self.ctrl_c),
63 Ok(console::CTRL_LOGOFF_EVENT) => Some(&self.ctrl_logoff),
64 Ok(console::CTRL_SHUTDOWN_EVENT) => Some(&self.ctrl_shutdown),
65 _ => None,
66 }
67 }
68
for_each<'a, F>(&'a self, mut f: F) where F: FnMut(&'a EventInfo),69 fn for_each<'a, F>(&'a self, mut f: F)
70 where
71 F: FnMut(&'a EventInfo),
72 {
73 f(&self.ctrl_break);
74 f(&self.ctrl_close);
75 f(&self.ctrl_c);
76 f(&self.ctrl_logoff);
77 f(&self.ctrl_shutdown);
78 }
79 }
80
81 #[derive(Debug)]
82 pub(crate) struct OsExtraData {}
83
84 impl Init for OsExtraData {
init() -> Self85 fn init() -> Self {
86 Self {}
87 }
88 }
89
global_init() -> io::Result<()>90 fn global_init() -> io::Result<()> {
91 static INIT: Once = Once::new();
92
93 let mut init = None;
94
95 INIT.call_once(|| unsafe {
96 let rc = console::SetConsoleCtrlHandler(Some(handler), 1);
97 let ret = if rc == 0 {
98 Err(io::Error::last_os_error())
99 } else {
100 Ok(())
101 };
102
103 init = Some(ret);
104 });
105
106 init.unwrap_or_else(|| Ok(()))
107 }
108
handler(ty: u32) -> BOOL109 unsafe extern "system" fn handler(ty: u32) -> BOOL {
110 let globals = globals();
111 globals.record_event(ty as EventId);
112
113 // According to https://docs.microsoft.com/en-us/windows/console/handlerroutine
114 // the handler routine is always invoked in a new thread, thus we don't
115 // have the same restrictions as in Unix signal handlers, meaning we can
116 // go ahead and perform the broadcast here.
117 if globals.broadcast() {
118 1
119 } else {
120 // No one is listening for this notification any more
121 // let the OS fire the next (possibly the default) handler.
122 0
123 }
124 }
125
126 #[cfg(all(test, not(loom)))]
127 mod tests {
128 use super::*;
129 use crate::runtime::Runtime;
130
131 use tokio_test::{assert_ok, assert_pending, assert_ready_ok, task};
132
133 #[test]
ctrl_c()134 fn ctrl_c() {
135 let rt = rt();
136 let _enter = rt.enter();
137
138 let mut ctrl_c = task::spawn(crate::signal::ctrl_c());
139
140 assert_pending!(ctrl_c.poll());
141
142 // Windows doesn't have a good programmatic way of sending events
143 // like sending signals on Unix, so we'll stub out the actual OS
144 // integration and test that our handling works.
145 unsafe {
146 super::handler(console::CTRL_C_EVENT);
147 }
148
149 assert_ready_ok!(ctrl_c.poll());
150 }
151
152 #[test]
ctrl_break()153 fn ctrl_break() {
154 let rt = rt();
155
156 rt.block_on(async {
157 let mut ctrl_break = assert_ok!(crate::signal::windows::ctrl_break());
158
159 // Windows doesn't have a good programmatic way of sending events
160 // like sending signals on Unix, so we'll stub out the actual OS
161 // integration and test that our handling works.
162 unsafe {
163 super::handler(console::CTRL_BREAK_EVENT);
164 }
165
166 ctrl_break.recv().await.unwrap();
167 });
168 }
169
170 #[test]
ctrl_close()171 fn ctrl_close() {
172 let rt = rt();
173
174 rt.block_on(async {
175 let mut ctrl_close = assert_ok!(crate::signal::windows::ctrl_close());
176
177 // Windows doesn't have a good programmatic way of sending events
178 // like sending signals on Unix, so we'll stub out the actual OS
179 // integration and test that our handling works.
180 unsafe {
181 super::handler(console::CTRL_CLOSE_EVENT);
182 }
183
184 ctrl_close.recv().await.unwrap();
185 });
186 }
187
188 #[test]
ctrl_shutdown()189 fn ctrl_shutdown() {
190 let rt = rt();
191
192 rt.block_on(async {
193 let mut ctrl_shutdown = assert_ok!(crate::signal::windows::ctrl_shutdown());
194
195 // Windows doesn't have a good programmatic way of sending events
196 // like sending signals on Unix, so we'll stub out the actual OS
197 // integration and test that our handling works.
198 unsafe {
199 super::handler(console::CTRL_SHUTDOWN_EVENT);
200 }
201
202 ctrl_shutdown.recv().await.unwrap();
203 });
204 }
205
206 #[test]
ctrl_logoff()207 fn ctrl_logoff() {
208 let rt = rt();
209
210 rt.block_on(async {
211 let mut ctrl_logoff = assert_ok!(crate::signal::windows::ctrl_logoff());
212
213 // Windows doesn't have a good programmatic way of sending events
214 // like sending signals on Unix, so we'll stub out the actual OS
215 // integration and test that our handling works.
216 unsafe {
217 super::handler(console::CTRL_LOGOFF_EVENT);
218 }
219
220 ctrl_logoff.recv().await.unwrap();
221 });
222 }
223
rt() -> Runtime224 fn rt() -> Runtime {
225 crate::runtime::Builder::new_current_thread()
226 .build()
227 .unwrap()
228 }
229 }
230