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