]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! Bindings to acquire a global named lock. |
2 | //! | |
3 | //! This is intended to be used to synchronize multiple compiler processes to | |
4 | //! ensure that we can output complete errors without interleaving on Windows. | |
5 | //! Note that this is currently only needed for allowing only one 32-bit MSVC | |
6 | //! linker to execute at once on MSVC hosts, so this is only implemented for | |
7 | //! `cfg(windows)`. Also note that this may not always be used on Windows, | |
8 | //! only when targeting 32-bit MSVC. | |
9 | //! | |
10 | //! For more information about why this is necessary, see where this is called. | |
11 | ||
12 | use std::any::Any; | |
13 | ||
14 | #[cfg(windows)] | |
15 | pub fn acquire_global_lock(name: &str) -> Box<dyn Any> { | |
16 | use std::ffi::CString; | |
17 | use std::io; | |
18 | ||
19 | use winapi::shared::ntdef::HANDLE; | |
20 | use winapi::um::handleapi::CloseHandle; | |
21 | use winapi::um::synchapi::{CreateMutexA, ReleaseMutex, WaitForSingleObject}; | |
22 | use winapi::um::winbase::{INFINITE, WAIT_ABANDONED, WAIT_OBJECT_0}; | |
23 | ||
24 | struct Handle(HANDLE); | |
25 | ||
26 | impl Drop for Handle { | |
27 | fn drop(&mut self) { | |
28 | unsafe { | |
29 | CloseHandle(self.0); | |
30 | } | |
31 | } | |
32 | } | |
33 | ||
34 | struct Guard(Handle); | |
35 | ||
36 | impl Drop for Guard { | |
37 | fn drop(&mut self) { | |
38 | unsafe { | |
39 | ReleaseMutex((self.0).0); | |
40 | } | |
41 | } | |
42 | } | |
43 | ||
44 | let cname = CString::new(name).unwrap(); | |
45 | unsafe { | |
46 | // Create a named mutex, with no security attributes and also not | |
47 | // acquired when we create it. | |
48 | // | |
49 | // This will silently create one if it doesn't already exist, or it'll | |
50 | // open up a handle to one if it already exists. | |
51 | let mutex = CreateMutexA(std::ptr::null_mut(), 0, cname.as_ptr()); | |
52 | if mutex.is_null() { | |
53 | panic!( | |
54 | "failed to create global mutex named `{}`: {}", | |
55 | name, | |
56 | io::Error::last_os_error() | |
57 | ); | |
58 | } | |
59 | let mutex = Handle(mutex); | |
60 | ||
61 | // Acquire the lock through `WaitForSingleObject`. | |
62 | // | |
63 | // A return value of `WAIT_OBJECT_0` means we successfully acquired it. | |
64 | // | |
65 | // A return value of `WAIT_ABANDONED` means that the previous holder of | |
66 | // the thread exited without calling `ReleaseMutex`. This can happen, | |
67 | // for example, when the compiler crashes or is interrupted via ctrl-c | |
68 | // or the like. In this case, however, we are still transferred | |
69 | // ownership of the lock so we continue. | |
70 | // | |
71 | // If an error happens.. well... that's surprising! | |
72 | match WaitForSingleObject(mutex.0, INFINITE) { | |
73 | WAIT_OBJECT_0 | WAIT_ABANDONED => {} | |
74 | code => { | |
75 | panic!( | |
76 | "WaitForSingleObject failed on global mutex named \ | |
77 | `{}`: {} (ret={:x})", | |
78 | name, | |
79 | io::Error::last_os_error(), | |
80 | code | |
81 | ); | |
82 | } | |
83 | } | |
84 | ||
85 | // Return a guard which will call `ReleaseMutex` when dropped. | |
86 | Box::new(Guard(mutex)) | |
87 | } | |
88 | } | |
89 | ||
90 | #[cfg(not(windows))] | |
91 | pub fn acquire_global_lock(_name: &str) -> Box<dyn Any> { | |
92 | Box::new(()) | |
93 | } |