From 892fed9d08e7392ad04fb4351e88bff416f6a2d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 18 Dec 2018 09:03:38 +0100 Subject: [PATCH 1/4] Add support for using a jobserver with Rayon --- src/librustc/Cargo.toml | 4 +- src/librustc/session/mod.rs | 30 +---- src/librustc/ty/query/job.rs | 5 + src/librustc_data_structures/Cargo.toml | 6 +- src/librustc_data_structures/jobserver.rs | 153 ++++++++++++++++++++++ src/librustc_data_structures/lib.rs | 1 + src/librustc_driver/Cargo.toml | 2 +- src/librustc_driver/driver.rs | 3 + 8 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 src/librustc_data_structures/jobserver.rs diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index e3557132a1252..26a27ea88e27a 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -20,8 +20,8 @@ num_cpus = "1.0" scoped-tls = "1.0" log = { version = "0.4", features = ["release_max_level_info", "std"] } polonius-engine = "0.6.2" -rustc-rayon = "0.1.1" -rustc-rayon-core = "0.1.1" +rustc-rayon = "0.1.2" +rustc-rayon-core = "0.1.2" rustc_apfloat = { path = "../librustc_apfloat" } rustc_target = { path = "../librustc_target" } rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 5b9b70edc6809..3cff5ec230959 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -34,7 +34,8 @@ use crate::util::profiling::SelfProfiler; use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple}; use rustc_data_structures::flock; -use jobserver::Client; +use rustc_data_structures::jobserver; +use ::jobserver::Client; use std; use std::cell::{self, Cell, RefCell}; @@ -1230,32 +1231,7 @@ pub fn build_session_( optimization_fuel, print_fuel_crate, print_fuel, - // 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 `Session` instances in this process, and the jobserver is - // per-process. - jobserver: unsafe { - static mut GLOBAL_JOBSERVER: *mut Client = 0 as *mut _; - static INIT: std::sync::Once = std::sync::ONCE_INIT; - INIT.call_once(|| { - let client = Client::from_env().unwrap_or_else(|| { - Client::new(32).expect("failed to create jobserver") - }); - GLOBAL_JOBSERVER = Box::into_raw(Box::new(client)); - }); - (*GLOBAL_JOBSERVER).clone() - }, + jobserver: jobserver::client(), has_global_allocator: Once::new(), has_panic_handler: Once::new(), driver_lint_caps, diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs index 22211468412c1..8e68c9fa30431 100644 --- a/src/librustc/ty/query/job.rs +++ b/src/librustc/ty/query/job.rs @@ -7,6 +7,7 @@ use std::{fmt, ptr}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::{Lock, LockGuard, Lrc, Weak}; use rustc_data_structures::OnDrop; +use rustc_data_structures::jobserver; use syntax_pos::Span; use crate::ty::tls; @@ -198,7 +199,11 @@ impl<'tcx> QueryLatch<'tcx> { // we have to be in the `wait` call. This is ensured by the deadlock handler // getting the self.info lock. rayon_core::mark_blocked(); + jobserver::release_thread(); waiter.condvar.wait(&mut info); + // Release the lock before we potentially block in `acquire_thread` + mem::drop(info); + jobserver::acquire_thread(); } } diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 6aa262715ecf4..6002bf69b7069 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -12,13 +12,15 @@ crate-type = ["dylib"] [dependencies] ena = "0.11" log = "0.4" +jobserver_crate = { version = "0.1", package = "jobserver" } +lazy_static = "1" rustc_cratesio_shim = { path = "../librustc_cratesio_shim" } serialize = { path = "../libserialize" } graphviz = { path = "../libgraphviz" } cfg-if = "0.1.2" stable_deref_trait = "1.0.0" -rayon = { version = "0.1.1", package = "rustc-rayon" } -rayon-core = { version = "0.1.1", package = "rustc-rayon-core" } +rayon = { version = "0.1.2", package = "rustc-rayon" } +rayon-core = { version = "0.1.2", package = "rustc-rayon-core" } rustc-hash = "1.0.1" smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } diff --git a/src/librustc_data_structures/jobserver.rs b/src/librustc_data_structures/jobserver.rs new file mode 100644 index 0000000000000..c85cdfbdcd8dd --- /dev/null +++ b/src/librustc_data_structures/jobserver.rs @@ -0,0 +1,153 @@ +use jobserver_crate::{Client, HelperThread, Acquired}; +use lazy_static::lazy_static; +use std::sync::{Condvar, Arc, Mutex}; +use std::mem; + +#[derive(Default)] +pub struct LockedProxyData { + /// The number of free thread tokens, this may include the implicit token given to the process + free: usize, + + /// The number of threads waiting for a token + waiters: usize, + + /// The number of tokens we requested from the server + requested: usize, + + /// Stored tokens which will be dropped when we no longer need them + tokens: Vec, +} + +impl LockedProxyData { + fn request_token(&mut self, thread: &Mutex) { + self.requested += 1; + thread.lock().unwrap().request_token(); + } + + fn release_token(&mut self, cond_var: &Condvar) { + if self.waiters > 0 { + self.free += 1; + cond_var.notify_one(); + } else { + if self.tokens.is_empty() { + // We are returning the implicit token + self.free += 1; + } else { + // Return a real token to the server + self.tokens.pop().unwrap(); + } + } + } + + fn take_token(&mut self, thread: &Mutex) -> bool { + if self.free > 0 { + self.free -= 1; + self.waiters -= 1; + + // We stole some token reqested by someone else + // Request another one + if self.requested + self.free < self.waiters { + self.request_token(thread); + } + + true + } else { + false + } + } + + fn new_requested_token(&mut self, token: Acquired, cond_var: &Condvar) { + self.requested -= 1; + + // Does anything need this token? + if self.waiters > 0 { + self.free += 1; + self.tokens.push(token); + cond_var.notify_one(); + } else { + // Otherwise we'll just drop it + mem::drop(token); + } + } +} + +#[derive(Default)] +pub struct ProxyData { + lock: Mutex, + cond_var: Condvar, +} + +pub struct Proxy { + thread: Mutex, + data: Arc, +} + +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(|| { + Client::new(32).expect("failed to create jobserver") + }) + }; + + static ref GLOBAL_PROXY: Proxy = { + let data = Arc::new(ProxyData::default()); + + Proxy { + data: data.clone(), + thread: Mutex::new(client().into_helper_thread(move |token| { + data.lock.lock().unwrap().new_requested_token(token.unwrap(), &data.cond_var); + }).unwrap()), + } + }; +} + +pub fn client() -> Client { + GLOBAL_CLIENT.clone() +} + +pub fn acquire_thread() { + GLOBAL_PROXY.acquire_token(); +} + +pub fn release_thread() { + GLOBAL_PROXY.release_token(); +} + +impl Proxy { + pub fn release_token(&self) { + self.data.lock.lock().unwrap().release_token(&self.data.cond_var); + } + + pub fn acquire_token(&self) { + let mut data = self.data.lock.lock().unwrap(); + data.waiters += 1; + if data.take_token(&self.thread) { + return; + } + // Request a token for us + data.request_token(&self.thread); + loop { + data = self.data.cond_var.wait(data).unwrap(); + if data.take_token(&self.thread) { + return; + } + } + } +} diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 2bfb1b24a81b9..09482340b1a1d 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -77,6 +77,7 @@ pub mod fx; pub mod graph; pub mod indexed_vec; pub mod interner; +pub mod jobserver; pub mod obligation_forest; pub mod owning_ref; pub mod ptr_key; diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 0b379ef662dde..a77e497af7b87 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -13,7 +13,7 @@ arena = { path = "../libarena" } graphviz = { path = "../libgraphviz" } log = "0.4" env_logger = { version = "0.5", default-features = false } -rustc-rayon = "0.1.1" +rustc-rayon = "0.1.2" scoped-tls = "1.0" rustc = { path = "../librustc" } rustc_allocator = { path = "../librustc_allocator" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f87a809e6c6eb..c4d8a66aa6db6 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -17,6 +17,7 @@ use rustc_allocator as allocator; use rustc_borrowck as borrowck; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::sync::{self, Lock}; +use rustc_data_structures::jobserver; use rustc_incremental; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; @@ -72,6 +73,8 @@ pub fn spawn_thread_pool R + sync::Send, R: sync:: let gcx_ptr = &Lock::new(0); let config = ThreadPoolBuilder::new() + .acquire_thread_handler(jobserver::acquire_thread) + .release_thread_handler(jobserver::release_thread) .num_threads(Session::threads_from_count(opts.debugging_opts.threads)) .deadlock_handler(|| unsafe { ty::query::handle_deadlock() }) .stack_size(::STACK_SIZE); From 35a1b91c4b4153027a722a267ba0bcdfec6d2bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 31 Jan 2019 17:00:06 +0100 Subject: [PATCH 2/4] Address comments --- src/librustc_data_structures/jobserver.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/librustc_data_structures/jobserver.rs b/src/librustc_data_structures/jobserver.rs index c85cdfbdcd8dd..48ac8125a0d66 100644 --- a/src/librustc_data_structures/jobserver.rs +++ b/src/librustc_data_structures/jobserver.rs @@ -4,7 +4,7 @@ use std::sync::{Condvar, Arc, Mutex}; use std::mem; #[derive(Default)] -pub struct LockedProxyData { +struct LockedProxyData { /// The number of free thread tokens, this may include the implicit token given to the process free: usize, @@ -72,12 +72,15 @@ impl LockedProxyData { } #[derive(Default)] -pub struct ProxyData { +struct ProxyData { lock: Mutex, cond_var: Condvar, } -pub struct Proxy { +/// A helper type which makes managing jobserver tokens easier. +/// It also allows you to treat the implicit token given to the process +/// in the same manner as requested tokens. +struct Proxy { thread: Mutex, data: Arc, } @@ -131,11 +134,11 @@ pub fn release_thread() { } impl Proxy { - pub fn release_token(&self) { + fn release_token(&self) { self.data.lock.lock().unwrap().release_token(&self.data.cond_var); } - pub fn acquire_token(&self) { + fn acquire_token(&self) { let mut data = self.data.lock.lock().unwrap(); data.waiters += 1; if data.take_token(&self.thread) { From 42d817f3e72cbc1e06b404bc4326cb70f4264811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 31 Jan 2019 20:09:43 +0100 Subject: [PATCH 3/4] Fix import --- src/librustc_driver/driver.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index c4d8a66aa6db6..858a5602e7b7f 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -17,6 +17,7 @@ use rustc_allocator as allocator; use rustc_borrowck as borrowck; use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_data_structures::sync::{self, Lock}; +#[cfg(parallel_compiler)] use rustc_data_structures::jobserver; use rustc_incremental; use rustc_metadata::creader::CrateLoader; From 5c78fa836d271e1ae664b9e0be4c5de3cb9e4698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 27 Feb 2019 15:17:12 +0100 Subject: [PATCH 4/4] Update Cargo.lock --- Cargo.lock | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 381322bc421d4..4fe8195117048 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2358,8 +2358,8 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "polonius-engine 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_apfloat 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", @@ -2409,8 +2409,8 @@ dependencies = [ "rustc-ap-rustc_cratesio_shim 373.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-serialize 373.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2508,23 +2508,23 @@ dependencies = [ [[package]] name = "rustc-rayon" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-rayon-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2672,11 +2672,13 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", + "jobserver 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_cratesio_shim 0.0.0", "serialize 0.0.0", "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2692,7 +2694,7 @@ dependencies = [ "graphviz 0.0.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_borrowck 0.0.0", "rustc_codegen_utils 0.0.0", @@ -2758,7 +2760,7 @@ version = "0.0.0" dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_borrowck 0.0.0", "rustc_codegen_utils 0.0.0", @@ -4190,8 +4192,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-ap-syntax_pos 373.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e4f88a1213562373cee9de5a1d77bbf16dd706030304af041c9733492fcc952" "checksum rustc-demangle 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "82ae957aa1b3055d8e086486723c0ccd3d7b8fa190ae8fa2e35543b6171c810e" "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" -"checksum rustc-rayon 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c6d5a683c6ba4ed37959097e88d71c9e8e26659a3cb5be8b389078e7ad45306" -"checksum rustc-rayon-core 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40f06724db71e18d68b3b946fdf890ca8c921d9edccc1404fdfdb537b0d12649" +"checksum rustc-rayon 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d98c51d9cbbe810c8b6693236d3412d8cd60513ff27a3e1b6af483dca0af544" +"checksum rustc-rayon-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "526e7b6d2707a5b9bec3927d424ad70fa3cfc68e0ac1b75e46cdbbc95adc5108" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_tools_util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c5a95edfa0c893236ae4778bb7c4752760e4c0d245e19b5eff33c5aa5eb9dc" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"