Skip to content

Commit c5e7f45

Browse files
committed
Auto merge of rust-lang#115220 - Zoxc:revive-gcx-ptr, r=oli-obk
Add a `CurrentGcx` type to let the deadlock handler access `TyCtxt` This brings back `GCX_PTR` (previously removed in rust-lang#74969) allowing the deadlock handler access to `GlobalCtxt`. This fixes rust-lang#111522. r? `@cjgillot`
2 parents 4ea92e3 + 9936a39 commit c5e7f45

File tree

5 files changed

+88
-16
lines changed

5 files changed

+88
-16
lines changed

Diff for: compiler/rustc_interface/src/interface.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use rustc_data_structures::sync::Lrc;
1010
use rustc_errors::registry::Registry;
1111
use rustc_errors::{DiagCtxt, ErrorGuaranteed};
1212
use rustc_lint::LintStore;
13+
1314
use rustc_middle::ty;
15+
use rustc_middle::ty::CurrentGcx;
1416
use rustc_middle::util::Providers;
1517
use rustc_parse::maybe_new_parser_from_source_str;
1618
use rustc_query_impl::QueryCtxt;
@@ -39,6 +41,7 @@ pub struct Compiler {
3941
pub sess: Session,
4042
pub codegen_backend: Box<dyn CodegenBackend>,
4143
pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
44+
pub(crate) current_gcx: CurrentGcx,
4245
}
4346

4447
/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
@@ -336,7 +339,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
336339
util::run_in_thread_pool_with_globals(
337340
config.opts.edition,
338341
config.opts.unstable_opts.threads,
339-
|| {
342+
|current_gcx| {
340343
crate::callbacks::setup_callbacks();
341344

342345
let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
@@ -430,8 +433,12 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
430433
}
431434
sess.lint_store = Some(Lrc::new(lint_store));
432435

433-
let compiler =
434-
Compiler { sess, codegen_backend, override_queries: config.override_queries };
436+
let compiler = Compiler {
437+
sess,
438+
codegen_backend,
439+
override_queries: config.override_queries,
440+
current_gcx,
441+
};
435442

436443
rustc_span::set_source_map(compiler.sess.psess.clone_source_map(), move || {
437444
// There are two paths out of `f`.

Diff for: compiler/rustc_interface/src/passes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ pub fn create_global_ctxt<'tcx>(
680680
incremental,
681681
),
682682
providers.hooks,
683+
compiler.current_gcx.clone(),
683684
)
684685
})
685686
})

Diff for: compiler/rustc_interface/src/util.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_codegen_ssa::traits::CodegenBackend;
55
#[cfg(parallel_compiler)]
66
use rustc_data_structures::sync;
77
use rustc_metadata::{load_symbol_from_dylib, DylibError};
8+
use rustc_middle::ty::CurrentGcx;
89
use rustc_parse::validate_attr;
910
use rustc_session as session;
1011
use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes};
@@ -64,7 +65,7 @@ fn init_stack_size() -> usize {
6465
})
6566
}
6667

67-
pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
68+
pub(crate) fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
6869
edition: Edition,
6970
f: F,
7071
) -> R {
@@ -82,7 +83,9 @@ pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
8283
// `unwrap` is ok here because `spawn_scoped` only panics if the thread
8384
// name contains null bytes.
8485
let r = builder
85-
.spawn_scoped(s, move || rustc_span::create_session_globals_then(edition, f))
86+
.spawn_scoped(s, move || {
87+
rustc_span::create_session_globals_then(edition, || f(CurrentGcx::new()))
88+
})
8689
.unwrap()
8790
.join();
8891

@@ -94,7 +97,7 @@ pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>(
9497
}
9598

9699
#[cfg(not(parallel_compiler))]
97-
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
100+
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
98101
edition: Edition,
99102
_threads: usize,
100103
f: F,
@@ -103,7 +106,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
103106
}
104107

105108
#[cfg(parallel_compiler)]
106-
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
109+
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
107110
edition: Edition,
108111
threads: usize,
109112
f: F,
@@ -117,24 +120,34 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
117120
let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
118121

119122
if !sync::is_dyn_thread_safe() {
120-
return run_in_thread_with_globals(edition, || {
123+
return run_in_thread_with_globals(edition, |current_gcx| {
121124
// Register the thread for use with the `WorkerLocal` type.
122125
registry.register();
123126

124-
f()
127+
f(current_gcx)
125128
});
126129
}
127130

131+
let current_gcx = FromDyn::from(CurrentGcx::new());
132+
let current_gcx2 = current_gcx.clone();
133+
128134
let builder = rayon::ThreadPoolBuilder::new()
129135
.thread_name(|_| "rustc".to_string())
130136
.acquire_thread_handler(jobserver::acquire_thread)
131137
.release_thread_handler(jobserver::release_thread)
132138
.num_threads(threads)
133-
.deadlock_handler(|| {
139+
.deadlock_handler(move || {
134140
// On deadlock, creates a new thread and forwards information in thread
135141
// locals to it. The new thread runs the deadlock handler.
136-
let query_map =
137-
FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs()));
142+
143+
// Get a `GlobalCtxt` reference from `CurrentGcx` as we cannot rely on having a
144+
// `TyCtxt` TLS reference here.
145+
let query_map = current_gcx2.access(|gcx| {
146+
tls::enter_context(&tls::ImplicitCtxt::new(gcx), || {
147+
tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs())
148+
})
149+
});
150+
let query_map = FromDyn::from(query_map);
138151
let registry = rayon_core::Registry::current();
139152
thread::Builder::new()
140153
.name("rustc query cycle handler".to_string())
@@ -171,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
171184
})
172185
},
173186
// Run `f` on the first thread in the thread pool.
174-
move |pool: &rayon::ThreadPool| pool.install(f),
187+
move |pool: &rayon::ThreadPool| pool.install(|| f(current_gcx.into_inner())),
175188
)
176189
.unwrap()
177190
})

Diff for: compiler/rustc_middle/src/ty/context.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ 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+
use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal};
4344
#[cfg(parallel_compiler)]
4445
use rustc_data_structures::sync::{DynSend, DynSync};
4546
use rustc_data_structures::unord::UnordSet;
@@ -723,6 +724,8 @@ pub struct GlobalCtxt<'tcx> {
723724

724725
/// Stores memory for globals (statics/consts).
725726
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
727+
728+
current_gcx: CurrentGcx,
726729
}
727730

728731
impl<'tcx> GlobalCtxt<'tcx> {
@@ -733,6 +736,19 @@ impl<'tcx> GlobalCtxt<'tcx> {
733736
F: FnOnce(TyCtxt<'tcx>) -> R,
734737
{
735738
let icx = tls::ImplicitCtxt::new(self);
739+
740+
// Reset `current_gcx` to `None` when we exit.
741+
let _on_drop = defer(move || {
742+
*self.current_gcx.value.write() = None;
743+
});
744+
745+
// Set this `GlobalCtxt` as the current one.
746+
{
747+
let mut guard = self.current_gcx.value.write();
748+
assert!(guard.is_none(), "no `GlobalCtxt` is currently set");
749+
*guard = Some(self as *const _ as *const ());
750+
}
751+
736752
tls::enter_context(&icx, || f(icx.tcx))
737753
}
738754

@@ -741,6 +757,39 @@ impl<'tcx> GlobalCtxt<'tcx> {
741757
}
742758
}
743759

760+
/// This is used to get a reference to a `GlobalCtxt` if one is available.
761+
///
762+
/// This is needed to allow the deadlock handler access to `GlobalCtxt` to look for query cycles.
763+
/// It cannot use the `TLV` global because that's only guaranteed to be defined on the thread
764+
/// creating the `GlobalCtxt`. Other threads have access to the `TLV` only inside Rayon jobs, but
765+
/// the deadlock handler is not called inside such a job.
766+
#[derive(Clone)]
767+
pub struct CurrentGcx {
768+
/// This stores a pointer to a `GlobalCtxt`. This is set to `Some` inside `GlobalCtxt::enter`
769+
/// and reset to `None` when that function returns or unwinds.
770+
value: Lrc<RwLock<Option<*const ()>>>,
771+
}
772+
773+
#[cfg(parallel_compiler)]
774+
unsafe impl DynSend for CurrentGcx {}
775+
#[cfg(parallel_compiler)]
776+
unsafe impl DynSync for CurrentGcx {}
777+
778+
impl CurrentGcx {
779+
pub fn new() -> Self {
780+
Self { value: Lrc::new(RwLock::new(None)) }
781+
}
782+
783+
pub fn access<R>(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R {
784+
let read_guard = self.value.read();
785+
let gcx: *const GlobalCtxt<'_> = read_guard.unwrap() as *const _;
786+
// SAFETY: We hold the read lock for the `GlobalCtxt` pointer. That prevents
787+
// `GlobalCtxt::enter` from returning as it would first acquire the write lock.
788+
// This ensures the `GlobalCtxt` is live during `f`.
789+
f(unsafe { &*gcx })
790+
}
791+
}
792+
744793
impl<'tcx> TyCtxt<'tcx> {
745794
/// Expects a body and returns its codegen attributes.
746795
///
@@ -859,6 +908,7 @@ impl<'tcx> TyCtxt<'tcx> {
859908
query_kinds: &'tcx [DepKindStruct<'tcx>],
860909
query_system: QuerySystem<'tcx>,
861910
hooks: crate::hooks::Providers,
911+
current_gcx: CurrentGcx,
862912
) -> GlobalCtxt<'tcx> {
863913
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
864914
s.dcx().emit_fatal(err);
@@ -893,6 +943,7 @@ impl<'tcx> TyCtxt<'tcx> {
893943
canonical_param_env_cache: Default::default(),
894944
data_layout,
895945
alloc_map: Lock::new(interpret::AllocMap::new()),
946+
current_gcx,
896947
}
897948
}
898949

Diff for: compiler/rustc_middle/src/ty/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ pub use self::consts::{
8787
Const, ConstData, ConstInt, ConstKind, Expr, ScalarInt, UnevaluatedConst, ValTree,
8888
};
8989
pub use self::context::{
90-
tls, CtxtInterners, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,
91-
TyCtxtFeed,
90+
tls, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift,
91+
TyCtxt, TyCtxtFeed,
9292
};
9393
pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams};
9494
pub use self::list::List;

0 commit comments

Comments
 (0)