1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Job management on Windows for bootstrapping
13 //! Most of the time when you're running a build system (e.g. make) you expect
14 //! Ctrl-C or abnormal termination to actually terminate the entire tree of
15 //! process in play, not just the one at the top. This currently works "by
16 //! default" on Unix platforms because Ctrl-C actually sends a signal to the
17 //! *process group* rather than the parent process, so everything will get torn
18 //! down. On Windows, however, this does not happen and Ctrl-C just kills the
21 //! To achieve the same semantics on Windows we use Job Objects to ensure that
22 //! all processes die at the same time. Job objects have a mode of operation
23 //! where when all handles to the object are closed it causes all child
24 //! processes associated with the object to be terminated immediately.
25 //! Conveniently whenever a process in the job object spawns a new process the
26 //! child will be associated with the job object as well. This means if we add
27 //! ourselves to the job object we create then everything will get torn down!
29 //! Unfortunately most of the time the build system is actually called from a
30 //! python wrapper (which manages things like building the build system) so this
31 //! all doesn't quite cut it so far. To go the last mile we duplicate the job
32 //! object handle into our parent process (a python process probably) and then
33 //! close our own handle. This means that the only handle to the job object
34 //! resides in the parent python process, so when python dies the whole build
35 //! system dies (as one would probably expect!).
37 //! Note that this module has a #[cfg(windows)] above it as none of this logic
38 //! is required on Unix.
40 extern crate kernel32
;
48 use self::kernel32
::*;
50 pub unsafe fn setup() {
51 // Create a new job object for us to use
52 let job
= CreateJobObjectW(0 as *mut _
, 0 as *const _
);
53 assert
!(job
!= 0 as *mut _
, "{}", io
::Error
::last_os_error());
55 // Indicate that when all handles to the job object are gone that all
56 // process in the object should be killed. Note that this includes our
57 // entire process tree by default because we've added ourselves and our
58 // children will reside in the job by default.
59 let mut info
= mem
::zeroed
::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION
>();
60 info
.BasicLimitInformation
.LimitFlags
= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
;
61 let r
= SetInformationJobObject(job
,
62 JobObjectExtendedLimitInformation
,
63 &mut info
as *mut _
as LPVOID
,
64 mem
::size_of_val(&info
) as DWORD
);
65 assert
!(r
!= 0, "{}", io
::Error
::last_os_error());
67 // Assign our process to this job object. Note that if this fails, one very
68 // likely reason is that we are ourselves already in a job object! This can
69 // happen on the build bots that we've got for Windows, or if just anyone
70 // else is instrumenting the build. In this case we just bail out
71 // immediately and assume that they take care of it.
73 // Also note that nested jobs (why this might fail) are supported in recent
74 // versions of Windows, but the version of Windows that our bots are running
75 // at least don't support nested job objects.
76 let r
= AssignProcessToJobObject(job
, GetCurrentProcess());
82 // If we've got a parent process (e.g. the python script that called us)
83 // then move ownership of this job object up to them. That way if the python
84 // script is killed (e.g. via ctrl-c) then we'll all be torn down.
86 // If we don't have a parent (e.g. this was run directly) then we
87 // intentionally leak the job object handle. When our process exits
88 // (normally or abnormally) it will close the handle implicitly, causing all
89 // processes in the job to be cleaned up.
90 let pid
= match env
::var("BOOTSTRAP_PARENT_ID") {
95 let parent
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
.parse().unwrap());
96 assert
!(parent
!= 0 as *mut _
, "{}", io
::Error
::last_os_error());
97 let mut parent_handle
= 0 as *mut _
;
98 let r
= DuplicateHandle(GetCurrentProcess(), job
,
99 parent
, &mut parent_handle
,
100 0, FALSE
, DUPLICATE_SAME_ACCESS
);
102 // If this failed, well at least we tried! An example of DuplicateHandle
103 // failing in the past has been when the wrong python2 package spawed this
104 // build system (e.g. the `python2` package in MSYS instead of
105 // `mingw-w64-x86_64-python2`. Not sure why it failed, but the "failure
106 // mode" here is that we only clean everything up when the build system
107 // dies, not when the python parent does, so not too bad.