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}