diff --git a/Cargo.lock b/Cargo.lock index f44644cffa62a..3425bd66666a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3113,7 +3113,6 @@ dependencies = [ "chalk-engine", "fmt_macros", "graphviz", - "jobserver", "log", "measureme", "parking_lot 0.9.0", @@ -3127,6 +3126,7 @@ dependencies = [ "rustc_feature", "rustc_hir", "rustc_index", + "rustc_jobserver", "rustc_macros", "rustc_session", "rustc_span", @@ -3587,7 +3587,6 @@ version = "0.0.0" dependencies = [ "bitflags", "cc", - "jobserver", "libc", "log", "memmap", @@ -3602,6 +3601,7 @@ dependencies = [ "rustc_hir", "rustc_incremental", "rustc_index", + "rustc_jobserver", "rustc_session", "rustc_span", "rustc_target", @@ -3636,7 +3636,6 @@ dependencies = [ "ena", "graphviz", "indexmap", - "jobserver", "lazy_static 1.4.0", "log", "measureme", @@ -3668,6 +3667,7 @@ dependencies = [ "rustc_feature", "rustc_hir", "rustc_interface", + "rustc_jobserver", "rustc_lint", "rustc_metadata", "rustc_mir", @@ -3796,6 +3796,7 @@ dependencies = [ "rustc_expand", "rustc_hir", "rustc_incremental", + "rustc_jobserver", "rustc_lint", "rustc_metadata", "rustc_mir", @@ -3818,6 +3819,16 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "rustc_jobserver" +version = "0.0.0" +dependencies = [ + "jobserver", + "lazy_static 1.4.0", + "log", + "serialize", +] + [[package]] name = "rustc_lexer" version = "0.1.0" diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index 782c6879ac58f..053d6db02494d 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -14,7 +14,7 @@ arena = { path = "../libarena" } bitflags = "1.2.1" fmt_macros = { path = "../libfmt_macros" } graphviz = { path = "../libgraphviz" } -jobserver = "0.1" +rustc_jobserver = { path = "../librustc_jobserver" } scoped-tls = "1.0" log = { version = "0.4", features = ["release_max_level_info", "std"] } rustc-rayon = "0.3.0" diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs index 8aae57e72cd52..48d13fd965083 100644 --- a/src/librustc/ty/query/job.rs +++ b/src/librustc/ty/query/job.rs @@ -18,8 +18,8 @@ use { rustc_data_structures::stable_hasher::{HashStable, StableHasher}, rustc_data_structures::sync::Lock, rustc_data_structures::sync::Lrc, - rustc_data_structures::{jobserver, OnDrop}, - rustc_rayon_core as rayon_core, + rustc_data_structures::OnDrop, + rustc_jobserver as jobserver, rustc_rayon_core as rayon_core, rustc_span::DUMMY_SP, std::iter::FromIterator, std::{mem, process, thread}, diff --git a/src/librustc_codegen_ssa/Cargo.toml b/src/librustc_codegen_ssa/Cargo.toml index 8d767e5c2a04f..5db8733c14b86 100644 --- a/src/librustc_codegen_ssa/Cargo.toml +++ b/src/librustc_codegen_ssa/Cargo.toml @@ -16,7 +16,6 @@ num_cpus = "1.0" memmap = "0.7" log = "0.4.5" libc = "0.2.44" -jobserver = "0.1.11" tempfile = "3.1" rustc_serialize = { path = "../libserialize", package = "serialize" } @@ -34,3 +33,4 @@ rustc_incremental = { path = "../librustc_incremental" } rustc_index = { path = "../librustc_index" } rustc_target = { path = "../librustc_target" } rustc_session = { path = "../librustc_session" } +rustc_jobserver = { path = "../librustc_jobserver" } diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index 92f795acc5438..22166fc452c61 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -10,7 +10,6 @@ use crate::{ }; use crate::traits::*; -use jobserver::{Acquired, Client}; use rustc::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId}; use rustc::middle::cstore::EncodedMetadata; use rustc::middle::exported_symbols::SymbolExportLevel; @@ -32,6 +31,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; +use rustc_jobserver::Acquired; use rustc_session::cgu_reuse_tracker::CguReuseTracker; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::SourceMap; @@ -41,7 +41,6 @@ use syntax::attr; use std::any::Any; use std::fs; -use std::io; use std::mem; use std::path::{Path, PathBuf}; use std::str; @@ -453,7 +452,6 @@ pub fn start_async_codegen( codegen_worker_send, coordinator_receive, total_cgus, - sess.jobserver.clone(), Arc::new(modules_config), Arc::new(metadata_config), Arc::new(allocator_config), @@ -905,7 +903,7 @@ fn execute_lto_work_item( } pub enum Message { - Token(io::Result), + Token(Acquired), NeedsFatLTO { result: FatLTOInput, worker_id: usize, @@ -953,7 +951,6 @@ fn start_executing_work( codegen_worker_send: Sender>, coordinator_receive: Receiver>, total_cgus: usize, - jobserver: Client, modules_config: Arc, metadata_config: Arc, allocator_config: Arc, @@ -997,11 +994,12 @@ fn start_executing_work( // get tokens on `coordinator_receive` which will // get managed in the main loop below. let coordinator_send2 = coordinator_send.clone(); - let helper = jobserver - .into_helper_thread(move |token| { + let request_token = move || { + let coordinator_send2 = coordinator_send2.clone(); + rustc_jobserver::request_token(move |token| { drop(coordinator_send2.send(Box::new(Message::Token::(token)))); - }) - .expect("failed to spawn helper thread"); + }); + }; let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, &mut |cnum, path| { @@ -1308,7 +1306,7 @@ fn start_executing_work( .unwrap_or_else(|e| e); work_items.insert(insertion_index, (work, cost)); if !cgcx.opts.debugging_opts.no_parallel_llvm { - helper.request_token(); + request_token(); } } } @@ -1390,25 +1388,15 @@ fn start_executing_work( // this to spawn a new unit of work, or it may get dropped // immediately if we have no more work to spawn. Message::Token(token) => { - match token { - Ok(token) => { - tokens.push(token); - - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - // If the main thread token is used for LLVM work - // at the moment, we turn that thread into a regular - // LLVM worker thread, so the main thread is free - // to react to codegen demand. - main_thread_worker_state = MainThreadWorkerState::Idle; - running += 1; - } - } - Err(e) => { - let msg = &format!("failed to acquire jobserver token: {}", e); - shared_emitter.fatal(msg); - // Exit the coordinator thread - panic!("{}", msg) - } + tokens.push(token); + + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + // If the main thread token is used for LLVM work + // at the moment, we turn that thread into a regular + // LLVM worker thread, so the main thread is free + // to react to codegen demand. + main_thread_worker_state = MainThreadWorkerState::Idle; + running += 1; } } @@ -1428,7 +1416,7 @@ fn start_executing_work( work_items.insert(insertion_index, (llvm_work_item, cost)); if !cgcx.opts.debugging_opts.no_parallel_llvm { - helper.request_token(); + request_token(); } assert!(!codegen_aborted); assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index fb4f818c4b249..ffffd3eb647e7 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -13,7 +13,6 @@ doctest = false ena = "0.13.1" indexmap = "1" log = "0.4" -jobserver_crate = { version = "0.1.13", package = "jobserver" } lazy_static = "1" rustc_serialize = { path = "../libserialize", package = "serialize" } graphviz = { path = "../libgraphviz" } diff --git a/src/librustc_data_structures/jobserver.rs b/src/librustc_data_structures/jobserver.rs deleted file mode 100644 index a811c88839d70..0000000000000 --- a/src/librustc_data_structures/jobserver.rs +++ /dev/null @@ -1,42 +0,0 @@ -pub use jobserver_crate::Client; -use lazy_static::lazy_static; - -lazy_static! { - // We can only call `from_env` once per process - - // Note that this is unsafe because it may misinterpret file descriptors - // on Unix as jobserver file descriptors. We hopefully execute this near - // the beginning of the process though to ensure we don't get false - // positives, or in other words we try to execute this before we open - // any file descriptors ourselves. - // - // Pick a "reasonable maximum" if we don't otherwise have - // a jobserver in our environment, capping out at 32 so we - // don't take everything down by hogging the process run queue. - // The fixed number is used to have deterministic compilation - // across machines. - // - // Also note that we stick this in a global because there could be - // multiple rustc instances in this process, and the jobserver is - // per-process. - static ref GLOBAL_CLIENT: Client = unsafe { - Client::from_env().unwrap_or_else(|| { - let client = Client::new(32).expect("failed to create jobserver"); - // Acquire a token for the main thread which we can release later - client.acquire_raw().ok(); - client - }) - }; -} - -pub fn client() -> Client { - GLOBAL_CLIENT.clone() -} - -pub fn acquire_thread() { - GLOBAL_CLIENT.acquire_raw().ok(); -} - -pub fn release_thread() { - GLOBAL_CLIENT.release_raw().ok(); -} diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 13792a0c890c4..290130f2dff7f 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -67,7 +67,6 @@ pub mod const_cstr; pub mod flock; pub mod fx; pub mod graph; -pub mod jobserver; pub mod macros; pub mod obligation_forest; pub mod owning_ref; diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 5b185f9a8b6b9..89329e9b0a8c3 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -31,6 +31,7 @@ rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_error_codes = { path = "../librustc_error_codes" } rustc_interface = { path = "../librustc_interface" } rustc_serialize = { path = "../libserialize", package = "serialize" } +rustc_jobserver = { path = "../librustc_jobserver" } syntax = { path = "../libsyntax" } rustc_span = { path = "../librustc_span" } diff --git a/src/librustc_interface/Cargo.toml b/src/librustc_interface/Cargo.toml index de7a9f4f5af1c..5b03952cf8f29 100644 --- a/src/librustc_interface/Cargo.toml +++ b/src/librustc_interface/Cargo.toml @@ -27,6 +27,7 @@ rustc_ast_passes = { path = "../librustc_ast_passes" } rustc_incremental = { path = "../librustc_incremental" } rustc_traits = { path = "../librustc_traits" } rustc_data_structures = { path = "../librustc_data_structures" } +rustc_jobserver = { path = "../librustc_jobserver" } rustc_codegen_ssa = { path = "../librustc_codegen_ssa" } rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_codegen_llvm = { path = "../librustc_codegen_llvm", optional = true } diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs index e213a4d33a6fb..3d03015b4c1fa 100644 --- a/src/librustc_interface/interface.rs +++ b/src/librustc_interface/interface.rs @@ -206,6 +206,22 @@ pub fn run_compiler_in_existing_thread_pool( pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { let stderr = config.stderr.take(); + + if config.opts.debugging_opts.jobserver_token_requests { + if let config::ErrorOutputType::Json { .. } = config.opts.error_format { + if stderr.is_some() { + panic!("Non-default output not supported with -Zjobserver-token-requests"); + } + } else { + panic!( + "-Zjobserver-token-requests can only be specified if using \ + JSON error output type" + ); + } + } + + rustc_jobserver::initialize(config.opts.debugging_opts.jobserver_token_requests); + util::spawn_thread_pool( config.opts.edition, config.opts.debugging_opts.threads, @@ -215,6 +231,9 @@ pub fn run_compiler(mut config: Config, f: impl FnOnce(&Compiler) -> R } pub fn default_thread_pool(edition: edition::Edition, f: impl FnOnce() -> R + Send) -> R { + // FIXME: allow for smart jobserver + rustc_jobserver::initialize(false); + // the 1 here is duplicating code in config.opts.debugging_opts.threads // which also defaults to 1; it ultimately doesn't matter as the default // isn't threaded, and just ignores this parameter diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs index 659323d1c2555..ff280fcde1ccc 100644 --- a/src/librustc_interface/util.rs +++ b/src/librustc_interface/util.rs @@ -4,11 +4,11 @@ use rustc::ty; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -#[cfg(parallel_compiler)] -use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::registry::Registry; +#[cfg(parallel_compiler)] +use rustc_jobserver as jobserver; use rustc_metadata::dynamic_lib::DynamicLibrary; use rustc_resolve::{self, Resolver}; use rustc_session as session; diff --git a/src/librustc_jobserver/Cargo.toml b/src/librustc_jobserver/Cargo.toml new file mode 100644 index 0000000000000..3374dc6165aba --- /dev/null +++ b/src/librustc_jobserver/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_jobserver" +version = "0.0.0" +edition = "2018" + +[lib] +name = "rustc_jobserver" +path = "lib.rs" +doctest = false + +[dependencies] +jobserver = "0.1.21" +log = "0.4" +lazy_static = "1" +rustc_serialize = { path = "../libserialize", package = "serialize" } diff --git a/src/librustc_jobserver/lib.rs b/src/librustc_jobserver/lib.rs new file mode 100644 index 0000000000000..2030107a14198 --- /dev/null +++ b/src/librustc_jobserver/lib.rs @@ -0,0 +1,298 @@ +//! rustc wants to manage its jobserver pool such that it never keeps a token +//! around for too long if it's not being used (i.e., eagerly return tokens), so +//! that Cargo can spawn more rustcs to go around. +//! +//! rustc also has a process-global implicit token when it starts, which we keep +//! track of -- we cannot release it to Cargo, and we want to make sure that if +//! it is released we *must* unblock a thread of execution onto it (otherwise we +//! will deadlock on it almost for sure). +//! +//! So, when we start up, we have an implicit token and no acquired tokens from +//! Cargo. +//! +//! We immediately on startup spawn a thread into the background to manage +//! communication with the jobserver (otherwise it's too hard to work with the +//! jobserver API). This is non-ideal, and it would be good to avoid, but +//! currently that cost is pretty much required for correct functionality, as we +//! must be able to atomically wait on both other threads releasing the implicit +//! token and the jobserver itself. That's not possible with the jobserver API +//! today unless we spawn up an additional thread. +//! +//! There are 3 primary APIs this crate exposes: +//! * `acquire_thread()` +//! * `release_thread()` +//! * `request_token(impl FnOnce(Acquired))` +//! +//! `acquire_thread` blocks on obtaining a token, `release_thread` releases a +//! token without blocking. +//! +//! `request_token` queues up the called function without blocking. +//! +//! ------------------------------------- +//! +//! We also have two modes to manage here. In the primary (default) mode we +//! communicate directly with the underlying jobserver (i.e., all +//! acquire/release requests fall through to the jobserver crate's +//! acquire/release functions). +//! +//! This can be quite poor for scalability, as at least on current Linux +//! kernels, each release on the jobserver will trigger the kernel to wake up +//! *all* waiters instead of just one, which, if you have lots of threads +//! waiting, is quite bad. +//! +//! For that reason, we have a second mode which utilizes Cargo to improve +//! scaling here. In that mode, we have slightly altered communication with the +//! jobserver. Instead of just blocking on the jobserver, we will instead first +//! print to stderr a JSON message indicating that we're interested in receiving +//! a jobserver token, and only then block on actually receiving said token. On +//! release, we don't write into the jobserver at all, instead merely printing +//! out that we've released a token. +//! +//! Note that the second mode allows Cargo to hook up each rustc with its own +//! jobserver (i.e., one per rustc process) and then fairly easily make sure to +//! fulfill the requests from rustc and such. Ultimately, that means that we +//! have just one rustc thread waiting on the jobserver: a solution that is +//! nearly optimal for scalability. + +use jobserver::Client; +use lazy_static::lazy_static; +use rustc_serialize::json::as_json; +use std::collections::VecDeque; +use std::mem; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Condvar, Mutex, MutexGuard}; + +lazy_static! { + // We can only call `from_env` once per process + + // Note that this is unsafe because it may misinterpret file descriptors + // on Unix as jobserver file descriptors. We hopefully execute this near + // the beginning of the process though to ensure we don't get false + // positives, or in other words we try to execute this before we open + // any file descriptors ourselves. + // + // Pick a "reasonable maximum" if we don't otherwise have + // a jobserver in our environment, capping out at 32 so we + // don't take everything down by hogging the process run queue. + // + // Also note that we stick this in a global because there could be + // multiple rustc instances in this process, and the jobserver is + // per-process. + static ref GLOBAL_CLIENT: Client = unsafe { + Client::from_env().unwrap_or_else(|| { + log::trace!("initializing fresh jobserver (not from env)"); + let client = Client::new(32).expect("failed to create jobserver"); + // Acquire a token for the main thread which we can release later + client.acquire_raw().ok(); + client + }) + }; + + static ref HELPER: Helper = Helper::new(); +} + +// These are the values for TOKEN_REQUESTS, which is an enum between these +// different options. +// +// +// It takes the following values: +// * EMPTY: not yet set +// * CARGO_REQUESTED: we're in the jobserver-per-rustc mode +// * MAKE_REQUESTED: legacy global jobserver client +const EMPTY: usize = 0; +const CARGO_REQUESTED: usize = 1; +const MAKE_REQUESTED: usize = 2; +static TOKEN_REQUESTS: AtomicUsize = AtomicUsize::new(EMPTY); + +fn should_notify() -> bool { + let value = TOKEN_REQUESTS.load(Ordering::SeqCst); + assert!(value != EMPTY, "jobserver must be initialized"); + value == CARGO_REQUESTED +} + +/// This will only adjust the global value to the new value of token_requests +/// the first time it's called, which means that you want to make sure that the +/// first call you make has the right value for `token_requests`. We try to help +/// out a bit by making sure that this is called before any interaction with the +/// jobserver (which usually happens almost immediately as soon as rustc does +/// anything due to spawning up the Rayon threadpool). +/// +/// Unfortunately the jobserver is inherently a global resource (we can't +/// have more than one) so the token requesting strategy must likewise be global. +/// +/// Usually this doesn't matter too much, as you're not wanting to set the token +/// requests unless you're in the one-rustc-per-process model. If this is called +/// twice with different values, then we will panic, as that likely represents a +/// bug in the calling code. +pub fn initialize(token_requests: bool) { + lazy_static::initialize(&GLOBAL_CLIENT); + lazy_static::initialize(&HELPER); + let new = if token_requests { CARGO_REQUESTED } else { MAKE_REQUESTED }; + let previous = TOKEN_REQUESTS.compare_and_swap(EMPTY, new, Ordering::SeqCst); + if previous == EMPTY { + log::info!("initialized rustc jobserver, set token_requests={:?}", token_requests); + } else if previous != new { + panic!("attempted to initialize jobserver with different token request setting"); + } +} + +struct Helper { + helper: jobserver::HelperThread, + + token_requests: Arc>, +} + +struct TokenRequests { + tokens: usize, + requests: VecDeque>, +} + +impl TokenRequests { + fn new() -> Self { + Self { tokens: 1, requests: VecDeque::new() } + } + + fn push_request(&mut self, request: impl FnOnce(Acquired) + Send + 'static) { + self.requests.push_back(Box::new(request)); + } +} + +impl Helper { + fn new() -> Self { + let requests = Arc::new(Mutex::new(TokenRequests::new())); + let helper_thread_requests = requests.clone(); + let helper = GLOBAL_CLIENT + .clone() + .into_helper_thread(move |token| { + log::trace!("Helper thread token sending into channel"); + let mut helper_thread_requests = helper_thread_requests.lock().unwrap(); + let sender = helper_thread_requests.requests.pop_front(); + if let Some(sender) = sender { + // We've acquired a token, but we need to not use it as we have our own + // custom release-on-drop struct since we'll want different logic than + // just normally releasing the token in this case. + // + // On unix this unfortunately means that we lose the specific byte that + // was in the pipe (i.e., we just write back the same byte all the time) + // but that's not expected to be a problem. + token.expect("acquire token").drop_without_releasing(); + sender(Acquired::new(helper_thread_requests)); + } + + // If we didn't manage to send the token off, just drop it on + // the ground; it'll get released automatically. + }) + .expect("spawned helper"); + Helper { helper, token_requests: requests } + } + + // This blocks on acquiring a token that was requested from the + // HelperThread, i.e., through `Helper::request_token`. + fn acquire_token_from_prior_request(&self) -> Acquired { + let mut token_requests = self.token_requests.lock().unwrap(); + if token_requests.tokens == 0 { + return Acquired::new(token_requests); + } + + let receiver = Arc::new((Mutex::new(None), Condvar::new())); + let receiver2 = receiver.clone(); + token_requests.push_request(move |token| { + let mut slot = receiver.0.lock().unwrap(); + *slot = Some(token); + receiver.1.notify_one(); + }); + + // Release tokens guard after registering our callback + mem::drop(token_requests); + + let (lock, cvar) = &*receiver2; + let mut guard = cvar.wait_while(lock.lock().unwrap(), |s| s.is_none()).unwrap(); + guard.take().unwrap() + } + + fn release_token(&self) { + let mut token_requests = self.token_requests.lock().unwrap(); + token_requests.tokens -= 1; + if token_requests.tokens == 0 { + // If there is a sender, then it needs to be given this token. + if let Some(sender) = token_requests.requests.pop_front() { + sender(Acquired::new(token_requests)); + return; + } + + return; + } + + if should_notify() { + eprintln!("{}", as_json(&JobserverNotification { jobserver_event: Event::Release })); + } else { + GLOBAL_CLIENT.release_raw().unwrap(); + } + } + + fn request_token(&self) { + log::trace!("{:?} requesting token", std::thread::current().id()); + // Just notify, don't actually acquire here. + notify_acquiring_token(); + self.helper.request_token(); + } +} + +#[must_use] +pub struct Acquired { + armed: bool, +} + +impl Drop for Acquired { + fn drop(&mut self) { + if self.armed { + release_thread(); + } + } +} + +impl Acquired { + fn new(mut requests: MutexGuard<'_, TokenRequests>) -> Self { + // When we create a token, bump up the acquired token counter + requests.tokens += 1; + Self { armed: true } + } + + fn disarm(mut self) { + self.armed = false; + } +} + +#[derive(RustcEncodable)] +enum Event { + WillAcquire, + Release, +} + +#[derive(RustcEncodable)] +struct JobserverNotification { + jobserver_event: Event, +} + +fn notify_acquiring_token() { + if should_notify() { + eprintln!("{}", as_json(&JobserverNotification { jobserver_event: Event::WillAcquire })); + } +} + +/// This does not block the current thread, but schedules the passed callback to +/// be called at some point in the future when a token is acquired. +pub fn request_token(f: impl FnOnce(Acquired) + Send + 'static) { + HELPER.token_requests.lock().unwrap().push_request(f); + HELPER.request_token(); +} + +/// This blocks the current thread until a token is acquired. +pub fn acquire_thread() { + HELPER.request_token(); + HELPER.acquire_token_from_prior_request().disarm(); +} + +pub fn release_thread() { + HELPER.release_token(); +} diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index d3163fa356436..15d465b0e13b8 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -970,4 +970,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "link the `.rlink` file generated by `-Z no-link`"), new_llvm_pass_manager: Option = (None, parse_opt_bool, [TRACKED], "use new LLVM pass manager"), + jobserver_token_requests: bool = (false, parse_bool, [UNTRACKED], + "Coordinate with caller through JSON messages on acquiring/releasing jobserver tokens"), } diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 6f003043aa95c..f433a7dc86e65 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -29,7 +29,6 @@ use rustc_span::source_map; use rustc_span::{MultiSpan, Span}; use rustc_data_structures::flock; -use rustc_data_structures::jobserver::{self, Client}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; @@ -119,10 +118,6 @@ pub struct Session { /// Always set to zero and incremented so that we can print fuel expended by a crate. pub print_fuel: AtomicU64, - /// Loaded up early on in the initialization of this `Session` to avoid - /// false positives about a job server in our environment. - pub jobserver: Client, - /// Cap lint level specified by a driver specifically. pub driver_lint_caps: FxHashMap, @@ -1068,7 +1063,6 @@ fn build_session_( optimization_fuel, print_fuel_crate, print_fuel, - jobserver: jobserver::client(), driver_lint_caps, trait_methods_not_found: Lock::new(Default::default()), confused_type_with_std_module: Lock::new(Default::default()),