Skip to content

Commit b686f35

Browse files
committed
Add job work (ends up a PR merged upstream that enables this rust-lang/rust#114848
Signed-off-by: James Sturtevant <[email protected]>
1 parent 9ed3160 commit b686f35

File tree

10 files changed

+353
-14
lines changed

10 files changed

+353
-14
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ windows-sys = { version = "0.48.0", features = [
1818
"Win32_Security_Cryptography",
1919
"Win32_System_Console",
2020
"Win32_System_WindowsProgramming",
21-
2221
]}
2322
cvt = "0.1.2"
2423
libc = "0.2.147"
24+
25+
[dev-dependencies]
26+
windows = { version="0.51", features = ["Win32_Foundation", "Win32_Security", "Win32_System_JobObjects"] }

src/args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
66
use crate::c;
77
use crate::path_ext::get_long_path;
8-
use crate::windows::{ensure_no_nuls, to_u16s};
8+
use crate::util::{ensure_no_nuls, to_u16s};
99

1010
use std::ffi::OsString;
1111
use std::fmt;
@@ -177,7 +177,7 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
177177
from_wide_to_user_path(to_u16s(path)?)
178178
}
179179
pub(crate) fn from_wide_to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
180-
use crate::windows::fill_utf16_buf;
180+
use crate::util::fill_utf16_buf;
181181
use std::ptr;
182182

183183
// UTF-16 encoded code points, used in parsing and building UTF-16 paths.

src/c.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub use windows_sys::Win32::System::Threading::TerminateProcess;
3434
pub use windows_sys::Win32::System::Threading::{
3535
CreateEventW, GetCurrentProcessId, GetExitCodeProcess, GetProcessId, SleepEx,
3636
WaitForSingleObject, CREATE_NEW_PROCESS_GROUP, CREATE_UNICODE_ENVIRONMENT, DETACHED_PROCESS,
37-
INFINITE, STARTF_USESTDHANDLES,
37+
INFINITE, STARTF_USESTDHANDLES, EXTENDED_STARTUPINFO_PRESENT,
3838
};
3939
use windows_sys::Win32::System::Threading::{PROCESS_CREATION_FLAGS, STARTUPINFOW_FLAGS};
4040
pub use windows_sys::Win32::System::IO::CancelIo;
@@ -382,6 +382,19 @@ impl ::core::clone::Clone for STARTUPINFOW {
382382
*self
383383
}
384384
}
385+
pub type LPPROC_THREAD_ATTRIBUTE_LIST = *mut ::core::ffi::c_void;
386+
387+
#[repr(C)]
388+
pub struct STARTUPINFOEXW {
389+
pub StartupInfo: STARTUPINFOW,
390+
pub lpAttributeList: LPPROC_THREAD_ATTRIBUTE_LIST,
391+
}
392+
impl ::core::marker::Copy for STARTUPINFOEXW {}
393+
impl ::core::clone::Clone for STARTUPINFOEXW {
394+
fn clone(&self) -> Self {
395+
*self
396+
}
397+
}
385398

386399
#[repr(C)]
387400
pub struct PROCESS_INFORMATION {
@@ -396,3 +409,35 @@ impl ::core::clone::Clone for PROCESS_INFORMATION {
396409
*self
397410
}
398411
}
412+
413+
#[link(name = "kernel32")]
414+
extern "system" {
415+
pub fn InitializeProcThreadAttributeList(
416+
lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST,
417+
dwattributecount: u32,
418+
dwflags: u32,
419+
lpsize: *mut usize,
420+
) -> BOOL;
421+
}
422+
423+
#[link(name = "kernel32")]
424+
extern "system" {
425+
pub fn UpdateProcThreadAttribute(
426+
lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST,
427+
dwflags: u32,
428+
attribute: usize,
429+
lpvalue: *const ::core::ffi::c_void,
430+
cbsize: usize,
431+
lppreviousvalue: *mut ::core::ffi::c_void,
432+
lpreturnsize: *const usize,
433+
) -> BOOL;
434+
}
435+
#[link(name = "kernel32")]
436+
extern "system" {
437+
pub fn DeleteProcThreadAttributeList(lpattributelist: LPPROC_THREAD_ATTRIBUTE_LIST) -> ();
438+
}
439+
440+
#[link(name = "kernel32")]
441+
extern "system" {
442+
pub fn CloseHandle(hobject: HANDLE) -> BOOL;
443+
}

src/child.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ pub struct Child {
2626
pub(crate) main_thread_handle: Handle,
2727
}
2828

29+
impl AsRawHandle for Child {
30+
fn as_raw_handle(&self) -> c::HANDLE {
31+
self.handle.as_raw_handle()
32+
}
33+
}
34+
2935
impl Child {
3036
pub fn kill(&mut self) -> io::Result<()> {
3137
let result = unsafe { c::TerminateProcess(self.handle.as_raw_handle() as isize, 1) };
@@ -118,3 +124,14 @@ pub fn wait_with_output(
118124
let status = process.wait()?;
119125
Ok((status, stdout, stderr))
120126
}
127+
128+
pub trait ChildExt {
129+
fn main_thread_handle(&self) -> BorrowedHandle<'_>;
130+
}
131+
132+
133+
impl ChildExt for Child {
134+
fn main_thread_handle(&self) -> BorrowedHandle<'_> {
135+
self.main_thread_handle.as_handle()
136+
}
137+
}

src/command.rs

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ use crate::file::{open, OpenOptions};
66
use crate::handle::Handle;
77
use crate::path_ext;
88
use crate::pipe::{self, AnonPipe};
9-
use crate::{c, windows};
9+
use crate::{c, util};
1010
use cvt::cvt;
1111
use std::collections::BTreeMap;
1212
use std::env::consts::{EXE_EXTENSION, EXE_SUFFIX};
1313
use std::ffi::{c_void, OsStr, OsString};
1414
use std::fs::File;
15+
use std::io::ErrorKind;
16+
use std::mem::MaybeUninit;
1517
use std::os::windows::prelude::{
1618
AsRawHandle, FromRawHandle, IntoRawHandle, OsStrExt, OsStringExt, RawHandle,
1719
};
@@ -45,6 +47,7 @@ pub struct Command {
4547
stdout: Option<Stdio>,
4648
stderr: Option<Stdio>,
4749
force_quotes_enabled: bool,
50+
proc_thread_attributes: BTreeMap<usize, ProcThreadAttributeValue>,
4851
}
4952

5053
impl Command {
@@ -60,6 +63,7 @@ impl Command {
6063
stdout: None,
6164
stderr: None,
6265
force_quotes_enabled: false,
66+
proc_thread_attributes: Default::default(),
6367
}
6468
}
6569

@@ -115,6 +119,17 @@ impl Command {
115119
// self.env.iter()
116120
// }
117121

122+
pub unsafe fn raw_attribute<T: Copy + Send + Sync + 'static>(
123+
&mut self,
124+
attribute: usize,
125+
value: T,
126+
) {
127+
self.proc_thread_attributes.insert(
128+
attribute,
129+
ProcThreadAttributeValue { size: mem::size_of::<T>(), data: Box::new(value) },
130+
);
131+
}
132+
118133
pub fn get_current_dir(&self) -> Option<&Path> {
119134
self.cwd.as_ref().map(Path::new)
120135
}
@@ -191,7 +206,6 @@ impl Command {
191206
let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
192207

193208
let mut si = zeroed_startupinfo();
194-
si.cb = mem::size_of::<c::STARTUPINFOW>() as c::DWORD;
195209

196210
// If at least one of stdin, stdout or stderr are set (i.e. are non null)
197211
// then set the `hStd` fields in `STARTUPINFO`.
@@ -205,6 +219,27 @@ impl Command {
205219
si.hStdError = stderr.as_raw_handle();
206220
}
207221

222+
let si_ptr: *mut c::STARTUPINFOW;
223+
224+
let mut proc_thread_attribute_list;
225+
let mut si_ex;
226+
227+
if !self.proc_thread_attributes.is_empty() {
228+
si.cb = mem::size_of::<c::STARTUPINFOEXW>() as u32;
229+
flags |= c::EXTENDED_STARTUPINFO_PRESENT;
230+
231+
proc_thread_attribute_list =
232+
make_proc_thread_attribute_list(&self.proc_thread_attributes)?;
233+
si_ex = c::STARTUPINFOEXW {
234+
StartupInfo: si,
235+
lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _,
236+
};
237+
si_ptr = &mut si_ex as *mut _ as _;
238+
} else {
239+
si.cb = mem::size_of::<c::STARTUPINFOW> as c::DWORD;
240+
si_ptr = &mut si as *mut _ as _;
241+
}
242+
208243
unsafe {
209244
cvt(c::CreateProcessW(
210245
program.as_ptr(),
@@ -215,7 +250,7 @@ impl Command {
215250
flags,
216251
envp,
217252
dirp,
218-
&si,
253+
si_ptr,
219254
&mut pi,
220255
))
221256
}?;
@@ -339,7 +374,7 @@ pub struct CommandArgs<'a> {
339374

340375
// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
341376
fn command_prompt() -> io::Result<Vec<u16>> {
342-
let mut system: Vec<u16> = windows::fill_utf16_buf(
377+
let mut system: Vec<u16> = util::fill_utf16_buf(
343378
|buf, size| unsafe { c::GetSystemDirectoryW(buf, size) },
344379
|buf| buf.into(),
345380
)?;
@@ -557,15 +592,15 @@ where
557592
// 3 & 4. System paths
558593
// SAFETY: This uses `fill_utf16_buf` to safely call the OS functions.
559594
unsafe {
560-
if let Ok(Some(path)) = windows::fill_utf16_buf(
595+
if let Ok(Some(path)) = util::fill_utf16_buf(
561596
|buf, size| c::GetSystemDirectoryW(buf, size),
562597
|buf| exists(PathBuf::from(OsString::from_wide(buf))),
563598
) {
564599
return Some(path);
565600
}
566601
#[cfg(not(target_vendor = "uwp"))]
567602
{
568-
if let Ok(Some(path)) = windows::fill_utf16_buf(
603+
if let Ok(Some(path)) = util::fill_utf16_buf(
569604
|buf, size| c::GetWindowsDirectoryW(buf, size),
570605
|buf| exists(PathBuf::from(OsString::from_wide(buf))),
571606
) {
@@ -600,3 +635,79 @@ fn program_exists(path: &Path) -> Option<Vec<u16>> {
600635
}
601636
}
602637
}
638+
639+
640+
struct ProcThreadAttributeList(Box<[MaybeUninit<u8>]>);
641+
642+
impl Drop for ProcThreadAttributeList {
643+
fn drop(&mut self) {
644+
let lp_attribute_list = self.0.as_mut_ptr() as _;
645+
unsafe { c::DeleteProcThreadAttributeList(lp_attribute_list) }
646+
}
647+
}
648+
649+
/// Wrapper around the value data to be used as a Process Thread Attribute.
650+
struct ProcThreadAttributeValue {
651+
data: Box<dyn Send + Sync>,
652+
size: usize,
653+
}
654+
655+
fn make_proc_thread_attribute_list(
656+
attributes: &BTreeMap<usize, ProcThreadAttributeValue>,
657+
) -> io::Result<ProcThreadAttributeList> {
658+
// To initialize our ProcThreadAttributeList, we need to determine
659+
// how many bytes to allocate for it. The Windows API simplifies this process
660+
// by allowing us to call `InitializeProcThreadAttributeList` with
661+
// a null pointer to retrieve the required size.
662+
let mut required_size = 0;
663+
let Ok(attribute_count) = attributes.len().try_into() else {
664+
return Err(io::Error::new(
665+
ErrorKind::InvalidInput,
666+
"maximum number of ProcThreadAttributes exceeded",
667+
));
668+
};
669+
unsafe {
670+
c::InitializeProcThreadAttributeList(
671+
ptr::null_mut(),
672+
attribute_count,
673+
0,
674+
&mut required_size,
675+
)
676+
};
677+
678+
let mut proc_thread_attribute_list = ProcThreadAttributeList(
679+
vec![MaybeUninit::uninit(); required_size as usize].into_boxed_slice(),
680+
);
681+
682+
// Once we've allocated the necessary memory, it's safe to invoke
683+
// `InitializeProcThreadAttributeList` to properly initialize the list.
684+
cvt(unsafe {
685+
c::InitializeProcThreadAttributeList(
686+
proc_thread_attribute_list.0.as_mut_ptr() as *mut _,
687+
attribute_count,
688+
0,
689+
&mut required_size,
690+
)
691+
})?;
692+
693+
// # Add our attributes to the buffer.
694+
// It's theoretically possible for the attribute count to exceed a u32 value.
695+
// Therefore, we ensure that we don't add more attributes than the buffer was initialized for.
696+
for (&attribute, value) in attributes.iter().take(attribute_count as usize) {
697+
let value_ptr = &*value.data as *const (dyn Send + Sync) as _;
698+
cvt(unsafe {
699+
c::UpdateProcThreadAttribute(
700+
proc_thread_attribute_list.0.as_mut_ptr() as _,
701+
0,
702+
attribute,
703+
value_ptr,
704+
value.size,
705+
ptr::null_mut(),
706+
ptr::null_mut(),
707+
)
708+
})?;
709+
}
710+
711+
Ok(proc_thread_attribute_list)
712+
}
713+

0 commit comments

Comments
 (0)