process_wrap/std/
job_object.rs1use std::{
2 io::Result,
3 os::windows::{io::AsRawHandle, process::CommandExt},
4 process::{Child, Command, ExitStatus},
5 time::Duration,
6};
7
8#[cfg(feature = "tracing")]
9use tracing::{debug, instrument};
10use windows::Win32::{
11 Foundation::{CloseHandle, HANDLE},
12 System::Threading::CREATE_SUSPENDED,
13};
14
15use crate::{
16 windows::{make_job_object, resume_threads, terminate_job, wait_on_job, JobPort},
17 ChildExitStatus,
18};
19
20#[cfg(feature = "creation-flags")]
21use super::CreationFlags;
22use super::{StdChildWrapper, StdCommandWrap, StdCommandWrapper};
23
24#[derive(Clone, Copy, Debug)]
34pub struct JobObject;
35
36impl StdCommandWrapper for JobObject {
37 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
38 fn pre_spawn(&mut self, command: &mut Command, core: &StdCommandWrap) -> Result<()> {
39 let mut flags = CREATE_SUSPENDED;
40 #[cfg(feature = "creation-flags")]
41 if let Some(CreationFlags(user_flags)) = core.get_wrap::<CreationFlags>() {
42 flags |= *user_flags;
43 }
44
45 command.creation_flags(flags.0);
46 Ok(())
47 }
48
49 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
50 fn wrap_child(
51 &mut self,
52 inner: Box<dyn StdChildWrapper>,
53 core: &StdCommandWrap,
54 ) -> Result<Box<dyn StdChildWrapper>> {
55 #[cfg(feature = "creation-flags")]
56 let create_suspended = core
57 .get_wrap::<CreationFlags>()
58 .map_or(false, |flags| flags.0.contains(CREATE_SUSPENDED));
59 #[cfg(not(feature = "creation-flags"))]
60 let create_suspended = false;
61
62 debug!(?create_suspended, "options from other wrappers");
63
64 let handle = HANDLE(inner.inner().as_raw_handle() as _);
65
66 let job_port = make_job_object(handle, false)?;
67
68 if !create_suspended {
70 resume_threads(handle)?;
71 }
72
73 Ok(Box::new(JobObjectChild::new(inner, job_port)))
74 }
75}
76
77#[derive(Debug)]
79pub struct JobObjectChild {
80 inner: Box<dyn StdChildWrapper>,
81 exit_status: ChildExitStatus,
82 job_port: JobPort,
83}
84
85impl JobObjectChild {
86 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(job_port)))]
87 pub(crate) fn new(inner: Box<dyn StdChildWrapper>, job_port: JobPort) -> Self {
88 Self {
89 inner,
90 exit_status: ChildExitStatus::Running,
91 job_port,
92 }
93 }
94}
95
96impl StdChildWrapper for JobObjectChild {
97 fn inner(&self) -> &Child {
98 self.inner.inner()
99 }
100 fn inner_mut(&mut self) -> &mut Child {
101 self.inner.inner_mut()
102 }
103 fn into_inner(self: Box<Self>) -> Child {
104 let its = std::mem::ManuallyDrop::new(self.job_port);
106 unsafe { CloseHandle(its.completion_port.0) }.ok();
107 self.inner.into_inner()
111 }
112
113 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
114 fn start_kill(&mut self) -> Result<()> {
115 terminate_job(self.job_port.job, 1)
116 }
117
118 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
119 fn wait(&mut self) -> Result<ExitStatus> {
120 if let ChildExitStatus::Exited(status) = &self.exit_status {
121 return Ok(*status);
122 }
123
124 let status = self.inner.wait()?;
127 self.exit_status = ChildExitStatus::Exited(status);
128
129 let JobPort {
131 completion_port, ..
132 } = self.job_port;
133 wait_on_job(completion_port, None)?;
134 Ok(status)
135 }
136
137 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
138 fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
139 wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?;
140 self.inner.try_wait()
141 }
142}