Rollup merge of #96725 - nico-abram:win_tid, r=ChrisDenton

Expose process windows_process_extensions_main_thread_handle on Windows

~~I did not find any tests in 7d3e03666a/library/std/src/sys/windows/process/tests.rs that actually launch processes, so I haven't added tests for this.~~ I ran the following locally, to check that it works as expected:
```rs
#![feature(windows_process_extensions_main_thread_handle)]

fn main() {
    use std::os::windows::process::{ChildExt, CommandExt};
    const CREATE_SUSPENDED: u32 = 0x00000004;

    let proc = std::process::Command::new("cmd")
        .args(["/C", "echo hello"])
        .creation_flags(CREATE_SUSPENDED)
        .spawn()
        .unwrap();

    extern "system" {
        fn ResumeThread(_: *mut std::ffi::c_void) -> u32;
    }
    unsafe {
        ResumeThread(proc.main_thread_handle());
    }

    let output = proc.wait_with_output().unwrap();
    let str_output = std::str::from_utf8(&output.stdout[..]).unwrap();
    println!("{}", str_output);
}

```

Without the feature attribute it wouldn't compile, and commenting the `ResumeThread` line makes it hang forever, showing that it works.

Trakcing issue https://github.com/rust-lang/rust/issues/96723
This commit is contained in:
Yuki Okushi 2022-05-11 00:09:32 +09:00 committed by GitHub
commit f689f6582c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 7 deletions

View File

@ -180,3 +180,17 @@ fn raw_arg<S: AsRef<OsStr>>(&mut self, raw_text: S) -> &mut process::Command {
self self
} }
} }
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
pub trait ChildExt: Sealed {
/// Extracts the main thread raw handle, without taking ownership
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
fn main_thread_handle(&self) -> BorrowedHandle<'_>;
}
#[unstable(feature = "windows_process_extensions_main_thread_handle", issue = "96723")]
impl ChildExt for process::Child {
fn main_thread_handle(&self) -> BorrowedHandle<'_> {
self.handle.main_thread_handle()
}
}

View File

@ -14,7 +14,7 @@
use crate::mem; use crate::mem;
use crate::num::NonZeroI32; use crate::num::NonZeroI32;
use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::ffi::{OsStrExt, OsStringExt};
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle};
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::ptr; use crate::ptr;
use crate::sys::args::{self, Arg}; use crate::sys::args::{self, Arg};
@ -334,13 +334,14 @@ pub fn spawn(
)) ))
}?; }?;
// We close the thread handle because we don't care about keeping
// the thread id valid, and we aren't keeping the thread handle
// around to be able to close it later.
unsafe { unsafe {
drop(Handle::from_raw_handle(pi.hThread)); Ok((
Process {
Ok((Process { handle: Handle::from_raw_handle(pi.hProcess) }, pipes)) handle: Handle::from_raw_handle(pi.hProcess),
main_thread_handle: Handle::from_raw_handle(pi.hThread),
},
pipes,
))
} }
} }
} }
@ -609,6 +610,7 @@ fn from(file: File) -> Stdio {
/// for the process to terminate. /// for the process to terminate.
pub struct Process { pub struct Process {
handle: Handle, handle: Handle,
main_thread_handle: Handle,
} }
impl Process { impl Process {
@ -621,6 +623,10 @@ pub fn id(&self) -> u32 {
unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 } unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 }
} }
pub fn main_thread_handle(&self) -> BorrowedHandle<'_> {
self.main_thread_handle.as_handle()
}
pub fn wait(&mut self) -> io::Result<ExitStatus> { pub fn wait(&mut self) -> io::Result<ExitStatus> {
unsafe { unsafe {
let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE); let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE);

View File

@ -24,6 +24,31 @@ fn test_raw_args() {
); );
} }
#[test]
fn test_thread_handle() {
use crate::os::windows::io::BorrowedHandle;
use crate::os::windows::process::{ChildExt, CommandExt};
const CREATE_SUSPENDED: u32 = 0x00000004;
let p = Command::new("cmd").args(&["/C", "exit 0"]).creation_flags(CREATE_SUSPENDED).spawn();
assert!(p.is_ok());
let mut p = p.unwrap();
extern "system" {
fn ResumeThread(_: BorrowedHandle<'_>) -> u32;
}
unsafe {
ResumeThread(p.main_thread_handle());
}
crate::thread::sleep(crate::time::Duration::from_millis(100));
let res = p.try_wait();
assert!(res.is_ok());
assert!(res.unwrap().is_some());
assert!(p.try_wait().unwrap().unwrap().success());
}
#[test] #[test]
fn test_make_command_line() { fn test_make_command_line() {
fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String { fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {