Auto merge of #140145 - Zoxc:job-server-proxy, r=SparrowLii
Add a jobserver proxy to ensure at least one token is always held This adds a jobserver proxy to ensure at least one token is always held by `rustc`. Currently with `-Z threads` `rustc` can temporarily give up all its tokens, causing `cargo` to spawn additional `rustc` instances beyond the job limit. The current behavior causes an issue with `cargo fix` which has a global lock preventing concurrent `rustc` instances, but it also holds a jobserver token, causing a deadlock when `rustc` gives up its token. That is fixed by this PR. Fixes https://github.com/rust-lang/rust/issues/67385. Fixes https://github.com/rust-lang/rust/issues/133873. Fixes https://github.com/rust-lang/rust/issues/140093.
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
use std::sync::{LazyLock, OnceLock};
|
use std::sync::{Arc, LazyLock, OnceLock};
|
||||||
|
|
||||||
pub use jobserver_crate::{Acquired, Client, HelperThread};
|
pub use jobserver_crate::{Acquired, Client, HelperThread};
|
||||||
use jobserver_crate::{FromEnv, FromEnvErrorKind};
|
use jobserver_crate::{FromEnv, FromEnvErrorKind};
|
||||||
|
use parking_lot::{Condvar, Mutex};
|
||||||
|
|
||||||
// We can only call `from_env_ext` once per process
|
// We can only call `from_env_ext` once per process
|
||||||
|
|
||||||
@@ -71,10 +72,93 @@ pub fn client() -> Client {
|
|||||||
GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone()
|
GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acquire_thread() {
|
struct ProxyData {
|
||||||
GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).acquire_raw().ok();
|
/// The number of tokens assigned to threads.
|
||||||
|
/// If this is 0, a single token is still assigned to this process, but is unused.
|
||||||
|
used: u16,
|
||||||
|
|
||||||
|
/// The number of threads requesting a token
|
||||||
|
pending: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release_thread() {
|
/// This is a jobserver proxy used to ensure that we hold on to at least one token.
|
||||||
GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).release_raw().ok();
|
pub struct Proxy {
|
||||||
|
client: Client,
|
||||||
|
data: Mutex<ProxyData>,
|
||||||
|
|
||||||
|
/// Threads which are waiting on a token will wait on this.
|
||||||
|
wake_pending: Condvar,
|
||||||
|
|
||||||
|
helper: OnceLock<HelperThread>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Proxy {
|
||||||
|
pub fn new() -> Arc<Self> {
|
||||||
|
let proxy = Arc::new(Proxy {
|
||||||
|
client: client(),
|
||||||
|
data: Mutex::new(ProxyData { used: 1, pending: 0 }),
|
||||||
|
wake_pending: Condvar::new(),
|
||||||
|
helper: OnceLock::new(),
|
||||||
|
});
|
||||||
|
let proxy_ = Arc::clone(&proxy);
|
||||||
|
let helper = proxy
|
||||||
|
.client
|
||||||
|
.clone()
|
||||||
|
.into_helper_thread(move |token| {
|
||||||
|
if let Ok(token) = token {
|
||||||
|
let mut data = proxy_.data.lock();
|
||||||
|
if data.pending > 0 {
|
||||||
|
// Give the token to a waiting thread
|
||||||
|
token.drop_without_releasing();
|
||||||
|
assert!(data.used > 0);
|
||||||
|
data.used += 1;
|
||||||
|
data.pending -= 1;
|
||||||
|
proxy_.wake_pending.notify_one();
|
||||||
|
} else {
|
||||||
|
// The token is no longer needed, drop it.
|
||||||
|
drop(data);
|
||||||
|
drop(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("failed to create helper thread");
|
||||||
|
proxy.helper.set(helper).unwrap();
|
||||||
|
proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire_thread(&self) {
|
||||||
|
let mut data = self.data.lock();
|
||||||
|
|
||||||
|
if data.used == 0 {
|
||||||
|
// There was a free token around. This can
|
||||||
|
// happen when all threads release their token.
|
||||||
|
assert_eq!(data.pending, 0);
|
||||||
|
data.used += 1;
|
||||||
|
} else {
|
||||||
|
// Request a token from the helper thread. We can't directly use `acquire_raw`
|
||||||
|
// as we also need to be able to wait for the final token in the process which
|
||||||
|
// does not get a corresponding `release_raw` call.
|
||||||
|
self.helper.get().unwrap().request_token();
|
||||||
|
data.pending += 1;
|
||||||
|
self.wake_pending.wait(&mut data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release_thread(&self) {
|
||||||
|
let mut data = self.data.lock();
|
||||||
|
|
||||||
|
if data.pending > 0 {
|
||||||
|
// Give the token to a waiting thread
|
||||||
|
data.pending -= 1;
|
||||||
|
self.wake_pending.notify_one();
|
||||||
|
} else {
|
||||||
|
data.used -= 1;
|
||||||
|
|
||||||
|
// Release the token unless it's the last one in the process
|
||||||
|
if data.used > 0 {
|
||||||
|
drop(data);
|
||||||
|
self.client.release_raw().ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ macro_rules! already_send {
|
|||||||
// These structures are already `Send`.
|
// These structures are already `Send`.
|
||||||
already_send!(
|
already_send!(
|
||||||
[std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File]
|
[std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File]
|
||||||
[rustc_arena::DroplessArena][crate::memmap::Mmap][crate::profiling::SelfProfiler]
|
[rustc_arena::DroplessArena][jobserver_crate::Client][jobserver_crate::HelperThread]
|
||||||
[crate::owned_slice::OwnedSlice]
|
[crate::memmap::Mmap][crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice]
|
||||||
);
|
);
|
||||||
|
|
||||||
macro_rules! impl_dyn_send {
|
macro_rules! impl_dyn_send {
|
||||||
@@ -134,8 +134,8 @@ macro_rules! already_sync {
|
|||||||
already_sync!(
|
already_sync!(
|
||||||
[std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8]
|
[std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8]
|
||||||
[std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File]
|
[std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File]
|
||||||
[jobserver_crate::Client][crate::memmap::Mmap][crate::profiling::SelfProfiler]
|
[jobserver_crate::Client][jobserver_crate::HelperThread][crate::memmap::Mmap]
|
||||||
[crate::owned_slice::OwnedSlice]
|
[crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use portable AtomicU64 for targets without native 64-bit atomics
|
// Use portable AtomicU64 for targets without native 64-bit atomics
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
use rustc_ast::{LitKind, MetaItemKind, token};
|
use rustc_ast::{LitKind, MetaItemKind, token};
|
||||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::jobserver;
|
use rustc_data_structures::jobserver::{self, Proxy};
|
||||||
use rustc_data_structures::stable_hasher::StableHasher;
|
use rustc_data_structures::stable_hasher::StableHasher;
|
||||||
use rustc_errors::registry::Registry;
|
use rustc_errors::registry::Registry;
|
||||||
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
|
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
|
||||||
@@ -40,7 +40,12 @@ pub struct Compiler {
|
|||||||
pub sess: Session,
|
pub sess: Session,
|
||||||
pub codegen_backend: Box<dyn CodegenBackend>,
|
pub codegen_backend: Box<dyn CodegenBackend>,
|
||||||
pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
|
pub(crate) override_queries: Option<fn(&Session, &mut Providers)>,
|
||||||
|
|
||||||
|
/// A reference to the current `GlobalCtxt` which we pass on to `GlobalCtxt`.
|
||||||
pub(crate) current_gcx: CurrentGcx,
|
pub(crate) current_gcx: CurrentGcx,
|
||||||
|
|
||||||
|
/// A jobserver reference which we pass on to `GlobalCtxt`.
|
||||||
|
pub(crate) jobserver_proxy: Arc<Proxy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
|
/// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
|
||||||
@@ -415,7 +420,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||||||
config.opts.unstable_opts.threads,
|
config.opts.unstable_opts.threads,
|
||||||
&config.extra_symbols,
|
&config.extra_symbols,
|
||||||
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind },
|
SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind },
|
||||||
|current_gcx| {
|
|current_gcx, jobserver_proxy| {
|
||||||
// The previous `early_dcx` can't be reused here because it doesn't
|
// The previous `early_dcx` can't be reused here because it doesn't
|
||||||
// impl `Send`. Creating a new one is fine.
|
// impl `Send`. Creating a new one is fine.
|
||||||
let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
|
let early_dcx = EarlyDiagCtxt::new(config.opts.error_format);
|
||||||
@@ -511,6 +516,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
|
|||||||
codegen_backend,
|
codegen_backend,
|
||||||
override_queries: config.override_queries,
|
override_queries: config.override_queries,
|
||||||
current_gcx,
|
current_gcx,
|
||||||
|
jobserver_proxy,
|
||||||
};
|
};
|
||||||
|
|
||||||
// There are two paths out of `f`.
|
// There are two paths out of `f`.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::{env, fs, iter};
|
|||||||
|
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||||
|
use rustc_data_structures::jobserver::Proxy;
|
||||||
use rustc_data_structures::parallel;
|
use rustc_data_structures::parallel;
|
||||||
use rustc_data_structures::steal::Steal;
|
use rustc_data_structures::steal::Steal;
|
||||||
use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
|
use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
|
||||||
@@ -841,12 +842,13 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
|
|||||||
dyn for<'tcx> FnOnce(
|
dyn for<'tcx> FnOnce(
|
||||||
&'tcx Session,
|
&'tcx Session,
|
||||||
CurrentGcx,
|
CurrentGcx,
|
||||||
|
Arc<Proxy>,
|
||||||
&'tcx OnceLock<GlobalCtxt<'tcx>>,
|
&'tcx OnceLock<GlobalCtxt<'tcx>>,
|
||||||
&'tcx WorkerLocal<Arena<'tcx>>,
|
&'tcx WorkerLocal<Arena<'tcx>>,
|
||||||
&'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
|
&'tcx WorkerLocal<rustc_hir::Arena<'tcx>>,
|
||||||
F,
|
F,
|
||||||
) -> T,
|
) -> T,
|
||||||
> = Box::new(move |sess, current_gcx, gcx_cell, arena, hir_arena, f| {
|
> = Box::new(move |sess, current_gcx, jobserver_proxy, gcx_cell, arena, hir_arena, f| {
|
||||||
TyCtxt::create_global_ctxt(
|
TyCtxt::create_global_ctxt(
|
||||||
gcx_cell,
|
gcx_cell,
|
||||||
sess,
|
sess,
|
||||||
@@ -865,6 +867,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
|
|||||||
),
|
),
|
||||||
providers.hooks,
|
providers.hooks,
|
||||||
current_gcx,
|
current_gcx,
|
||||||
|
jobserver_proxy,
|
||||||
|tcx| {
|
|tcx| {
|
||||||
let feed = tcx.create_crate_num(stable_crate_id).unwrap();
|
let feed = tcx.create_crate_num(stable_crate_id).unwrap();
|
||||||
assert_eq!(feed.key(), LOCAL_CRATE);
|
assert_eq!(feed.key(), LOCAL_CRATE);
|
||||||
@@ -887,7 +890,15 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
inner(&compiler.sess, compiler.current_gcx.clone(), &gcx_cell, &arena, &hir_arena, f)
|
inner(
|
||||||
|
&compiler.sess,
|
||||||
|
compiler.current_gcx.clone(),
|
||||||
|
Arc::clone(&compiler.jobserver_proxy),
|
||||||
|
&gcx_cell,
|
||||||
|
&arena,
|
||||||
|
&hir_arena,
|
||||||
|
f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses.
|
/// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses.
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
|
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::OnceLock;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Arc, OnceLock};
|
||||||
use std::{env, iter, thread};
|
use std::{env, iter, thread};
|
||||||
|
|
||||||
use rustc_ast as ast;
|
use rustc_ast as ast;
|
||||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||||
|
use rustc_data_structures::jobserver::Proxy;
|
||||||
use rustc_data_structures::sync;
|
use rustc_data_structures::sync;
|
||||||
use rustc_metadata::{DylibError, load_symbol_from_dylib};
|
use rustc_metadata::{DylibError, load_symbol_from_dylib};
|
||||||
use rustc_middle::ty::CurrentGcx;
|
use rustc_middle::ty::CurrentGcx;
|
||||||
@@ -124,7 +125,7 @@ fn init_stack_size(early_dcx: &EarlyDiagCtxt) -> usize {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
|
fn run_in_thread_with_globals<F: FnOnce(CurrentGcx, Arc<Proxy>) -> R + Send, R: Send>(
|
||||||
thread_stack_size: usize,
|
thread_stack_size: usize,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
sm_inputs: SourceMapInputs,
|
sm_inputs: SourceMapInputs,
|
||||||
@@ -150,7 +151,7 @@ fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
|
|||||||
edition,
|
edition,
|
||||||
extra_symbols,
|
extra_symbols,
|
||||||
Some(sm_inputs),
|
Some(sm_inputs),
|
||||||
|| f(CurrentGcx::new()),
|
|| f(CurrentGcx::new(), Proxy::new()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -163,7 +164,10 @@ fn run_in_thread_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send, R: Send>(
|
pub(crate) fn run_in_thread_pool_with_globals<
|
||||||
|
F: FnOnce(CurrentGcx, Arc<Proxy>) -> R + Send,
|
||||||
|
R: Send,
|
||||||
|
>(
|
||||||
thread_builder_diag: &EarlyDiagCtxt,
|
thread_builder_diag: &EarlyDiagCtxt,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
threads: usize,
|
threads: usize,
|
||||||
@@ -173,8 +177,8 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
|
|||||||
) -> R {
|
) -> R {
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
use rustc_data_structures::defer;
|
||||||
use rustc_data_structures::sync::FromDyn;
|
use rustc_data_structures::sync::FromDyn;
|
||||||
use rustc_data_structures::{defer, jobserver};
|
|
||||||
use rustc_middle::ty::tls;
|
use rustc_middle::ty::tls;
|
||||||
use rustc_query_impl::QueryCtxt;
|
use rustc_query_impl::QueryCtxt;
|
||||||
use rustc_query_system::query::{QueryContext, break_query_cycles};
|
use rustc_query_system::query::{QueryContext, break_query_cycles};
|
||||||
@@ -189,11 +193,11 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
|
|||||||
edition,
|
edition,
|
||||||
sm_inputs,
|
sm_inputs,
|
||||||
extra_symbols,
|
extra_symbols,
|
||||||
|current_gcx| {
|
|current_gcx, jobserver_proxy| {
|
||||||
// Register the thread for use with the `WorkerLocal` type.
|
// Register the thread for use with the `WorkerLocal` type.
|
||||||
registry.register();
|
registry.register();
|
||||||
|
|
||||||
f(current_gcx)
|
f(current_gcx, jobserver_proxy)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -201,10 +205,14 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
|
|||||||
let current_gcx = FromDyn::from(CurrentGcx::new());
|
let current_gcx = FromDyn::from(CurrentGcx::new());
|
||||||
let current_gcx2 = current_gcx.clone();
|
let current_gcx2 = current_gcx.clone();
|
||||||
|
|
||||||
|
let proxy = Proxy::new();
|
||||||
|
|
||||||
|
let proxy_ = Arc::clone(&proxy);
|
||||||
|
let proxy__ = Arc::clone(&proxy);
|
||||||
let builder = rayon_core::ThreadPoolBuilder::new()
|
let builder = rayon_core::ThreadPoolBuilder::new()
|
||||||
.thread_name(|_| "rustc".to_string())
|
.thread_name(|_| "rustc".to_string())
|
||||||
.acquire_thread_handler(jobserver::acquire_thread)
|
.acquire_thread_handler(move || proxy_.acquire_thread())
|
||||||
.release_thread_handler(jobserver::release_thread)
|
.release_thread_handler(move || proxy__.release_thread())
|
||||||
.num_threads(threads)
|
.num_threads(threads)
|
||||||
.deadlock_handler(move || {
|
.deadlock_handler(move || {
|
||||||
// On deadlock, creates a new thread and forwards information in thread
|
// On deadlock, creates a new thread and forwards information in thread
|
||||||
@@ -268,7 +276,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce(CurrentGcx) -> R + Send,
|
|||||||
},
|
},
|
||||||
// Run `f` on the first thread in the thread pool.
|
// Run `f` on the first thread in the thread pool.
|
||||||
move |pool: &rayon_core::ThreadPool| {
|
move |pool: &rayon_core::ThreadPool| {
|
||||||
pool.install(|| f(current_gcx.into_inner()))
|
pool.install(|| f(current_gcx.into_inner(), proxy))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use rustc_data_structures::defer;
|
|||||||
use rustc_data_structures::fingerprint::Fingerprint;
|
use rustc_data_structures::fingerprint::Fingerprint;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::intern::Interned;
|
use rustc_data_structures::intern::Interned;
|
||||||
|
use rustc_data_structures::jobserver::Proxy;
|
||||||
use rustc_data_structures::profiling::SelfProfilerRef;
|
use rustc_data_structures::profiling::SelfProfilerRef;
|
||||||
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
|
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
|
||||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||||
@@ -1441,6 +1442,9 @@ pub struct GlobalCtxt<'tcx> {
|
|||||||
pub(crate) alloc_map: interpret::AllocMap<'tcx>,
|
pub(crate) alloc_map: interpret::AllocMap<'tcx>,
|
||||||
|
|
||||||
current_gcx: CurrentGcx,
|
current_gcx: CurrentGcx,
|
||||||
|
|
||||||
|
/// A jobserver reference used to release then acquire a token while waiting on a query.
|
||||||
|
pub jobserver_proxy: Arc<Proxy>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> GlobalCtxt<'tcx> {
|
impl<'tcx> GlobalCtxt<'tcx> {
|
||||||
@@ -1645,6 +1649,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
query_system: QuerySystem<'tcx>,
|
query_system: QuerySystem<'tcx>,
|
||||||
hooks: crate::hooks::Providers,
|
hooks: crate::hooks::Providers,
|
||||||
current_gcx: CurrentGcx,
|
current_gcx: CurrentGcx,
|
||||||
|
jobserver_proxy: Arc<Proxy>,
|
||||||
f: impl FnOnce(TyCtxt<'tcx>) -> T,
|
f: impl FnOnce(TyCtxt<'tcx>) -> T,
|
||||||
) -> T {
|
) -> T {
|
||||||
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
|
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
|
||||||
@@ -1679,6 +1684,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
data_layout,
|
data_layout,
|
||||||
alloc_map: interpret::AllocMap::new(),
|
alloc_map: interpret::AllocMap::new(),
|
||||||
current_gcx,
|
current_gcx,
|
||||||
|
jobserver_proxy,
|
||||||
});
|
});
|
||||||
|
|
||||||
// This is a separate function to work around a crash with parallel rustc (#135870)
|
// This is a separate function to work around a crash with parallel rustc (#135870)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
|
|
||||||
|
use rustc_data_structures::jobserver::Proxy;
|
||||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||||
use rustc_data_structures::sync::{DynSend, DynSync};
|
use rustc_data_structures::sync::{DynSend, DynSync};
|
||||||
use rustc_data_structures::unord::UnordMap;
|
use rustc_data_structures::unord::UnordMap;
|
||||||
@@ -69,6 +70,11 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
|
|||||||
impl<'tcx> QueryContext for QueryCtxt<'tcx> {
|
impl<'tcx> QueryContext for QueryCtxt<'tcx> {
|
||||||
type QueryInfo = QueryStackDeferred<'tcx>;
|
type QueryInfo = QueryStackDeferred<'tcx>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn jobserver_proxy(&self) -> &Proxy {
|
||||||
|
&*self.jobserver_proxy
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_job_id(self) -> QueryJobId {
|
fn next_job_id(self) -> QueryJobId {
|
||||||
QueryJobId(
|
QueryJobId(
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use parking_lot::{Condvar, Mutex};
|
use parking_lot::{Condvar, Mutex};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_data_structures::jobserver;
|
|
||||||
use rustc_errors::{Diag, DiagCtxtHandle};
|
use rustc_errors::{Diag, DiagCtxtHandle};
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
@@ -207,12 +206,13 @@ impl<I> QueryLatch<I> {
|
|||||||
/// Awaits for the query job to complete.
|
/// Awaits for the query job to complete.
|
||||||
pub(super) fn wait_on(
|
pub(super) fn wait_on(
|
||||||
&self,
|
&self,
|
||||||
|
qcx: impl QueryContext,
|
||||||
query: Option<QueryJobId>,
|
query: Option<QueryJobId>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<(), CycleError<I>> {
|
) -> Result<(), CycleError<I>> {
|
||||||
let waiter =
|
let waiter =
|
||||||
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
|
Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
|
||||||
self.wait_on_inner(&waiter);
|
self.wait_on_inner(qcx, &waiter);
|
||||||
// FIXME: Get rid of this lock. We have ownership of the QueryWaiter
|
// FIXME: Get rid of this lock. We have ownership of the QueryWaiter
|
||||||
// although another thread may still have a Arc reference so we cannot
|
// although another thread may still have a Arc reference so we cannot
|
||||||
// use Arc::get_mut
|
// use Arc::get_mut
|
||||||
@@ -224,7 +224,7 @@ impl<I> QueryLatch<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Awaits the caller on this latch by blocking the current thread.
|
/// Awaits the caller on this latch by blocking the current thread.
|
||||||
fn wait_on_inner(&self, waiter: &Arc<QueryWaiter<I>>) {
|
fn wait_on_inner(&self, qcx: impl QueryContext, waiter: &Arc<QueryWaiter<I>>) {
|
||||||
let mut info = self.info.lock();
|
let mut info = self.info.lock();
|
||||||
if !info.complete {
|
if !info.complete {
|
||||||
// We push the waiter on to the `waiters` list. It can be accessed inside
|
// We push the waiter on to the `waiters` list. It can be accessed inside
|
||||||
@@ -237,11 +237,12 @@ impl<I> QueryLatch<I> {
|
|||||||
// we have to be in the `wait` call. This is ensured by the deadlock handler
|
// we have to be in the `wait` call. This is ensured by the deadlock handler
|
||||||
// getting the self.info lock.
|
// getting the self.info lock.
|
||||||
rayon_core::mark_blocked();
|
rayon_core::mark_blocked();
|
||||||
jobserver::release_thread();
|
let proxy = qcx.jobserver_proxy();
|
||||||
|
proxy.release_thread();
|
||||||
waiter.condvar.wait(&mut info);
|
waiter.condvar.wait(&mut info);
|
||||||
// Release the lock before we potentially block in `acquire_thread`
|
// Release the lock before we potentially block in `acquire_thread`
|
||||||
drop(info);
|
drop(info);
|
||||||
jobserver::acquire_thread();
|
proxy.acquire_thread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ mod caches;
|
|||||||
pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
|
pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
use rustc_data_structures::jobserver::Proxy;
|
||||||
use rustc_data_structures::sync::{DynSend, DynSync};
|
use rustc_data_structures::sync::{DynSend, DynSync};
|
||||||
use rustc_errors::DiagInner;
|
use rustc_errors::DiagInner;
|
||||||
use rustc_hashes::Hash64;
|
use rustc_hashes::Hash64;
|
||||||
@@ -151,6 +152,10 @@ pub enum QuerySideEffect {
|
|||||||
pub trait QueryContext: HasDepContext {
|
pub trait QueryContext: HasDepContext {
|
||||||
type QueryInfo: Clone;
|
type QueryInfo: Clone;
|
||||||
|
|
||||||
|
/// Gets a jobserver reference which is used to release then acquire
|
||||||
|
/// a token while waiting on a query.
|
||||||
|
fn jobserver_proxy(&self) -> &Proxy;
|
||||||
|
|
||||||
fn next_job_id(self) -> QueryJobId;
|
fn next_job_id(self) -> QueryJobId;
|
||||||
|
|
||||||
/// Get the query information from the TLS context.
|
/// Get the query information from the TLS context.
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ where
|
|||||||
|
|
||||||
// With parallel queries we might just have to wait on some other
|
// With parallel queries we might just have to wait on some other
|
||||||
// thread.
|
// thread.
|
||||||
let result = latch.wait_on(current, span);
|
let result = latch.wait_on(qcx, current, span);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user