1 //! Windows asynchronous process handling.
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.
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`.
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
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
;
24 use std
::fs
::File
as StdFile
;
25 use std
::future
::Future
;
27 use std
::os
::windows
::prelude
::{AsRawHandle, IntoRawHandle, RawHandle}
;
29 use std
::process
::Stdio
;
30 use std
::process
::{Child as StdChild, Command as StdCommand, ExitStatus}
;
32 use std
::task
::{Context, Poll}
;
36 DuplicateHandle
, BOOLEAN
, DUPLICATE_SAME_ACCESS
, HANDLE
, INVALID_HANDLE_VALUE
,
38 Win32
::System
::Threading
::{
39 GetCurrentProcess
, RegisterWaitForSingleObject
, UnregisterWaitEx
, INFINITE
,
40 WT_EXECUTEINWAITTHREAD
, WT_EXECUTEONLYONCE
,
44 #[must_use = "futures do nothing unless polled"]
45 pub(crate) struct Child
{
47 waiting
: Option
<Waiting
>,
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", &"..")
61 rx
: oneshot
::Receiver
<()>,
63 tx
: *mut Option
<oneshot
::Sender
<()>>,
66 unsafe impl Sync
for Waiting {}
67 unsafe impl Send
for Waiting {}
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()?
;
87 pub(crate) fn id(&self) -> u32 {
91 pub(crate) fn try_wait(&mut self) -> io
::Result
<Option
<ExitStatus
>> {
97 fn kill(&mut self) -> io
::Result
<()> {
102 impl Future
for Child
{
103 type Output
= io
::Result
<ExitStatus
>;
105 fn poll(self: Pin
<&mut Self>, cx
: &mut Context
<'_
>) -> Poll
<Self::Output
> {
106 let inner
= Pin
::get_mut(self);
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
,
114 let status
= inner
.try_wait()?
.expect("not ready yet");
115 return Poll
::Ready(Ok(status
));
118 if let Some(e
) = inner
.try_wait()?
{
119 return Poll
::Ready(Ok(e
));
121 let (tx
, rx
) = oneshot
::channel();
122 let ptr
= Box
::into_raw(Box
::new(Some(tx
)));
123 let mut wait_object
= 0;
125 RegisterWaitForSingleObject(
127 inner
.child
.as_raw_handle() as _
,
131 WT_EXECUTEINWAITTHREAD
| WT_EXECUTEONLYONCE
,
135 let err
= io
::Error
::last_os_error();
136 drop(unsafe { Box::from_raw(ptr) }
);
137 return Poll
::Ready(Err(err
));
139 inner
.waiting
= Some(Waiting
{
148 impl AsRawHandle
for Child
{
149 fn as_raw_handle(&self) -> RawHandle
{
150 self.child
.as_raw_handle()
154 impl Drop
for Waiting
{
157 let rc
= UnregisterWaitEx(self.wait_object
, INVALID_HANDLE_VALUE
);
159 panic
!("failed to unregister: {}", io
::Error
::last_os_error());
161 drop(Box
::from_raw(self.tx
));
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(());
172 struct ArcFile(Arc
<StdFile
>);
174 impl io
::Read
for ArcFile
{
175 fn read(&mut self, bytes
: &mut [u8]) -> io
::Result
<usize> {
176 (&*self.0).read(bytes
)
180 impl io
::Write
for ArcFile
{
181 fn write(&mut self, bytes
: &[u8]) -> io
::Result
<usize> {
182 (&*self.0).write(bytes
)
185 fn flush(&mut self) -> io
::Result
<()> {
191 pub(crate) struct ChildStdio
{
192 // Used for accessing the raw handle, even if the io version is busy
194 // For doing I/O operations asynchronously
195 io
: Blocking
<ArcFile
>,
198 impl AsRawHandle
for ChildStdio
{
199 fn as_raw_handle(&self) -> RawHandle
{
200 self.raw
.as_raw_handle()
204 impl AsyncRead
for ChildStdio
{
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
)
214 impl AsyncWrite
for ChildStdio
{
216 mut self: Pin
<&mut Self>,
217 cx
: &mut Context
<'_
>,
219 ) -> Poll
<io
::Result
<usize>> {
220 Pin
::new(&mut self.io
).poll_write(cx
, buf
)
223 fn poll_flush(mut self: Pin
<&mut Self>, cx
: &mut Context
<'_
>) -> Poll
<io
::Result
<()>> {
224 Pin
::new(&mut self.io
).poll_flush(cx
)
227 fn poll_shutdown(mut self: Pin
<&mut Self>, cx
: &mut Context
<'_
>) -> Poll
<io
::Result
<()>> {
228 Pin
::new(&mut self.io
).poll_shutdown(cx
)
232 pub(super) fn stdio
<T
>(io
: T
) -> io
::Result
<ChildStdio
>
236 use std
::os
::windows
::prelude
::FromRawHandle
;
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 }
)
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
248 .or_else(|raw
| duplicate_handle(&*raw
))
252 fn duplicate_handle
<T
: AsRawHandle
>(io
: &T
) -> io
::Result
<StdFile
> {
253 use std
::os
::windows
::prelude
::FromRawHandle
;
256 let mut dup_handle
= INVALID_HANDLE_VALUE
;
257 let cur_proc
= GetCurrentProcess();
259 let status
= DuplicateHandle(
261 io
.as_raw_handle() as _
,
266 DUPLICATE_SAME_ACCESS
,
270 return Err(io
::Error
::last_os_error());
273 Ok(StdFile
::from_raw_handle(dup_handle
as _
))