]> git.proxmox.com Git - rustc.git/blame - src/bootstrap/job.rs
New upstream version 1.42.0+dfsg1
[rustc.git] / src / bootstrap / job.rs
CommitLineData
7453a54e
SL
1//! Job management on Windows for bootstrapping
2//!
0731742a 3//! Most of the time when you're running a build system (e.g., make) you expect
7453a54e
SL
4//! Ctrl-C or abnormal termination to actually terminate the entire tree of
5//! process in play, not just the one at the top. This currently works "by
6//! default" on Unix platforms because Ctrl-C actually sends a signal to the
7//! *process group* rather than the parent process, so everything will get torn
8//! down. On Windows, however, this does not happen and Ctrl-C just kills the
9//! parent process.
10//!
11//! To achieve the same semantics on Windows we use Job Objects to ensure that
12//! all processes die at the same time. Job objects have a mode of operation
13//! where when all handles to the object are closed it causes all child
14//! processes associated with the object to be terminated immediately.
15//! Conveniently whenever a process in the job object spawns a new process the
16//! child will be associated with the job object as well. This means if we add
17//! ourselves to the job object we create then everything will get torn down!
18//!
19//! Unfortunately most of the time the build system is actually called from a
20//! python wrapper (which manages things like building the build system) so this
21//! all doesn't quite cut it so far. To go the last mile we duplicate the job
22//! object handle into our parent process (a python process probably) and then
23//! close our own handle. This means that the only handle to the job object
24//! resides in the parent python process, so when python dies the whole build
25//! system dies (as one would probably expect!).
26//!
27//! Note that this module has a #[cfg(windows)] above it as none of this logic
28//! is required on Unix.
29
b7449926 30#![allow(nonstandard_style, dead_code)]
7453a54e 31
dfeec247 32use crate::Build;
7453a54e
SL
33use std::env;
34use std::io;
35use std::mem;
dc9dc135 36use std::ptr;
476ff2be 37
dfeec247
XL
38use winapi::shared::minwindef::{DWORD, FALSE, LPVOID};
39use winapi::um::errhandlingapi::SetErrorMode;
40use winapi::um::handleapi::{CloseHandle, DuplicateHandle};
41use winapi::um::jobapi2::{AssignProcessToJobObject, CreateJobObjectW, SetInformationJobObject};
42use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcess};
43use winapi::um::winbase::{BELOW_NORMAL_PRIORITY_CLASS, SEM_NOGPFAULTERRORBOX};
44use winapi::um::winnt::{
45 JobObjectExtendedLimitInformation, DUPLICATE_SAME_ACCESS, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
46 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOB_OBJECT_LIMIT_PRIORITY_CLASS, PROCESS_DUP_HANDLE,
47};
7453a54e 48
7cac9316 49pub unsafe fn setup(build: &mut Build) {
83c7162d
XL
50 // Enable the Windows Error Reporting dialog which msys disables,
51 // so we can JIT debug rustc
52 let mode = SetErrorMode(0);
53 SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
476ff2be 54
7453a54e 55 // Create a new job object for us to use
dc9dc135
XL
56 let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
57 assert!(!job.is_null(), "{}", io::Error::last_os_error());
7453a54e
SL
58
59 // Indicate that when all handles to the job object are gone that all
60 // process in the object should be killed. Note that this includes our
a7813a04 61 // entire process tree by default because we've added ourselves and our
7453a54e
SL
62 // children will reside in the job by default.
63 let mut info = mem::zeroed::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
64 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
7cac9316
XL
65 if build.config.low_priority {
66 info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_PRIORITY_CLASS;
67 info.BasicLimitInformation.PriorityClass = BELOW_NORMAL_PRIORITY_CLASS;
68 }
dfeec247
XL
69 let r = SetInformationJobObject(
70 job,
71 JobObjectExtendedLimitInformation,
72 &mut info as *mut _ as LPVOID,
73 mem::size_of_val(&info) as DWORD,
74 );
7453a54e
SL
75 assert!(r != 0, "{}", io::Error::last_os_error());
76
77 // Assign our process to this job object. Note that if this fails, one very
78 // likely reason is that we are ourselves already in a job object! This can
79 // happen on the build bots that we've got for Windows, or if just anyone
80 // else is instrumenting the build. In this case we just bail out
81 // immediately and assume that they take care of it.
82 //
83 // Also note that nested jobs (why this might fail) are supported in recent
84 // versions of Windows, but the version of Windows that our bots are running
85 // at least don't support nested job objects.
86 let r = AssignProcessToJobObject(job, GetCurrentProcess());
87 if r == 0 {
88 CloseHandle(job);
dfeec247 89 return;
7453a54e
SL
90 }
91
0731742a 92 // If we've got a parent process (e.g., the python script that called us)
7453a54e 93 // then move ownership of this job object up to them. That way if the python
0731742a 94 // script is killed (e.g., via ctrl-c) then we'll all be torn down.
7453a54e 95 //
0731742a 96 // If we don't have a parent (e.g., this was run directly) then we
7453a54e
SL
97 // intentionally leak the job object handle. When our process exits
98 // (normally or abnormally) it will close the handle implicitly, causing all
99 // processes in the job to be cleaned up.
100 let pid = match env::var("BOOTSTRAP_PARENT_ID") {
101 Ok(s) => s,
102 Err(..) => return,
103 };
104
105 let parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid.parse().unwrap());
dc9dc135
XL
106 assert!(!parent.is_null(), "{}", io::Error::last_os_error());
107 let mut parent_handle = ptr::null_mut();
dfeec247
XL
108 let r = DuplicateHandle(
109 GetCurrentProcess(),
110 job,
111 parent,
112 &mut parent_handle,
113 0,
114 FALSE,
115 DUPLICATE_SAME_ACCESS,
116 );
7453a54e
SL
117
118 // If this failed, well at least we tried! An example of DuplicateHandle
ff7c6d11 119 // failing in the past has been when the wrong python2 package spawned this
0731742a 120 // build system (e.g., the `python2` package in MSYS instead of
7453a54e
SL
121 // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
122 // mode" here is that we only clean everything up when the build system
123 // dies, not when the python parent does, so not too bad.
124 if r != 0 {
125 CloseHandle(job);
126 }
127}