Skip to content

Commit f4cecad

Browse files
committed
Revive GCX_PTR
1 parent 6cc1898 commit f4cecad

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
@@ -4051,6 +4051,7 @@ dependencies = [
40514051
"rustc_span",
40524052
"rustc_target",
40534053
"rustc_type_ir",
4054+
"scoped-tls",
40544055
"smallvec",
40554056
"thin-vec",
40564057
"tracing",

compiler/rustc_interface/src/util.rs

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

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

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

239255
rustc_span::set_session_globals_then(session_globals.into_inner(), || {
240-
thread.run()
256+
tls::GCX_PTR.set(&gcx_ptr, || thread.run())
241257
})
242258
},
243259
// 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
@@ -592,7 +592,7 @@ impl<'tcx> GlobalCtxt<'tcx> {
592592
F: FnOnce(TyCtxt<'tcx>) -> R,
593593
{
594594
let icx = tls::ImplicitCtxt::new(self);
595-
tls::enter_context(&icx, || f(icx.tcx))
595+
tls::enter_global_context(&icx, || f(icx.tcx))
596596
}
597597
}
598598

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)