• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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