From e2bee64d7f4d93abe901e8cc9f5775a70a44f362 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 5 Mar 2024 05:26:10 +0000 Subject: [PATCH 1/2] Give deadlock handler access to `GlobalCtxt` --- compiler/rustc_interface/src/interface.rs | 13 +++++++++++-- compiler/rustc_interface/src/queries.rs | 10 ++++++++++ compiler/rustc_interface/src/util.rs | 18 ++++++++++++------ 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 284d965979ece..04e49d962b23f 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -24,6 +24,7 @@ use rustc_span::symbol::sym; use rustc_span::FileName; use std::path::PathBuf; use std::result; +use std::sync::atomic::AtomicPtr; use std::sync::Arc; pub type Result = result::Result; @@ -39,6 +40,7 @@ pub struct Compiler { pub sess: Session, pub codegen_backend: Box, pub(crate) override_queries: Option, + pub compiler_for_deadlock_handler: Arc>, } /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`. @@ -331,9 +333,12 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); early_dcx.initialize_checked_jobserver(); + let compiler_for_deadlock_handler = Arc::>::default(); + util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.unstable_opts.threads, + compiler_for_deadlock_handler.clone(), || { crate::callbacks::setup_callbacks(); @@ -422,8 +427,12 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se } sess.lint_store = Some(Lrc::new(lint_store)); - let compiler = - Compiler { sess, codegen_backend, override_queries: config.override_queries }; + let compiler = Arc::new(Compiler { + sess, + codegen_backend, + override_queries: config.override_queries, + compiler_for_deadlock_handler, + }); rustc_span::set_source_map(compiler.sess.psess.clone_source_map(), move || { // There are two paths out of `f`. diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 7cdf7cd25b1c1..eb2c6b70c6f81 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -24,6 +24,7 @@ use rustc_session::Session; use rustc_span::symbol::sym; use std::any::Any; use std::cell::{RefCell, RefMut}; +use std::sync::atomic::Ordering; use std::sync::Arc; /// Represent the result of a query. @@ -160,6 +161,10 @@ impl<'tcx> Queries<'tcx> { &self.hir_arena, ); + self.compiler + .compiler_for_deadlock_handler + .store(qcx as *const _ as *mut _, Ordering::Relaxed); + qcx.enter(|tcx| { let feed = tcx.feed_local_crate(); feed.crate_name(crate_name); @@ -311,6 +316,11 @@ impl Compiler { { // Must declare `_timer` first so that it is dropped after `queries`. let mut _timer = None; + let _guard = rustc_data_structures::defer(|| { + // If we got here, there was no deadlock, so we can't be in a situation where a deadlock handler + // is accessing the `GlobalCtxt`. + self.compiler_for_deadlock_handler.store(std::ptr::null_mut(), Ordering::Relaxed); + }); let queries = Queries::new(self); let ret = f(&queries); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0d50200133cb2..1081b92e22754 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,8 +18,8 @@ use session::EarlyDiagCtxt; use std::env; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::path::{Path, PathBuf}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::OnceLock; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use std::sync::{Arc, OnceLock}; use std::thread; /// Function pointer type that constructs a new CodegenBackend. @@ -90,6 +90,7 @@ pub(crate) fn run_in_thread_with_globals R + Send, R: Send>( pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( edition: Edition, _threads: usize, + _compiler: Arc>, f: F, ) -> R { run_in_thread_with_globals(edition, f) @@ -99,10 +100,11 @@ 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, + compiler: Arc>, f: F, ) -> R { use rustc_data_structures::{jobserver, sync::FromDyn}; - use rustc_middle::ty::tls; + use rustc_middle::ty::GlobalCtxt; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::{deadlock, QueryContext}; @@ -122,11 +124,15 @@ pub(crate) fn run_in_thread_pool_with_globals R + Send, R: Send>( .acquire_thread_handler(jobserver::acquire_thread) .release_thread_handler(jobserver::release_thread) .num_threads(threads) - .deadlock_handler(|| { + .deadlock_handler(move || { + let compiler = compiler.load(Ordering::Relaxed); + assert!(!compiler.is_null()); + let compiler = compiler as *const GlobalCtxt<'_>; // 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).collect_active_jobs())); + let query_map = FromDyn::from(unsafe { + (*compiler).enter(|tcx| QueryCtxt::new(tcx).collect_active_jobs()) + }); let registry = rayon_core::Registry::current(); thread::spawn(move || deadlock(query_map.into_inner(), ®istry)); }); From c30186ce02b172ad385c12769d8c3919eee82f48 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 7 Mar 2024 10:57:06 +0000 Subject: [PATCH 2/2] Double check that the deadlock handler doesn't get overwritten --- compiler/rustc_interface/src/interface.rs | 3 +++ compiler/rustc_interface/src/lib.rs | 1 + compiler/rustc_interface/src/queries.rs | 7 +++++-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 04e49d962b23f..0a46ef0f8d6b9 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -43,6 +43,9 @@ pub struct Compiler { pub compiler_for_deadlock_handler: Arc>, } +impl !Sync for Compiler {} +impl !rustc_data_structures::sync::DynSync for Compiler {} + /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`. pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec) -> Cfg { cfgs.into_iter() diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index d0ce23dacb5ef..d4942d7e2f40b 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -4,6 +4,7 @@ #![feature(let_chains)] #![feature(thread_spawn_unchecked)] #![feature(try_blocks)] +#![feature(negative_impls)] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index eb2c6b70c6f81..0face90d040e4 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -161,9 +161,11 @@ impl<'tcx> Queries<'tcx> { &self.hir_arena, ); - self.compiler + let old = self + .compiler .compiler_for_deadlock_handler - .store(qcx as *const _ as *mut _, Ordering::Relaxed); + .swap(qcx as *const _ as *mut _, Ordering::Relaxed); + assert!(old.is_null()); qcx.enter(|tcx| { let feed = tcx.feed_local_crate(); @@ -321,6 +323,7 @@ impl Compiler { // is accessing the `GlobalCtxt`. self.compiler_for_deadlock_handler.store(std::ptr::null_mut(), Ordering::Relaxed); }); + assert!(self.compiler_for_deadlock_handler.load(Ordering::Relaxed).is_null()); let queries = Queries::new(self); let ret = f(&queries);