diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 1c330c064ab69..591e78e909a51 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -9,6 +9,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_lint::LintStore; +use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; use rustc_middle::{bug, ty}; use rustc_parse::maybe_new_parser_from_source_str; @@ -38,6 +39,7 @@ pub struct Compiler { codegen_backend: Lrc, pub(crate) register_lints: Option>, pub(crate) override_queries: Option, + pub(crate) current_gcx: CurrentGcx, } impl Compiler { @@ -298,7 +300,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.unstable_opts.threads, - || { + |current_gcx| { crate::callbacks::setup_callbacks(); let registry = &config.registry; @@ -335,6 +337,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se codegen_backend: Lrc::from(codegen_backend), register_lints: config.register_lints, override_queries: config.override_queries, + current_gcx, }; rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 0e8f93cef173f..f478d2531817f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -723,6 +723,7 @@ pub fn create_global_ctxt<'tcx>( incremental, ), providers.hooks, + compiler.current_gcx.clone(), ) }) }) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0634e44c5620b..e84e8fffbe41a 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[cfg(parallel_compiler)] use rustc_data_structures::sync; use rustc_errors::registry::Registry; +use rustc_middle::ty::CurrentGcx; use rustc_parse::validate_attr; use rustc_session as session; use rustc_session::config::CheckCfg; @@ -139,7 +140,7 @@ fn get_stack_size() -> Option { env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE) } -pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( edition: Edition, f: F, ) -> R { @@ -160,7 +161,9 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( // `unwrap` is ok here because `spawn_scoped` only panics if the thread // name contains null bytes. let r = builder - .spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f)) + .spawn_scoped(s, move || { + rustc_span::create_session_globals_then(edition, || f(CurrentGcx::new())) + }) .unwrap() .join(); @@ -172,7 +175,7 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( } #[cfg(not(parallel_compiler))] -pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, f: F, @@ -181,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( } #[cfg(parallel_compiler)] -pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( +pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, threads: usize, f: F, @@ -194,27 +197,39 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( let registry = sync::Registry::new(threads); if !sync::is_dyn_thread_safe() { - return run_in_thread_with_globals(edition, || { + return run_in_thread_with_globals(edition, |current_gcx| { // Register the thread for use with the `WorkerLocal` type. registry.register(); - f() + f(current_gcx) }); } + let current_gcx = CurrentGcx::new(); + let current_gcx_ = FromDyn::from(current_gcx.clone()); + let current_gcx = FromDyn::from(current_gcx); + let mut builder = rayon::ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) .acquire_thread_handler(jobserver::acquire_thread) .release_thread_handler(jobserver::release_thread) .num_threads(threads) - .deadlock_handler(|| { + .deadlock_handler(move || { // On deadlock, creates a new thread and forwards information in thread // locals to it. The new thread runs the deadlock handler. - let query_map = FromDyn::from(tls::with(|tcx| { - QueryCtxt::new(tcx) - .try_collect_active_jobs() - .expect("active jobs shouldn't be locked in deadlock handler") - })); + + // Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a + // `TyCtxt` TLS reference here. + let query_map = current_gcx_.access(|gcx| { + tls::enter_context(&tls::ImplicitCtxt::new(gcx), || { + tls::with(|tcx| { + QueryCtxt::new(tcx) + .try_collect_active_jobs() + .expect("active jobs shouldn't be locked in deadlock handler") + }) + }) + }); + let query_map = FromDyn::from(query_map); let registry = rayon_core::Registry::current(); thread::spawn(move || deadlock(query_map.into_inner(), ®istry)); }); @@ -241,7 +256,7 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( }) }, // Run `f` on the first thread in the thread pool. - move |pool: &rayon::ThreadPool| pool.install(f), + move |pool: &rayon::ThreadPool| pool.install(|| f(current_gcx.into_inner())), ) .unwrap() }) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a7dd6dc737da6..a05b2327f3a32 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -32,6 +32,7 @@ use crate::ty::{ }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; +use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::intern::Interned; @@ -39,7 +40,9 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal}; +#[cfg(parallel_compiler)] +use rustc_data_structures::sync::DynSend; +use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ DecorateLint, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, MultiSpan, @@ -585,6 +588,8 @@ pub struct GlobalCtxt<'tcx> { /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock>, + + current_gcx: CurrentGcx, } impl<'tcx> GlobalCtxt<'tcx> { @@ -595,10 +600,60 @@ impl<'tcx> GlobalCtxt<'tcx> { F: FnOnce(TyCtxt<'tcx>) -> R, { let icx = tls::ImplicitCtxt::new(self); + + let gcx_ptr = + GcxPtr { value: Lrc::new(RwLock::new(Some(icx.tcx.gcx as *const _ as *const ()))) }; + + // Reset `gcx_ptr` to `None` when we exit. + let gcx_ptr_ = gcx_ptr.clone(); + let _on_drop = defer(move || { + *gcx_ptr_.value.write() = None; + }); + + // Set this `GlobalCtxt` as the current one. + *self.current_gcx.value.lock() = Some(gcx_ptr); + tls::enter_context(&icx, || f(icx.tcx)) } } +/// This stores a pointer to a `GlobalCtxt`. When the `GlobalCtxt` is no longer available the lock +/// will be set to `None`. +#[derive(Clone)] +struct GcxPtr { + value: Lrc>>, +} + +#[cfg(parallel_compiler)] +unsafe impl DynSend for GcxPtr {} + +/// This is used to get a reference to a `GlobalCtxt` if one is available. +/// +/// This is needed to allow the deadlock handler access to `GlobalCtxt` to look for query cycles. +/// It cannot use the `TLV` global because that's only guaranteed to be defined on the thread +/// creating the `GlobalCtxt`. Other threads have access to the `TLV` only inside Rayon jobs, but +/// the deadlock handler is not called inside such a job. +#[derive(Clone)] +pub struct CurrentGcx { + value: Lrc>>, +} + +impl CurrentGcx { + pub fn new() -> Self { + Self { value: Lrc::new(Lock::new(None)) } + } + + pub fn access(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R { + let gcx_ptr = self.value.lock().clone().unwrap(); + let read_guard = gcx_ptr.value.read(); + let gcx: *const GlobalCtxt<'_> = read_guard.unwrap() as *const _; + // SAFETY: We hold the read lock for `GcxPtr`. That prevents `GlobalCtxt::enter` from + // returning as it would first acquire the write lock. This ensures the `GlobalCtxt` is + // live during `f`. + f(unsafe { &*gcx }) + } +} + impl<'tcx> TyCtxt<'tcx> { /// Expects a body and returns its codegen attributes. /// @@ -708,6 +763,7 @@ impl<'tcx> TyCtxt<'tcx> { query_kinds: &'tcx [DepKindStruct<'tcx>], query_system: QuerySystem<'tcx>, hooks: crate::hooks::Providers, + current_gcx: CurrentGcx, ) -> GlobalCtxt<'tcx> { let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| { s.emit_fatal(err); @@ -742,6 +798,7 @@ impl<'tcx> TyCtxt<'tcx> { new_solver_coherence_evaluation_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), + current_gcx, } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index aa1e7f216a069..d436fb6169d7b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -88,7 +88,8 @@ pub use self::consts::{ Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree, }; pub use self::context::{ - tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, + tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, + TyCtxtFeed, }; pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams}; pub use self::list::List;