rustc_data_structures/
jobserver.rs

1use std::sync::{LazyLock, OnceLock};
2
3pub use jobserver_crate::{Acquired, Client, HelperThread};
4use jobserver_crate::{FromEnv, FromEnvErrorKind};
5
6// We can only call `from_env_ext` once per process
7
8// We stick this in a global because there could be multiple rustc instances
9// in this process, and the jobserver is per-process.
10static GLOBAL_CLIENT: LazyLock<Result<Client, String>> = LazyLock::new(|| {
11    // Note that this is unsafe because it may misinterpret file descriptors
12    // on Unix as jobserver file descriptors. We hopefully execute this near
13    // the beginning of the process though to ensure we don't get false
14    // positives, or in other words we try to execute this before we open
15    // any file descriptors ourselves.
16    let FromEnv { client, var } = unsafe { Client::from_env_ext(true) };
17
18    let error = match client {
19        Ok(client) => return Ok(client),
20        Err(e) => e,
21    };
22
23    if matches!(
24        error.kind(),
25        FromEnvErrorKind::NoEnvVar
26            | FromEnvErrorKind::NoJobserver
27            | FromEnvErrorKind::NegativeFd
28            | FromEnvErrorKind::Unsupported
29    ) {
30        return Ok(default_client());
31    }
32
33    // Environment specifies jobserver, but it looks incorrect.
34    // Safety: `error.kind()` should be `NoEnvVar` if `var == None`.
35    let (name, value) = var.unwrap();
36    Err(format!(
37        "failed to connect to jobserver from environment variable `{name}={:?}`: {error}",
38        value
39    ))
40});
41
42// Create a new jobserver if there's no inherited one.
43fn default_client() -> Client {
44    // Pick a "reasonable maximum" capping out at 32
45    // so we don't take everything down by hogging the process run queue.
46    // The fixed number is used to have deterministic compilation across machines.
47    let client = Client::new(32).expect("failed to create jobserver");
48
49    // Acquire a token for the main thread which we can release later
50    client.acquire_raw().ok();
51
52    client
53}
54
55static GLOBAL_CLIENT_CHECKED: OnceLock<Client> = OnceLock::new();
56
57pub fn initialize_checked(report_warning: impl FnOnce(&'static str)) {
58    let client_checked = match &*GLOBAL_CLIENT {
59        Ok(client) => client.clone(),
60        Err(e) => {
61            report_warning(e);
62            default_client()
63        }
64    };
65    GLOBAL_CLIENT_CHECKED.set(client_checked).ok();
66}
67
68const ACCESS_ERROR: &str = "jobserver check should have been called earlier";
69
70pub fn client() -> Client {
71    GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone()
72}
73
74pub fn acquire_thread() {
75    GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).acquire_raw().ok();
76}
77
78pub fn release_thread() {
79    GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).release_raw().ok();
80}