Skip to content

Commit

Permalink
Add a CurrentGcx type to let the deadlock handler access TyCtxt
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Sep 25, 2023
1 parent 7bf47a4 commit 3bb7aa3
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 16 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_interface/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct Compiler {
codegen_backend: Lrc<dyn CodegenBackend>,
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
pub(crate) current_gcx: CurrentGcx,
}

impl Compiler {
Expand Down Expand Up @@ -298,7 +300,7 @@ pub fn run_compiler<R: Send>(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;
Expand Down Expand Up @@ -335,6 +337,7 @@ pub fn run_compiler<R: Send>(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 || {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,7 @@ pub fn create_global_ctxt<'tcx>(
incremental,
),
providers.hooks,
compiler.current_gcx.clone(),
)
})
})
Expand Down
41 changes: 28 additions & 13 deletions compiler/rustc_interface/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -139,7 +140,7 @@ fn get_stack_size() -> Option<usize> {
env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE)
}

pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
pub(crate) fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
edition: Edition,
f: F,
) -> R {
Expand All @@ -160,7 +161,9 @@ pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> 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();

Expand All @@ -172,7 +175,7 @@ pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
}

#[cfg(not(parallel_compiler))]
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
edition: Edition,
_threads: usize,
f: F,
Expand All @@ -181,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
}

#[cfg(parallel_compiler)]
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
edition: Edition,
threads: usize,
f: F,
Expand All @@ -194,27 +197,39 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> 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(), &registry));
});
Expand All @@ -241,7 +256,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> 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()
})
Expand Down
59 changes: 58 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@ 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;
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,
Expand Down Expand Up @@ -585,6 +588,8 @@ pub struct GlobalCtxt<'tcx> {

/// Stores memory for globals (statics/consts).
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,

current_gcx: CurrentGcx,
}

impl<'tcx> GlobalCtxt<'tcx> {
Expand All @@ -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<RwLock<Option<*const ()>>>,
}

#[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<Lock<Option<GcxPtr>>>,
}

impl CurrentGcx {
pub fn new() -> Self {
Self { value: Lrc::new(Lock::new(None)) }
}

pub fn access<R>(&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.
///
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
}
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 3bb7aa3

Please sign in to comment.