Skip to content

Commit db47bee

Browse files
committed
Revive GCX_PTR
1 parent 26089ba commit db47bee

File tree

5 files changed

+75
-8
lines changed

5 files changed

+75
-8
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4033,6 +4033,7 @@ dependencies = [
40334033
"rustc_span",
40344034
"rustc_target",
40354035
"rustc_type_ir",
4036+
"scoped-tls",
40364037
"smallvec",
40374038
"thin-vec",
40384039
"tracing",

compiler/rustc_interface/src/util.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
185185
f: F,
186186
) -> R {
187187
use rustc_data_structures::{jobserver, sync::FromDyn};
188-
use rustc_middle::ty::tls;
188+
use rustc_middle::ty::tls::{self, GcxPtr};
189189
use rustc_query_impl::QueryCtxt;
190190
use rustc_query_system::query::{deadlock, QueryContext};
191191

@@ -208,18 +208,34 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
208208
.deadlock_handler(|| {
209209
// On deadlock, creates a new thread and forwards information in thread
210210
// locals to it. The new thread runs the deadlock handler.
211-
let query_map = FromDyn::from(tls::with(|tcx| {
212-
QueryCtxt::new(tcx)
213-
.try_collect_active_jobs()
214-
.expect("active jobs shouldn't be locked in deadlock handler")
215-
}));
211+
let query_map = FromDyn::from({
212+
// Get a GlobalCtxt reference from GCX_PTR as we cannot rely on having a TyCtxt TLS
213+
// reference here.
214+
// SAFETY: No thread will end the lifetime of `GlobalCtxt` as they're deadlocked
215+
// and won't resume until the `deadlock` call.
216+
unsafe {
217+
tls::GCX_PTR.with(|gcx_ptr| {
218+
gcx_ptr.access(|gcx| {
219+
tls::enter_context(&tls::ImplicitCtxt::new(gcx), || {
220+
tls::with(|tcx| {
221+
QueryCtxt::new(tcx).try_collect_active_jobs().expect(
222+
"active jobs shouldn't be locked in deadlock handler",
223+
)
224+
})
225+
})
226+
})
227+
})
228+
}
229+
});
216230
let registry = rayon_core::Registry::current();
217231
thread::spawn(move || deadlock(query_map.into_inner(), &registry));
218232
});
219233
if let Some(size) = get_stack_size() {
220234
builder = builder.stack_size(size);
221235
}
222236

237+
let gcx_ptr = GcxPtr::new();
238+
223239
// We create the session globals on the main thread, then create the thread
224240
// pool. Upon creation, each worker thread created gets a copy of the
225241
// session globals in TLS. This is possible because `SessionGlobals` impls
@@ -235,7 +251,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
235251
registry.register();
236252

237253
rustc_span::set_session_globals_then(session_globals.into_inner(), || {
238-
thread.run()
254+
tls::GCX_PTR.set(&gcx_ptr, || thread.run())
239255
})
240256
},
241257
// Run `f` on the first thread in the thread pool.

compiler/rustc_middle/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ rustc_session = { path = "../rustc_session" }
3535
rustc_span = { path = "../rustc_span" }
3636
rustc_target = { path = "../rustc_target" }
3737
rustc_type_ir = { path = "../rustc_type_ir" }
38+
scoped-tls = "1.0"
3839
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
3940
thin-vec = "0.2.12"
4041
tracing = "0.1"

compiler/rustc_middle/src/ty/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ impl<'tcx> GlobalCtxt<'tcx> {
590590
F: FnOnce(TyCtxt<'tcx>) -> R,
591591
{
592592
let icx = tls::ImplicitCtxt::new(self);
593-
tls::enter_context(&icx, || f(icx.tcx))
593+
tls::enter_global_context(&icx, || f(icx.tcx))
594594
}
595595
}
596596

compiler/rustc_middle/src/ty/context/tls.rs

+49
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::{GlobalCtxt, TyCtxt};
22

33
use crate::dep_graph::TaskDepsRef;
44
use crate::query::plumbing::QueryJobId;
5+
use rustc_data_structures::defer;
56
use rustc_data_structures::sync::{self, Lock};
67
use rustc_errors::Diagnostic;
78
#[cfg(not(parallel_compiler))]
@@ -153,3 +154,51 @@ where
153154
{
154155
with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
155156
}
157+
158+
/// Enters `GlobalCtxt` by setting up the `GCX_PTR` pointer.
159+
pub fn enter_global_context<'tcx, F, R>(context: &ImplicitCtxt<'_, 'tcx>, f: F) -> R
160+
where
161+
F: FnOnce() -> R,
162+
{
163+
if cfg!(parallel_compiler) && GCX_PTR.is_set() {
164+
// Update `GCX_PTR` to indicate there's a `GlobalCtxt` available.
165+
GCX_PTR.with(|gcx_ptr| {
166+
let mut lock = gcx_ptr.value.lock();
167+
assert!(lock.is_none());
168+
*lock = Some(context.tcx.gcx as *const _ as *const ());
169+
});
170+
// Set `GCX_PTR` back to 0 when we exit.
171+
let _on_drop = defer(move || {
172+
GCX_PTR.with(|gcx_ptr| *gcx_ptr.value.lock() = None);
173+
});
174+
enter_context(context, f)
175+
} else {
176+
enter_context(context, f)
177+
}
178+
}
179+
180+
pub struct GcxPtr {
181+
/// Stores a pointer to the `GlobalCtxt` if one is available.
182+
/// This is used to access the `GlobalCtxt` in the deadlock handler given to Rayon.
183+
value: Lock<Option<*const ()>>,
184+
}
185+
186+
impl GcxPtr {
187+
pub fn new() -> Self {
188+
GcxPtr { value: Lock::new(None) }
189+
}
190+
191+
/// This accesses the GlobalCtxt.
192+
///
193+
/// Safety: The caller must ensure that the GlobalCtxt is live during `f`.
194+
pub unsafe fn access<R>(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R {
195+
let gcx_ptr: *const GlobalCtxt<'_> = self.value.lock().unwrap() as *const _;
196+
f(unsafe { &*gcx_ptr })
197+
}
198+
}
199+
200+
unsafe impl Sync for GcxPtr {}
201+
202+
scoped_tls::scoped_thread_local! {
203+
pub static GCX_PTR: GcxPtr
204+
}

0 commit comments

Comments
 (0)