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)]
36pub struct JobObject;
37
38impl StdCommandWrapper for JobObject {
39 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
40 fn pre_spawn(&mut self, command: &mut Command, core: &StdCommandWrap) -> Result<()> {
41 let mut flags = CREATE_SUSPENDED;
42 #[cfg(feature = "creation-flags")]
43 if let Some(CreationFlags(user_flags)) = core.get_wrap::<CreationFlags>() {
44 flags |= *user_flags;
45 }
46
47 command.creation_flags(flags.0);
48 Ok(())
49 }
50
51 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
52 fn wrap_child(
53 &mut self,
54 inner: Box<dyn StdChildWrapper>,
55 core: &StdCommandWrap,
56 ) -> Result<Box<dyn StdChildWrapper>> {
57 #[cfg(feature = "creation-flags")]
58 let create_suspended = core
59 .get_wrap::<CreationFlags>()
60 .map_or(false, |flags| flags.0.contains(CREATE_SUSPENDED));
61 #[cfg(not(feature = "creation-flags"))]
62 let create_suspended = false;
63
64 debug!(?create_suspended, "options from other wrappers");
65
66 let handle = HANDLE(inner.inner().as_raw_handle() as _);
67
68 let job_port = make_job_object(handle, false)?;
69
70 if !create_suspended {
72 resume_threads(handle)?;
73 }
74
75 Ok(Box::new(JobObjectChild::new(inner, job_port)))
76 }
77}
78
79#[derive(Debug)]
81pub struct JobObjectChild {
82 inner: Box<dyn StdChildWrapper>,
83 exit_status: ChildExitStatus,
84 job_port: JobPort,
85}
86
87impl JobObjectChild {
88 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(job_port)))]
89 pub(crate) fn new(inner: Box<dyn StdChildWrapper>, job_port: JobPort) -> Self {
90 Self {
91 inner,
92 exit_status: ChildExitStatus::Running,
93 job_port,
94 }
95 }
96}
97
98impl StdChildWrapper for JobObjectChild {
99 fn inner(&self) -> &Child {
100 self.inner.inner()
101 }
102 fn inner_mut(&mut self) -> &mut Child {
103 self.inner.inner_mut()
104 }
105 fn into_inner(self: Box<Self>) -> Child {
106 let its = std::mem::ManuallyDrop::new(self.job_port);
108 unsafe { CloseHandle(its.completion_port.0) }.ok();
109 self.inner.into_inner()
113 }
114
115 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
116 fn start_kill(&mut self) -> Result<()> {
117 terminate_job(self.job_port.job, 1)
118 }
119
120 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
121 fn wait(&mut self) -> Result<ExitStatus> {
122 if let ChildExitStatus::Exited(status) = &self.exit_status {
123 return Ok(*status);
124 }
125
126 let status = self.inner.wait()?;
129 self.exit_status = ChildExitStatus::Exited(status);
130
131 let JobPort {
133 completion_port, ..
134 } = self.job_port;
135 wait_on_job(completion_port, None)?;
136 Ok(status)
137 }
138
139 #[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
140 fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
141 wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?;
142 self.inner.try_wait()
143 }
144}