rustc_errors/lock.rs
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
12use std::any::Any;
13
14#[cfg(windows)]
15pub(crate) fn acquire_global_lock(name: &str) -> Box<dyn Any> {
16 use std::ffi::CString;
17 use std::io;
18
19 use windows::Win32::Foundation::{CloseHandle, HANDLE, WAIT_ABANDONED, WAIT_OBJECT_0};
20 use windows::Win32::System::Threading::{
21 CreateMutexA, INFINITE, ReleaseMutex, WaitForSingleObject,
22 };
23 use windows::core::PCSTR;
24
25 struct Handle(HANDLE);
26
27 impl Drop for Handle {
28 fn drop(&mut self) {
29 unsafe {
30 // FIXME can panic here
31 CloseHandle(self.0).unwrap();
32 }
33 }
34 }
35
36 struct Guard(Handle);
37
38 impl Drop for Guard {
39 fn drop(&mut self) {
40 unsafe {
41 // FIXME can panic here
42 ReleaseMutex((self.0).0).unwrap();
43 }
44 }
45 }
46
47 let cname = CString::new(name).unwrap();
48 // Create a named mutex, with no security attributes and also not
49 // acquired when we create it.
50 //
51 // This will silently create one if it doesn't already exist, or it'll
52 // open up a handle to one if it already exists.
53 let mutex = unsafe { CreateMutexA(None, false, PCSTR::from_raw(cname.as_ptr().cast())) }
54 .unwrap_or_else(|_| panic!("failed to create global mutex named `{}`", name));
55 let mutex = Handle(mutex);
56
57 // Acquire the lock through `WaitForSingleObject`.
58 //
59 // A return value of `WAIT_OBJECT_0` means we successfully acquired it.
60 //
61 // A return value of `WAIT_ABANDONED` means that the previous holder of
62 // the thread exited without calling `ReleaseMutex`. This can happen,
63 // for example, when the compiler crashes or is interrupted via ctrl-c
64 // or the like. In this case, however, we are still transferred
65 // ownership of the lock so we continue.
66 //
67 // If an error happens.. well... that's surprising!
68 match unsafe { WaitForSingleObject(mutex.0, INFINITE) } {
69 WAIT_OBJECT_0 | WAIT_ABANDONED => (),
70 err => panic!(
71 "WaitForSingleObject failed on global mutex named `{}`: {} (ret={:x})",
72 name,
73 io::Error::last_os_error(),
74 err.0
75 ),
76 }
77
78 // Return a guard which will call `ReleaseMutex` when dropped.
79 Box::new(Guard(mutex))
80}
81
82#[cfg(not(windows))]
83pub(crate) fn acquire_global_lock(_name: &str) -> Box<dyn Any> {
84 Box::new(())
85}