1 //! Windows asynchronous process handling.
2 //!
3 //! Like with Unix we don't actually have a way of registering a process with an
4 //! IOCP object. As a result we similarly need another mechanism for getting a
5 //! signal when a process has exited. For now this is implemented with the
6 //! `RegisterWaitForSingleObject` function in the kernel32.dll.
7 //!
8 //! This strategy is the same that libuv takes and essentially just queues up a
9 //! wait for the process in a kernel32-specific thread pool. Once the object is
10 //! notified (e.g. the process exits) then we have a callback that basically
11 //! just completes a `Oneshot`.
12 //!
13 //! The `poll_exit` implementation will attempt to wait for the process in a
14 //! nonblocking fashion, but failing that it'll fire off a
15 //! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot
16 //! from then on out.
17 
18 use crate::io::{blocking::Blocking, AsyncRead, AsyncWrite, ReadBuf};
19 use crate::process::kill::Kill;
20 use crate::process::SpawnedChild;
21 use crate::sync::oneshot;
22 
23 use std::fmt;
24 use std::fs::File as StdFile;
25 use std::future::Future;
26 use std::io;
27 use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, OwnedHandle, RawHandle};
28 use std::pin::Pin;
29 use std::process::Stdio;
30 use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
31 use std::sync::Arc;
32 use std::task::{Context, Poll};
33 
34 use windows_sys::{
35     Win32::Foundation::{
36         DuplicateHandle, BOOLEAN, DUPLICATE_SAME_ACCESS, HANDLE, INVALID_HANDLE_VALUE,
37     },
38     Win32::System::Threading::{
39         GetCurrentProcess, RegisterWaitForSingleObject, UnregisterWaitEx, INFINITE,
40         WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE,
41     },
42 };
43 
44 #[must_use = "futures do nothing unless polled"]
45 pub(crate) struct Child {
46     child: StdChild,
47     waiting: Option<Waiting>,
48 }
49 
50 impl fmt::Debug for Child {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result51     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
52         fmt.debug_struct("Child")
53             .field("pid", &self.id())
54             .field("child", &self.child)
55             .field("waiting", &"..")
56             .finish()
57     }
58 }
59 
60 struct Waiting {
61     rx: oneshot::Receiver<()>,
62     wait_object: HANDLE,
63     tx: *mut Option<oneshot::Sender<()>>,
64 }
65 
66 unsafe impl Sync for Waiting {}
67 unsafe impl Send for Waiting {}
68 
spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild>69 pub(crate) fn spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild> {
70     let mut child = cmd.spawn()?;
71     let stdin = child.stdin.take().map(stdio).transpose()?;
72     let stdout = child.stdout.take().map(stdio).transpose()?;
73     let stderr = child.stderr.take().map(stdio).transpose()?;
74 
75     Ok(SpawnedChild {
76         child: Child {
77             child,
78             waiting: None,
79         },
80         stdin,
81         stdout,
82         stderr,
83     })
84 }
85 
86 impl Child {
id(&self) -> u3287     pub(crate) fn id(&self) -> u32 {
88         self.child.id()
89     }
90 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>91     pub(crate) fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
92         self.child.try_wait()
93     }
94 }
95 
96 impl Kill for Child {
kill(&mut self) -> io::Result<()>97     fn kill(&mut self) -> io::Result<()> {
98         self.child.kill()
99     }
100 }
101 
102 impl Future for Child {
103     type Output = io::Result<ExitStatus>;
104 
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>105     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
106         let inner = Pin::get_mut(self);
107         loop {
108             if let Some(ref mut w) = inner.waiting {
109                 match Pin::new(&mut w.rx).poll(cx) {
110                     Poll::Ready(Ok(())) => {}
111                     Poll::Ready(Err(_)) => panic!("should not be canceled"),
112                     Poll::Pending => return Poll::Pending,
113                 }
114                 let status = inner.try_wait()?.expect("not ready yet");
115                 return Poll::Ready(Ok(status));
116             }
117 
118             if let Some(e) = inner.try_wait()? {
119                 return Poll::Ready(Ok(e));
120             }
121             let (tx, rx) = oneshot::channel();
122             let ptr = Box::into_raw(Box::new(Some(tx)));
123             let mut wait_object = 0;
124             let rc = unsafe {
125                 RegisterWaitForSingleObject(
126                     &mut wait_object,
127                     inner.child.as_raw_handle() as _,
128                     Some(callback),
129                     ptr as *mut _,
130                     INFINITE,
131                     WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE,
132                 )
133             };
134             if rc == 0 {
135                 let err = io::Error::last_os_error();
136                 drop(unsafe { Box::from_raw(ptr) });
137                 return Poll::Ready(Err(err));
138             }
139             inner.waiting = Some(Waiting {
140                 rx,
141                 wait_object,
142                 tx: ptr,
143             });
144         }
145     }
146 }
147 
148 impl AsRawHandle for Child {
as_raw_handle(&self) -> RawHandle149     fn as_raw_handle(&self) -> RawHandle {
150         self.child.as_raw_handle()
151     }
152 }
153 
154 impl Drop for Waiting {
drop(&mut self)155     fn drop(&mut self) {
156         unsafe {
157             let rc = UnregisterWaitEx(self.wait_object, INVALID_HANDLE_VALUE);
158             if rc == 0 {
159                 panic!("failed to unregister: {}", io::Error::last_os_error());
160             }
161             drop(Box::from_raw(self.tx));
162         }
163     }
164 }
165 
callback(ptr: *mut std::ffi::c_void, _timer_fired: BOOLEAN)166 unsafe extern "system" fn callback(ptr: *mut std::ffi::c_void, _timer_fired: BOOLEAN) {
167     let complete = &mut *(ptr as *mut Option<oneshot::Sender<()>>);
168     let _ = complete.take().unwrap().send(());
169 }
170 
171 #[derive(Debug)]
172 struct ArcFile(Arc<StdFile>);
173 
174 impl io::Read for ArcFile {
read(&mut self, bytes: &mut [u8]) -> io::Result<usize>175     fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
176         (&*self.0).read(bytes)
177     }
178 }
179 
180 impl io::Write for ArcFile {
write(&mut self, bytes: &[u8]) -> io::Result<usize>181     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
182         (&*self.0).write(bytes)
183     }
184 
flush(&mut self) -> io::Result<()>185     fn flush(&mut self) -> io::Result<()> {
186         (&*self.0).flush()
187     }
188 }
189 
190 #[derive(Debug)]
191 pub(crate) struct ChildStdio {
192     // Used for accessing the raw handle, even if the io version is busy
193     raw: Arc<StdFile>,
194     // For doing I/O operations asynchronously
195     io: Blocking<ArcFile>,
196 }
197 
198 impl ChildStdio {
into_owned_handle(self) -> io::Result<OwnedHandle>199     pub(super) fn into_owned_handle(self) -> io::Result<OwnedHandle> {
200         convert_to_file(self).map(OwnedHandle::from)
201     }
202 }
203 
204 impl AsRawHandle for ChildStdio {
as_raw_handle(&self) -> RawHandle205     fn as_raw_handle(&self) -> RawHandle {
206         self.raw.as_raw_handle()
207     }
208 }
209 
210 impl AsyncRead for ChildStdio {
poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>211     fn poll_read(
212         mut self: Pin<&mut Self>,
213         cx: &mut Context<'_>,
214         buf: &mut ReadBuf<'_>,
215     ) -> Poll<io::Result<()>> {
216         Pin::new(&mut self.io).poll_read(cx, buf)
217     }
218 }
219 
220 impl AsyncWrite for ChildStdio {
poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<io::Result<usize>>221     fn poll_write(
222         mut self: Pin<&mut Self>,
223         cx: &mut Context<'_>,
224         buf: &[u8],
225     ) -> Poll<io::Result<usize>> {
226         Pin::new(&mut self.io).poll_write(cx, buf)
227     }
228 
poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>229     fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
230         Pin::new(&mut self.io).poll_flush(cx)
231     }
232 
poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>233     fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
234         Pin::new(&mut self.io).poll_shutdown(cx)
235     }
236 }
237 
stdio<T>(io: T) -> io::Result<ChildStdio> where T: IntoRawHandle,238 pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
239 where
240     T: IntoRawHandle,
241 {
242     use std::os::windows::prelude::FromRawHandle;
243 
244     let raw = Arc::new(unsafe { StdFile::from_raw_handle(io.into_raw_handle()) });
245     let io = Blocking::new(ArcFile(raw.clone()));
246     Ok(ChildStdio { raw, io })
247 }
248 
convert_to_file(child_stdio: ChildStdio) -> io::Result<StdFile>249 fn convert_to_file(child_stdio: ChildStdio) -> io::Result<StdFile> {
250     let ChildStdio { raw, io } = child_stdio;
251     drop(io); // Try to drop the Arc count here
252 
253     Arc::try_unwrap(raw).or_else(|raw| duplicate_handle(&*raw))
254 }
255 
convert_to_stdio(child_stdio: ChildStdio) -> io::Result<Stdio>256 pub(crate) fn convert_to_stdio(child_stdio: ChildStdio) -> io::Result<Stdio> {
257     convert_to_file(child_stdio).map(Stdio::from)
258 }
259 
duplicate_handle<T: AsRawHandle>(io: &T) -> io::Result<StdFile>260 fn duplicate_handle<T: AsRawHandle>(io: &T) -> io::Result<StdFile> {
261     use std::os::windows::prelude::FromRawHandle;
262 
263     unsafe {
264         let mut dup_handle = INVALID_HANDLE_VALUE;
265         let cur_proc = GetCurrentProcess();
266 
267         let status = DuplicateHandle(
268             cur_proc,
269             io.as_raw_handle() as _,
270             cur_proc,
271             &mut dup_handle,
272             0,
273             0,
274             DUPLICATE_SAME_ACCESS,
275         );
276 
277         if status == 0 {
278             return Err(io::Error::last_os_error());
279         }
280 
281         Ok(StdFile::from_raw_handle(dup_handle as _))
282     }
283 }
284