Skip to content

Commit 24888de

Browse files
committed
Auto merge of #5887 - Seeker14491:job-fix, r=alexcrichton
Don't kill child processes on normal exit on Windows Fix for #5598
2 parents ae97799 + 4a7a946 commit 24888de

File tree

1 file changed

+4
-134
lines changed

1 file changed

+4
-134
lines changed

src/cargo/util/job.rs

Lines changed: 4 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,14 @@ mod imp {
4444
mod imp {
4545
extern crate winapi;
4646

47-
use std::ffi::OsString;
4847
use std::io;
4948
use std::mem;
50-
use std::os::windows::prelude::*;
5149
use std::ptr;
5250

53-
use self::winapi::shared::basetsd::*;
5451
use self::winapi::shared::minwindef::*;
55-
use self::winapi::shared::minwindef::{FALSE, TRUE};
5652
use self::winapi::um::handleapi::*;
5753
use self::winapi::um::jobapi2::*;
58-
use self::winapi::um::jobapi::*;
5954
use self::winapi::um::processthreadsapi::*;
60-
use self::winapi::um::psapi::*;
61-
use self::winapi::um::synchapi::*;
62-
use self::winapi::um::winbase::*;
6355
use self::winapi::um::winnt::*;
6456
use self::winapi::um::winnt::HANDLE;
6557

@@ -93,7 +85,7 @@ mod imp {
9385

9486
// Indicate that when all handles to the job object are gone that all
9587
// process in the object should be killed. Note that this includes our
96-
// entire process tree by default because we've added ourselves and and
88+
// entire process tree by default because we've added ourselves and
9789
// our children will reside in the job once we spawn a process.
9890
let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
9991
info = mem::zeroed();
@@ -121,24 +113,10 @@ mod imp {
121113

122114
impl Drop for Setup {
123115
fn drop(&mut self) {
124-
// This is a litte subtle. By default if we are terminated then all
125-
// processes in our job object are terminated as well, but we
126-
// intentionally want to whitelist some processes to outlive our job
127-
// object (see below).
128-
//
129-
// To allow for this, we manually kill processes instead of letting
130-
// the job object kill them for us. We do this in a loop to handle
131-
// processes spawning other processes.
132-
//
133-
// Finally once this is all done we know that the only remaining
134-
// ones are ourselves and the whitelisted processes. The destructor
135-
// here then configures our job object to *not* kill everything on
136-
// close, then closes the job object.
116+
// On normal exits (not ctrl-c), we don't want to kill any child
117+
// processes. The destructor here configures our job object to
118+
// *not* kill everything on close, then closes the job object.
137119
unsafe {
138-
while self.kill_remaining() {
139-
info!("killed some, going for more");
140-
}
141-
142120
let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
143121
info = mem::zeroed();
144122
let r = SetInformationJobObject(
@@ -154,114 +132,6 @@ mod imp {
154132
}
155133
}
156134

157-
impl Setup {
158-
unsafe fn kill_remaining(&mut self) -> bool {
159-
#[repr(C)]
160-
struct Jobs {
161-
header: JOBOBJECT_BASIC_PROCESS_ID_LIST,
162-
list: [ULONG_PTR; 1024],
163-
}
164-
165-
let mut jobs: Jobs = mem::zeroed();
166-
let r = QueryInformationJobObject(
167-
self.job.inner,
168-
JobObjectBasicProcessIdList,
169-
&mut jobs as *mut _ as LPVOID,
170-
mem::size_of_val(&jobs) as DWORD,
171-
ptr::null_mut(),
172-
);
173-
if r == 0 {
174-
info!("failed to query job object: {}", last_err());
175-
return false;
176-
}
177-
178-
let mut killed = false;
179-
let list = &jobs.list[..jobs.header.NumberOfProcessIdsInList as usize];
180-
assert!(!list.is_empty());
181-
info!("found {} remaining processes", list.len() - 1);
182-
183-
let list = list.iter()
184-
.filter(|&&id| {
185-
// let's not kill ourselves
186-
id as DWORD != GetCurrentProcessId()
187-
})
188-
.filter_map(|&id| {
189-
// Open the process with the necessary rights, and if this
190-
// fails then we probably raced with the process exiting so we
191-
// ignore the problem.
192-
let flags = PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE;
193-
let p = OpenProcess(flags, FALSE, id as DWORD);
194-
if p.is_null() {
195-
None
196-
} else {
197-
Some(Handle { inner: p })
198-
}
199-
})
200-
.filter(|p| {
201-
// Test if this process was actually in the job object or not.
202-
// If it's not then we likely raced with something else
203-
// recycling this PID, so we just skip this step.
204-
let mut res = 0;
205-
let r = IsProcessInJob(p.inner, self.job.inner, &mut res);
206-
if r == 0 {
207-
info!("failed to test is process in job: {}", last_err());
208-
return false;
209-
}
210-
res == TRUE
211-
});
212-
213-
for p in list {
214-
// Load the file which this process was spawned from. We then
215-
// later use this for identification purposes.
216-
let mut buf = [0; 1024];
217-
let r = GetProcessImageFileNameW(p.inner, buf.as_mut_ptr(), buf.len() as DWORD);
218-
if r == 0 {
219-
info!("failed to get image name: {}", last_err());
220-
continue;
221-
}
222-
let s = OsString::from_wide(&buf[..r as usize]);
223-
info!("found remaining: {:?}", s);
224-
225-
// And here's where we find the whole purpose for this
226-
// function! Currently, our only whitelisted process is
227-
// `mspdbsrv.exe`, and more details about that can be found
228-
// here:
229-
//
230-
// https://github.com/rust-lang/rust/issues/33145
231-
//
232-
// The gist of it is that all builds on one machine use the
233-
// same `mspdbsrv.exe` instance. If we were to kill this
234-
// instance then we could erroneously cause other builds to
235-
// fail.
236-
if let Some(s) = s.to_str() {
237-
if s.contains("mspdbsrv") {
238-
info!("\toops, this is mspdbsrv");
239-
continue;
240-
}
241-
}
242-
243-
// Ok, this isn't mspdbsrv, let's kill the process. After we
244-
// kill it we wait on it to ensure that the next time around in
245-
// this function we're not going to see it again.
246-
let r = TerminateProcess(p.inner, 1);
247-
if r == 0 {
248-
info!("\tfailed to kill subprocess: {}", last_err());
249-
info!("\tassuming subprocess is dead...");
250-
} else {
251-
info!("\tterminated subprocess");
252-
}
253-
let r = WaitForSingleObject(p.inner, INFINITE);
254-
if r != 0 {
255-
info!("failed to wait for process to die: {}", last_err());
256-
return false;
257-
}
258-
killed = true;
259-
}
260-
261-
killed
262-
}
263-
}
264-
265135
impl Drop for Handle {
266136
fn drop(&mut self) {
267137
unsafe {

0 commit comments

Comments
 (0)