Skip to content

Commit

Permalink
Revive GCX_PTR
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoxc committed Aug 31, 2023
1 parent 26089ba commit db47bee
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4033,6 +4033,7 @@ dependencies = [
"rustc_span",
"rustc_target",
"rustc_type_ir",
"scoped-tls",
"smallvec",
"thin-vec",
"tracing",
Expand Down
30 changes: 23 additions & 7 deletions compiler/rustc_interface/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
f: F,
) -> R {
use rustc_data_structures::{jobserver, sync::FromDyn};
use rustc_middle::ty::tls;
use rustc_middle::ty::tls::{self, GcxPtr};
use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::{deadlock, QueryContext};

Expand All @@ -208,18 +208,34 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
.deadlock_handler(|| {
// 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")
}));
let query_map = FromDyn::from({
// Get a GlobalCtxt reference from GCX_PTR as we cannot rely on having a TyCtxt TLS
// reference here.
// SAFETY: No thread will end the lifetime of `GlobalCtxt` as they're deadlocked
// and won't resume until the `deadlock` call.
unsafe {
tls::GCX_PTR.with(|gcx_ptr| {
gcx_ptr.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 registry = rayon_core::Registry::current();
thread::spawn(move || deadlock(query_map.into_inner(), &registry));
});
if let Some(size) = get_stack_size() {
builder = builder.stack_size(size);
}

let gcx_ptr = GcxPtr::new();

// We create the session globals on the main thread, then create the thread
// pool. Upon creation, each worker thread created gets a copy of the
// session globals in TLS. This is possible because `SessionGlobals` impls
Expand All @@ -235,7 +251,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
registry.register();

rustc_span::set_session_globals_then(session_globals.into_inner(), || {
thread.run()
tls::GCX_PTR.set(&gcx_ptr, || thread.run())
})
},
// Run `f` on the first thread in the thread pool.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_type_ir = { path = "../rustc_type_ir" }
scoped-tls = "1.0"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
thin-vec = "0.2.12"
tracing = "0.1"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ impl<'tcx> GlobalCtxt<'tcx> {
F: FnOnce(TyCtxt<'tcx>) -> R,
{
let icx = tls::ImplicitCtxt::new(self);
tls::enter_context(&icx, || f(icx.tcx))
tls::enter_global_context(&icx, || f(icx.tcx))
}
}

Expand Down
49 changes: 49 additions & 0 deletions compiler/rustc_middle/src/ty/context/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::{GlobalCtxt, TyCtxt};

use crate::dep_graph::TaskDepsRef;
use crate::query::plumbing::QueryJobId;
use rustc_data_structures::defer;
use rustc_data_structures::sync::{self, Lock};
use rustc_errors::Diagnostic;
#[cfg(not(parallel_compiler))]
Expand Down Expand Up @@ -153,3 +154,51 @@ where
{
with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
}

/// Enters `GlobalCtxt` by setting up the `GCX_PTR` pointer.
pub fn enter_global_context<'tcx, F, R>(context: &ImplicitCtxt<'_, 'tcx>, f: F) -> R
where
F: FnOnce() -> R,
{
if cfg!(parallel_compiler) && GCX_PTR.is_set() {
// Update `GCX_PTR` to indicate there's a `GlobalCtxt` available.
GCX_PTR.with(|gcx_ptr| {
let mut lock = gcx_ptr.value.lock();
assert!(lock.is_none());
*lock = Some(context.tcx.gcx as *const _ as *const ());
});
// Set `GCX_PTR` back to 0 when we exit.
let _on_drop = defer(move || {
GCX_PTR.with(|gcx_ptr| *gcx_ptr.value.lock() = None);
});
enter_context(context, f)
} else {
enter_context(context, f)
}
}

pub struct GcxPtr {
/// Stores a pointer to the `GlobalCtxt` if one is available.
/// This is used to access the `GlobalCtxt` in the deadlock handler given to Rayon.
value: Lock<Option<*const ()>>,
}

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

/// This accesses the GlobalCtxt.
///
/// Safety: The caller must ensure that the GlobalCtxt is live during `f`.
pub unsafe fn access<R>(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R {
let gcx_ptr: *const GlobalCtxt<'_> = self.value.lock().unwrap() as *const _;
f(unsafe { &*gcx_ptr })
}
}

unsafe impl Sync for GcxPtr {}

scoped_tls::scoped_thread_local! {
pub static GCX_PTR: GcxPtr
}

0 comments on commit db47bee

Please sign in to comment.