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::PollEvented;
19 use crate::process::kill::Kill;
20 use crate::process::SpawnedChild;
21 use crate::sync::oneshot;
22
23 use mio::windows::NamedPipe;
24 use std::fmt;
25 use std::future::Future;
26 use std::io;
27 use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle};
28 use std::pin::Pin;
29 use std::process::Stdio;
30 use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
31 use std::ptr;
32 use std::task::Context;
33 use std::task::Poll;
34 use winapi::shared::minwindef::{DWORD, FALSE};
35 use winapi::um::handleapi::{DuplicateHandle, INVALID_HANDLE_VALUE};
36 use winapi::um::processthreadsapi::GetCurrentProcess;
37 use winapi::um::threadpoollegacyapiset::UnregisterWaitEx;
38 use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE};
39 use winapi::um::winnt::{
40 BOOLEAN, DUPLICATE_SAME_ACCESS, HANDLE, PVOID, WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE,
41 };
42
43 #[must_use = "futures do nothing unless polled"]
44 pub(crate) struct Child {
45 child: StdChild,
46 waiting: Option<Waiting>,
47 }
48
49 impl fmt::Debug for Child {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result50 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
51 fmt.debug_struct("Child")
52 .field("pid", &self.id())
53 .field("child", &self.child)
54 .field("waiting", &"..")
55 .finish()
56 }
57 }
58
59 struct Waiting {
60 rx: oneshot::Receiver<()>,
61 wait_object: HANDLE,
62 tx: *mut Option<oneshot::Sender<()>>,
63 }
64
65 unsafe impl Sync for Waiting {}
66 unsafe impl Send for Waiting {}
67
spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild>68 pub(crate) fn spawn_child(cmd: &mut StdCommand) -> io::Result<SpawnedChild> {
69 let mut child = cmd.spawn()?;
70 let stdin = stdio(child.stdin.take());
71 let stdout = stdio(child.stdout.take());
72 let stderr = stdio(child.stderr.take());
73
74 Ok(SpawnedChild {
75 child: Child {
76 child,
77 waiting: None,
78 },
79 stdin,
80 stdout,
81 stderr,
82 })
83 }
84
85 impl Child {
id(&self) -> u3286 pub(crate) fn id(&self) -> u32 {
87 self.child.id()
88 }
89
try_wait(&mut self) -> io::Result<Option<ExitStatus>>90 pub(crate) fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
91 self.child.try_wait()
92 }
93 }
94
95 impl Kill for Child {
kill(&mut self) -> io::Result<()>96 fn kill(&mut self) -> io::Result<()> {
97 self.child.kill()
98 }
99 }
100
101 impl Future for Child {
102 type Output = io::Result<ExitStatus>;
103
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>104 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
105 let inner = Pin::get_mut(self);
106 loop {
107 if let Some(ref mut w) = inner.waiting {
108 match Pin::new(&mut w.rx).poll(cx) {
109 Poll::Ready(Ok(())) => {}
110 Poll::Ready(Err(_)) => panic!("should not be canceled"),
111 Poll::Pending => return Poll::Pending,
112 }
113 let status = inner.try_wait()?.expect("not ready yet");
114 return Poll::Ready(Ok(status));
115 }
116
117 if let Some(e) = inner.try_wait()? {
118 return Poll::Ready(Ok(e));
119 }
120 let (tx, rx) = oneshot::channel();
121 let ptr = Box::into_raw(Box::new(Some(tx)));
122 let mut wait_object = ptr::null_mut();
123 let rc = unsafe {
124 RegisterWaitForSingleObject(
125 &mut wait_object,
126 inner.child.as_raw_handle(),
127 Some(callback),
128 ptr as *mut _,
129 INFINITE,
130 WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE,
131 )
132 };
133 if rc == 0 {
134 let err = io::Error::last_os_error();
135 drop(unsafe { Box::from_raw(ptr) });
136 return Poll::Ready(Err(err));
137 }
138 inner.waiting = Some(Waiting {
139 rx,
140 wait_object,
141 tx: ptr,
142 });
143 }
144 }
145 }
146
147 impl Drop for Waiting {
drop(&mut self)148 fn drop(&mut self) {
149 unsafe {
150 let rc = UnregisterWaitEx(self.wait_object, INVALID_HANDLE_VALUE);
151 if rc == 0 {
152 panic!("failed to unregister: {}", io::Error::last_os_error());
153 }
154 drop(Box::from_raw(self.tx));
155 }
156 }
157 }
158
callback(ptr: PVOID, _timer_fired: BOOLEAN)159 unsafe extern "system" fn callback(ptr: PVOID, _timer_fired: BOOLEAN) {
160 let complete = &mut *(ptr as *mut Option<oneshot::Sender<()>>);
161 let _ = complete.take().unwrap().send(());
162 }
163
164 pub(crate) type ChildStdin = PollEvented<NamedPipe>;
165 pub(crate) type ChildStdout = PollEvented<NamedPipe>;
166 pub(crate) type ChildStderr = PollEvented<NamedPipe>;
167
stdio<T>(option: Option<T>) -> Option<PollEvented<NamedPipe>> where T: IntoRawHandle,168 fn stdio<T>(option: Option<T>) -> Option<PollEvented<NamedPipe>>
169 where
170 T: IntoRawHandle,
171 {
172 let io = match option {
173 Some(io) => io,
174 None => return None,
175 };
176 let pipe = unsafe { NamedPipe::from_raw_handle(io.into_raw_handle()) };
177 PollEvented::new(pipe).ok()
178 }
179
convert_to_stdio(io: PollEvented<NamedPipe>) -> io::Result<Stdio>180 pub(crate) fn convert_to_stdio(io: PollEvented<NamedPipe>) -> io::Result<Stdio> {
181 let named_pipe = io.into_inner()?;
182
183 // Mio does not implement `IntoRawHandle` for `NamedPipe`, so we'll manually
184 // duplicate the handle here...
185 unsafe {
186 let mut dup_handle = INVALID_HANDLE_VALUE;
187 let cur_proc = GetCurrentProcess();
188
189 let status = DuplicateHandle(
190 cur_proc,
191 named_pipe.as_raw_handle(),
192 cur_proc,
193 &mut dup_handle,
194 0 as DWORD,
195 FALSE,
196 DUPLICATE_SAME_ACCESS,
197 );
198
199 if status == 0 {
200 return Err(io::Error::last_os_error());
201 }
202
203 Ok(Stdio::from_raw_handle(dup_handle))
204 }
205 }
206