]> git.proxmox.com Git - rustc.git/blob - vendor/tokio/src/process/windows.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / vendor / tokio / src / process / windows.rs
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, 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 {
51 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
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 {
87 pub(crate) fn id(&self) -> u32 {
88 self.child.id()
89 }
90
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 {
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
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 {
149 fn as_raw_handle(&self) -> RawHandle {
150 self.child.as_raw_handle()
151 }
152 }
153
154 impl Drop for Waiting {
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
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 {
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 {
181 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
182 (&*self.0).write(bytes)
183 }
184
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 AsRawHandle for ChildStdio {
199 fn as_raw_handle(&self) -> RawHandle {
200 self.raw.as_raw_handle()
201 }
202 }
203
204 impl AsyncRead for ChildStdio {
205 fn poll_read(
206 mut self: Pin<&mut Self>,
207 cx: &mut Context<'_>,
208 buf: &mut ReadBuf<'_>,
209 ) -> Poll<io::Result<()>> {
210 Pin::new(&mut self.io).poll_read(cx, buf)
211 }
212 }
213
214 impl AsyncWrite for ChildStdio {
215 fn poll_write(
216 mut self: Pin<&mut Self>,
217 cx: &mut Context<'_>,
218 buf: &[u8],
219 ) -> Poll<io::Result<usize>> {
220 Pin::new(&mut self.io).poll_write(cx, buf)
221 }
222
223 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
224 Pin::new(&mut self.io).poll_flush(cx)
225 }
226
227 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
228 Pin::new(&mut self.io).poll_shutdown(cx)
229 }
230 }
231
232 pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
233 where
234 T: IntoRawHandle,
235 {
236 use std::os::windows::prelude::FromRawHandle;
237
238 let raw = Arc::new(unsafe { StdFile::from_raw_handle(io.into_raw_handle()) });
239 let io = Blocking::new(ArcFile(raw.clone()));
240 Ok(ChildStdio { raw, io })
241 }
242
243 pub(crate) fn convert_to_stdio(child_stdio: ChildStdio) -> io::Result<Stdio> {
244 let ChildStdio { raw, io } = child_stdio;
245 drop(io); // Try to drop the Arc count here
246
247 Arc::try_unwrap(raw)
248 .or_else(|raw| duplicate_handle(&*raw))
249 .map(Stdio::from)
250 }
251
252 fn duplicate_handle<T: AsRawHandle>(io: &T) -> io::Result<StdFile> {
253 use std::os::windows::prelude::FromRawHandle;
254
255 unsafe {
256 let mut dup_handle = INVALID_HANDLE_VALUE;
257 let cur_proc = GetCurrentProcess();
258
259 let status = DuplicateHandle(
260 cur_proc,
261 io.as_raw_handle() as _,
262 cur_proc,
263 &mut dup_handle,
264 0,
265 0,
266 DUPLICATE_SAME_ACCESS,
267 );
268
269 if status == 0 {
270 return Err(io::Error::last_os_error());
271 }
272
273 Ok(StdFile::from_raw_handle(dup_handle as _))
274 }
275 }