From 88e0226d932e3bd007ca78458011d3a132a274ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 12 Apr 2019 09:22:05 +0200 Subject: [PATCH 1/6] Add a Worker type which allows executing code in the background --- src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/sync.rs | 2 + src/librustc_data_structures/sync/worker.rs | 169 ++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 src/librustc_data_structures/sync/worker.rs diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index a1d7ab8856daa..18c7be71fc4f2 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -22,6 +22,7 @@ #![feature(stmt_expr_attributes)] #![feature(core_intrinsics)] #![feature(integer_atomics)] +#![feature(arbitrary_self_types)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 73247c1469efd..be5ae47d794d7 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -23,6 +23,8 @@ use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use crate::owning_ref::{Erased, OwningRef}; +pub mod worker; + pub fn serial_join(oper_a: A, oper_b: B) -> (RA, RB) where A: FnOnce() -> RA, B: FnOnce() -> RB diff --git a/src/librustc_data_structures/sync/worker.rs b/src/librustc_data_structures/sync/worker.rs new file mode 100644 index 0000000000000..7ce19ad273cb4 --- /dev/null +++ b/src/librustc_data_structures/sync/worker.rs @@ -0,0 +1,169 @@ +use super::{Lrc, Lock}; + +pub trait Worker: super::Send { + type Message: super::Send; + type Result: super::Send; + + fn message(&mut self, msg: Self::Message); + fn complete(self) -> Self::Result; +} + +pub use executor::WorkerExecutor; + +#[cfg(parallel_compiler)] +mod executor { + use super::*; + use crate::jobserver; + use parking_lot::Condvar; + use std::mem; + + struct WorkerQueue { + scheduled: bool, + complete: bool, + messages: Vec, + result: Option, + } + + /// Allows executing a worker on any Rayon thread, + /// sending it messages and waiting for it to complete its computation. + pub struct WorkerExecutor { + queue: Lock>, + worker: Lock>, + #[cfg(parallel_compiler)] + cond_var: Condvar, + } + + impl WorkerExecutor { + pub fn new(worker: T) -> Self { + WorkerExecutor { + queue: Lock::new(WorkerQueue { + scheduled: false, + complete: false, + messages: Vec::new(), + result: None, + }), + worker: Lock::new(Some(worker)), + #[cfg(parallel_compiler)] + cond_var: Condvar::new(), + } + } + + fn try_run_worker(&self) { + if let Some(mut worker) = self.worker.try_lock() { + self.run_worker(&mut *worker); + } + } + + fn run_worker(&self, worker: &mut Option) { + let worker_ref = if let Some(worker_ref) = worker.as_mut() { + worker_ref + } else { + return + }; + loop { + let msgs = { + let mut queue = self.queue.lock(); + let msgs = mem::replace(&mut queue.messages, Vec::new()); + if msgs.is_empty() { + queue.scheduled = false; + if queue.complete { + queue.result = Some(worker.take().unwrap().complete()); + self.cond_var.notify_all(); + } + break; + } + msgs + }; + for msg in msgs { + worker_ref.message(msg); + } + } + } + + pub fn complete(&self) -> T::Result { + let mut queue = self.queue.lock(); + assert!(!queue.complete); + queue.complete = true; + if !queue.scheduled { + // The worker is not scheduled to run, just run it on the current thread. + queue.scheduled = true; + mem::drop(queue); + self.run_worker(&mut *self.worker.lock()); + queue = self.queue.lock(); + } else if let Some(mut worker) = self.worker.try_lock() { + // Try to run the worker on the current thread. + // It was scheduled to run, but it may not have started yet. + // If we are using a single thread, it may never start at all. + mem::drop(queue); + self.run_worker(&mut *worker); + queue = self.queue.lock(); + } else { + // The worker must be running on some other thread, + // and will eventually look at the queue again, since queue.scheduled is true. + // Wait for it. + + #[cfg(parallel_compiler)] + { + // Wait for the result + jobserver::release_thread(); + self.cond_var.wait(&mut queue); + jobserver::acquire_thread(); + } + } + queue.result.take().unwrap() + } + + fn queue_message(&self, msg: T::Message) -> bool { + let mut queue = self.queue.lock(); + queue.messages.push(msg); + let was_scheduled = queue.scheduled; + if !was_scheduled { + queue.scheduled = true; + } + was_scheduled + } + + pub fn message_in_pool(self: &Lrc, msg: T::Message) + where + T: 'static + { + if !self.queue_message(msg) { + let this = self.clone(); + #[cfg(parallel_compiler)] + rayon::spawn(move || this.try_run_worker()); + #[cfg(not(parallel_compiler))] + this.try_run_worker(); + } + } + } +} + +#[cfg(not(parallel_compiler))] +mod executor { + use super::*; + + pub struct WorkerExecutor { + worker: Lock>, + } + + impl WorkerExecutor { + pub fn new(worker: T) -> Self { + WorkerExecutor { + worker: Lock::new(Some(worker)), + } + } + + #[inline] + pub fn complete(&self) -> T::Result { + self.worker.lock().take().unwrap().complete() + } + + #[inline] + pub fn message_in_pool(self: &Lrc, msg: T::Message) + where + T: 'static + { + self.worker.lock().as_mut().unwrap().message(msg); + } + } +} From 42bd80aef9ad0acc4cd7964998c4becae3693483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 16 Apr 2019 10:45:01 +0200 Subject: [PATCH 2/6] Fix inherent_impls --- .../coherence/inherent_impls.rs | 33 +++---------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 6088c03fc0681..0b04160522047 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -7,7 +7,6 @@ //! `tcx.inherent_impls(def_id)`). That value, however, //! is computed by selecting an idea from this table. -use rustc::dep_graph::DepKind; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::hir; use rustc::hir::itemlikevisit::ItemLikeVisitor; @@ -36,35 +35,11 @@ pub fn crate_inherent_impls<'tcx>( pub fn inherent_impls<'tcx>(tcx: TyCtxt<'tcx>, ty_def_id: DefId) -> &'tcx [DefId] { assert!(ty_def_id.is_local()); - // NB. Until we adopt the red-green dep-tracking algorithm (see - // [the plan] for details on that), we do some hackery here to get - // the dependencies correct. Basically, we use a `with_ignore` to - // read the result we want. If we didn't have the `with_ignore`, - // we would wind up with a dependency on the entire crate, which - // we don't want. Then we go and add dependencies on all the impls - // in the result (which is what we wanted). - // - // The result is a graph with an edge from `Hir(I)` for every impl - // `I` defined on some type `T` to `CoherentInherentImpls(T)`, - // thus ensuring that if any of those impls change, the set of - // inherent impls is considered dirty. - // - // [the plan]: https://github.com/rust-lang/rust-roadmap/issues/4 - - let result = tcx.dep_graph.with_ignore(|| { - let crate_map = tcx.crate_inherent_impls(ty_def_id.krate); - match crate_map.inherent_impls.get(&ty_def_id) { - Some(v) => &v[..], - None => &[], - } - }); - - for &impl_def_id in &result[..] { - let def_path_hash = tcx.def_path_hash(impl_def_id); - tcx.dep_graph.read(def_path_hash.to_dep_node(DepKind::Hir)); + let crate_map = tcx.crate_inherent_impls(ty_def_id.krate); + match crate_map.inherent_impls.get(&ty_def_id) { + Some(v) => &v[..], + None => &[], } - - result } struct InherentCollect<'tcx> { From f186b83f804a08db5423df96b1af07d10550bfa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 15 Mar 2019 12:17:11 +0100 Subject: [PATCH 3/6] Add an AtomicCell abstraction --- src/librustc_data_structures/Cargo.toml | 1 + src/librustc_data_structures/sync.rs | 59 ++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index cd792d31187bd..fc58da4335025 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -19,6 +19,7 @@ rustc_cratesio_shim = { path = "../librustc_cratesio_shim" } serialize = { path = "../libserialize" } graphviz = { path = "../libgraphviz" } cfg-if = "0.1.2" +crossbeam-utils = { version = "0.6.5", features = ["nightly"] } stable_deref_trait = "1.0.0" rayon = { version = "0.2.0", package = "rustc-rayon" } rayon-core = { version = "0.2.0", package = "rustc-rayon-core" } diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index be5ae47d794d7..60c4c767e0016 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -69,6 +69,59 @@ cfg_if! { use std::ops::Add; use std::panic::{resume_unwind, catch_unwind, AssertUnwindSafe}; + #[derive(Debug)] + pub struct AtomicCell(Cell); + + impl AtomicCell { + #[inline] + pub fn new(v: T) -> Self { + AtomicCell(Cell::new(v)) + } + } + + impl AtomicCell { + pub fn into_inner(self) -> T { + self.0.into_inner() + } + + #[inline] + pub fn load(&self) -> T { + self.0.get() + } + + #[inline] + pub fn store(&self, val: T) { + self.0.set(val) + } + + pub fn swap(&self, val: T) -> T { + self.0.replace(val) + } + } + + impl AtomicCell { + pub fn compare_exchange(&self, + current: T, + new: T) + -> Result { + let read = self.0.get(); + if read == current { + self.0.set(new); + Ok(read) + } else { + Err(read) + } + } + } + + impl + Copy> AtomicCell { + pub fn fetch_add(&self, val: T) -> T { + let old = self.0.get(); + self.0.set(old + val); + old + } + } + #[derive(Debug)] pub struct Atomic(Cell); @@ -79,7 +132,7 @@ cfg_if! { } } - impl Atomic { + impl Atomic { pub fn into_inner(self) -> T { self.0.into_inner() } @@ -97,7 +150,9 @@ cfg_if! { pub fn swap(&self, val: T, _: Ordering) -> T { self.0.replace(val) } + } + impl Atomic { pub fn compare_exchange(&self, current: T, new: T, @@ -273,6 +328,8 @@ cfg_if! { pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64}; + pub use crossbeam_utils::atomic::AtomicCell; + pub use std::sync::Arc as Lrc; pub use std::sync::Weak as Weak; From 9bdd397952b06caa072bcf6e3ad48d6f722d1a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 19 Apr 2019 21:38:27 +0200 Subject: [PATCH 4/6] Add compare_and_swap --- src/librustc_data_structures/sync.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 60c4c767e0016..224f2a1d566f0 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -100,6 +100,13 @@ cfg_if! { } impl AtomicCell { + pub fn compare_and_swap(&self, current: T, new: T) -> T { + match self.compare_exchange(current, new) { + Ok(v) => v, + Err(v) => v, + } + } + pub fn compare_exchange(&self, current: T, new: T) From c052998aac9defb67394723525cdb2a19706bf83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 6 May 2019 09:32:50 +0200 Subject: [PATCH 5/6] Add get_mut --- src/librustc_data_structures/sync.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/librustc_data_structures/sync.rs b/src/librustc_data_structures/sync.rs index 224f2a1d566f0..1b62bd5633c2e 100644 --- a/src/librustc_data_structures/sync.rs +++ b/src/librustc_data_structures/sync.rs @@ -77,6 +77,11 @@ cfg_if! { pub fn new(v: T) -> Self { AtomicCell(Cell::new(v)) } + + #[inline] + pub fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } } impl AtomicCell { From 86e1d91fb3c326779f0808ad8f4e3964d82ce465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 18 Jun 2019 03:38:45 +0200 Subject: [PATCH 6/6] Update Cargo.lock --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index d3c6be59b75df..63a84f1fc2adc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2780,6 +2780,7 @@ name = "rustc_data_structures" version = "0.0.0" dependencies = [ "cfg-if 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "ena 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",