Skip to content

Commit 3bb7aa3

Browse files
committed
Add a CurrentGcx type to let the deadlock handler access TyCtxt
1 parent 7bf47a4 commit 3bb7aa3

File tree

5 files changed

+93
-16
lines changed

5 files changed

+93
-16
lines changed

compiler/rustc_interface/src/interface.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::sync::Lrc;
99
use rustc_errors::registry::Registry;
1010
use rustc_errors::{ErrorGuaranteed, Handler};
1111
use rustc_lint::LintStore;
12+
use rustc_middle::ty::CurrentGcx;
1213
use rustc_middle::util::Providers;
1314
use rustc_middle::{bug, ty};
1415
use rustc_parse::maybe_new_parser_from_source_str;
@@ -38,6 +39,7 @@ pub struct Compiler {
3839
codegen_backend: Lrc<dyn CodegenBackend>,
3940
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
4041
pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
42+
pub(crate) current_gcx: CurrentGcx,
4143
}
4244

4345
impl Compiler {
@@ -298,7 +300,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
298300
util::run_in_thread_pool_with_globals(
299301
config.opts.edition,
300302
config.opts.unstable_opts.threads,
301-
|| {
303+
|current_gcx| {
302304
crate::callbacks::setup_callbacks();
303305

304306
let registry = &config.registry;
@@ -335,6 +337,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
335337
codegen_backend: Lrc::from(codegen_backend),
336338
register_lints: config.register_lints,
337339
override_queries: config.override_queries,
340+
current_gcx,
338341
};
339342

340343
rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {

compiler/rustc_interface/src/passes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ pub fn create_global_ctxt<'tcx>(
723723
incremental,
724724
),
725725
providers.hooks,
726+
compiler.current_gcx.clone(),
726727
)
727728
})
728729
})

compiler/rustc_interface/src/util.rs

+28-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
77
#[cfg(parallel_compiler)]
88
use rustc_data_structures::sync;
99
use rustc_errors::registry::Registry;
10+
use rustc_middle::ty::CurrentGcx;
1011
use rustc_parse::validate_attr;
1112
use rustc_session as session;
1213
use rustc_session::config::CheckCfg;
@@ -139,7 +140,7 @@ fn get_stack_size() -> Option<usize> {
139140
env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE)
140141
}
141142

142-
pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
143+
pub(crate) fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
143144
edition: Edition,
144145
f: F,
145146
) -> R {
@@ -160,7 +161,9 @@ pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
160161
// `unwrap` is ok here because `spawn_scoped` only panics if the thread
161162
// name contains null bytes.
162163
let r = builder
163-
.spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f))
164+
.spawn_scoped(s, move || {
165+
rustc_span::create_session_globals_then(edition, || f(CurrentGcx::new()))
166+
})
164167
.unwrap()
165168
.join();
166169

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

174177
#[cfg(not(parallel_compiler))]
175-
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
178+
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
176179
edition: Edition,
177180
_threads: usize,
178181
f: F,
@@ -181,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
181184
}
182185

183186
#[cfg(parallel_compiler)]
184-
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
187+
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
185188
edition: Edition,
186189
threads: usize,
187190
f: F,
@@ -194,27 +197,39 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
194197
let registry = sync::Registry::new(threads);
195198

196199
if !sync::is_dyn_thread_safe() {
197-
return run_in_thread_with_globals(edition, || {
200+
return run_in_thread_with_globals(edition, |current_gcx| {
198201
// Register the thread for use with the `WorkerLocal` type.
199202
registry.register();
200203

201-
f()
204+
f(current_gcx)
202205
});
203206
}
204207

208+
let current_gcx = CurrentGcx::new();
209+
let current_gcx_ = FromDyn::from(current_gcx.clone());
210+
let current_gcx = FromDyn::from(current_gcx);
211+
205212
let mut builder = rayon::ThreadPoolBuilder::new()
206213
.thread_name(|_| "rustc".to_string())
207214
.acquire_thread_handler(jobserver::acquire_thread)
208215
.release_thread_handler(jobserver::release_thread)
209216
.num_threads(threads)
210-
.deadlock_handler(|| {
217+
.deadlock_handler(move || {
211218
// On deadlock, creates a new thread and forwards information in thread
212219
// locals to it. The new thread runs the deadlock handler.
213-
let query_map = FromDyn::from(tls::with(|tcx| {
214-
QueryCtxt::new(tcx)
215-
.try_collect_active_jobs()
216-
.expect("active jobs shouldn't be locked in deadlock handler")
217-
}));
220+
221+
// Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a
222+
// `TyCtxt` TLS reference here.
223+
let query_map = current_gcx_.access(|gcx| {
224+
tls::enter_context(&tls::ImplicitCtxt::new(gcx), || {
225+
tls::with(|tcx| {
226+
QueryCtxt::new(tcx)
227+
.try_collect_active_jobs()
228+
.expect("active jobs shouldn't be locked in deadlock handler")
229+
})
230+
})
231+
});
232+
let query_map = FromDyn::from(query_map);
218233
let registry = rayon_core::Registry::current();
219234
thread::spawn(move || deadlock(query_map.into_inner(), &registry));
220235
});
@@ -241,7 +256,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
241256
})
242257
},
243258
// Run `f` on the first thread in the thread pool.
244-
move |pool: &rayon::ThreadPool| pool.install(f),
259+
move |pool: &rayon::ThreadPool| pool.install(|| f(current_gcx.into_inner())),
245260
)
246261
.unwrap()
247262
})

compiler/rustc_middle/src/ty/context.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,17 @@ use crate::ty::{
3232
};
3333
use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
3434
use rustc_ast::{self as ast, attr};
35+
use rustc_data_structures::defer;
3536
use rustc_data_structures::fingerprint::Fingerprint;
3637
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3738
use rustc_data_structures::intern::Interned;
3839
use rustc_data_structures::profiling::SelfProfilerRef;
3940
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
4041
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
4142
use rustc_data_structures::steal::Steal;
42-
use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal};
43+
#[cfg(parallel_compiler)]
44+
use rustc_data_structures::sync::DynSend;
45+
use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal};
4346
use rustc_data_structures::unord::UnordSet;
4447
use rustc_errors::{
4548
DecorateLint, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed, MultiSpan,
@@ -585,6 +588,8 @@ pub struct GlobalCtxt<'tcx> {
585588

586589
/// Stores memory for globals (statics/consts).
587590
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
591+
592+
current_gcx: CurrentGcx,
588593
}
589594

590595
impl<'tcx> GlobalCtxt<'tcx> {
@@ -595,10 +600,60 @@ impl<'tcx> GlobalCtxt<'tcx> {
595600
F: FnOnce(TyCtxt<'tcx>) -> R,
596601
{
597602
let icx = tls::ImplicitCtxt::new(self);
603+
604+
let gcx_ptr =
605+
GcxPtr { value: Lrc::new(RwLock::new(Some(icx.tcx.gcx as *const _ as *const ()))) };
606+
607+
// Reset `gcx_ptr` to `None` when we exit.
608+
let gcx_ptr_ = gcx_ptr.clone();
609+
let _on_drop = defer(move || {
610+
*gcx_ptr_.value.write() = None;
611+
});
612+
613+
// Set this `GlobalCtxt` as the current one.
614+
*self.current_gcx.value.lock() = Some(gcx_ptr);
615+
598616
tls::enter_context(&icx, || f(icx.tcx))
599617
}
600618
}
601619

620+
/// This stores a pointer to a `GlobalCtxt`. When the `GlobalCtxt` is no longer available the lock
621+
/// will be set to `None`.
622+
#[derive(Clone)]
623+
struct GcxPtr {
624+
value: Lrc<RwLock<Option<*const ()>>>,
625+
}
626+
627+
#[cfg(parallel_compiler)]
628+
unsafe impl DynSend for GcxPtr {}
629+
630+
/// This is used to get a reference to a `GlobalCtxt` if one is available.
631+
///
632+
/// This is needed to allow the deadlock handler access to `GlobalCtxt` to look for query cycles.
633+
/// It cannot use the `TLV` global because that's only guaranteed to be defined on the thread
634+
/// creating the `GlobalCtxt`. Other threads have access to the `TLV` only inside Rayon jobs, but
635+
/// the deadlock handler is not called inside such a job.
636+
#[derive(Clone)]
637+
pub struct CurrentGcx {
638+
value: Lrc<Lock<Option<GcxPtr>>>,
639+
}
640+
641+
impl CurrentGcx {
642+
pub fn new() -> Self {
643+
Self { value: Lrc::new(Lock::new(None)) }
644+
}
645+
646+
pub fn access<R>(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R {
647+
let gcx_ptr = self.value.lock().clone().unwrap();
648+
let read_guard = gcx_ptr.value.read();
649+
let gcx: *const GlobalCtxt<'_> = read_guard.unwrap() as *const _;
650+
// SAFETY: We hold the read lock for `GcxPtr`. That prevents `GlobalCtxt::enter` from
651+
// returning as it would first acquire the write lock. This ensures the `GlobalCtxt` is
652+
// live during `f`.
653+
f(unsafe { &*gcx })
654+
}
655+
}
656+
602657
impl<'tcx> TyCtxt<'tcx> {
603658
/// Expects a body and returns its codegen attributes.
604659
///
@@ -708,6 +763,7 @@ impl<'tcx> TyCtxt<'tcx> {
708763
query_kinds: &'tcx [DepKindStruct<'tcx>],
709764
query_system: QuerySystem<'tcx>,
710765
hooks: crate::hooks::Providers,
766+
current_gcx: CurrentGcx,
711767
) -> GlobalCtxt<'tcx> {
712768
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
713769
s.emit_fatal(err);
@@ -742,6 +798,7 @@ impl<'tcx> TyCtxt<'tcx> {
742798
new_solver_coherence_evaluation_cache: Default::default(),
743799
data_layout,
744800
alloc_map: Lock::new(interpret::AllocMap::new()),
801+
current_gcx,
745802
}
746803
}
747804

compiler/rustc_middle/src/ty/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ pub use self::consts::{
8888
Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
8989
};
9090
pub use self::context::{
91-
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed,
91+
tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,
92+
TyCtxtFeed,
9293
};
9394
pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams};
9495
pub use self::list::List;

0 commit comments

Comments
 (0)