Skip to content

Commit e5690e7

Browse files
authored
Rollup merge of rust-lang#148777 - ywxt:depth_limit_error, r=petrochenkov
Lock shards while emitting depth limit error. Locking shards avoids collect_active_jobs isn't able to be completed during emitting depth limit error. fix rust-lang#142159 Zulip: https://rust-lang.zulipchat.com/#narrow/channel/187679-t-compiler.2Fparallel-rustc/topic/panic.20while.20depth_limit_error/with/554616169 cc `@Zoxc`
2 parents f61bfb0 + a4d0507 commit e5690e7

File tree

5 files changed

+47
-25
lines changed

5 files changed

+47
-25
lines changed

compiler/rustc_interface/src/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
252252
let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || {
253253
// Ensure there was no errors collecting all active jobs.
254254
// We need the complete map to ensure we find a cycle to break.
255-
QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler")
255+
QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler")
256256
});
257257
break_query_cycles(query_map, &registry);
258258
})

compiler/rustc_query_impl/src/plumbing.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,25 @@ impl<'tcx> QueryContext for QueryCtxt<'tcx> {
8888
tls::with_related_context(self.tcx, |icx| icx.query)
8989
}
9090

91-
/// Returns a query map representing active query jobs.
92-
/// It returns an incomplete map as an error if it fails
93-
/// to take locks.
91+
/// Returns a map of currently active query jobs.
92+
///
93+
/// If `require_complete` is `true`, this function locks all shards of the
94+
/// query results to produce a complete map, which always returns `Ok`.
95+
/// Otherwise, it may return an incomplete map as an error if any shard
96+
/// lock cannot be acquired.
97+
///
98+
/// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
99+
/// especially when called from within a deadlock handler, unless a
100+
/// complete map is needed and no deadlock is possible at this call site.
94101
fn collect_active_jobs(
95102
self,
103+
require_complete: bool,
96104
) -> Result<QueryMap<QueryStackDeferred<'tcx>>, QueryMap<QueryStackDeferred<'tcx>>> {
97105
let mut jobs = QueryMap::default();
98106
let mut complete = true;
99107

100-
for collect in super::TRY_COLLECT_ACTIVE_JOBS.iter() {
101-
if collect(self.tcx, &mut jobs).is_none() {
108+
for collect in super::COLLECT_ACTIVE_JOBS.iter() {
109+
if collect(self.tcx, &mut jobs, require_complete).is_none() {
102110
complete = false;
103111
}
104112
}
@@ -163,11 +171,7 @@ impl<'tcx> QueryContext for QueryCtxt<'tcx> {
163171
}
164172

165173
fn depth_limit_error(self, job: QueryJobId) {
166-
// FIXME: `collect_active_jobs` expects no locks to be held, which doesn't hold for this call.
167-
let query_map = match self.collect_active_jobs() {
168-
Ok(query_map) => query_map,
169-
Err(query_map) => query_map,
170-
};
174+
let query_map = self.collect_active_jobs(true).expect("failed to collect active queries");
171175
let (info, depth) = job.find_dep_kind_root(query_map);
172176

173177
let suggested_limit = match self.recursion_limit() {
@@ -731,19 +735,21 @@ macro_rules! define_queries {
731735
}
732736
}
733737

734-
pub(crate) fn try_collect_active_jobs<'tcx>(
738+
pub(crate) fn collect_active_jobs<'tcx>(
735739
tcx: TyCtxt<'tcx>,
736740
qmap: &mut QueryMap<QueryStackDeferred<'tcx>>,
741+
require_complete: bool,
737742
) -> Option<()> {
738743
let make_query = |tcx, key| {
739744
let kind = rustc_middle::dep_graph::dep_kinds::$name;
740745
let name = stringify!($name);
741746
$crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name)
742747
};
743-
let res = tcx.query_system.states.$name.try_collect_active_jobs(
748+
let res = tcx.query_system.states.$name.collect_active_jobs(
744749
tcx,
745750
make_query,
746751
qmap,
752+
require_complete,
747753
);
748754
// this can be called during unwinding, and the function has a `try_`-prefix, so
749755
// don't `unwrap()` here, just manually check for `None` and do best-effort error
@@ -814,10 +820,10 @@ macro_rules! define_queries {
814820

815821
// These arrays are used for iteration and can't be indexed by `DepKind`.
816822

817-
const TRY_COLLECT_ACTIVE_JOBS: &[
818-
for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<QueryStackDeferred<'tcx>>) -> Option<()>
823+
const COLLECT_ACTIVE_JOBS: &[
824+
for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<QueryStackDeferred<'tcx>>, bool) -> Option<()>
819825
] =
820-
&[$(query_impl::$name::try_collect_active_jobs),*];
826+
&[$(query_impl::$name::collect_active_jobs),*];
821827

822828
const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[
823829
for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache)

compiler/rustc_query_system/src/query/job.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ pub fn print_query_stack<Qcx: QueryContext>(
616616
let mut count_total = 0;
617617

618618
// Make use of a partial query map if we fail to take locks collecting active queries.
619-
let query_map = match qcx.collect_active_jobs() {
619+
let query_map = match qcx.collect_active_jobs(false) {
620620
Ok(query_map) => query_map,
621621
Err(query_map) => query_map,
622622
};

compiler/rustc_query_system/src/query/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ pub trait QueryContext: HasDepContext {
161161
/// Get the query information from the TLS context.
162162
fn current_query_job(self) -> Option<QueryJobId>;
163163

164-
fn collect_active_jobs(self) -> Result<QueryMap<Self::QueryInfo>, QueryMap<Self::QueryInfo>>;
164+
fn collect_active_jobs(
165+
self,
166+
require_complete: bool,
167+
) -> Result<QueryMap<Self::QueryInfo>, QueryMap<Self::QueryInfo>>;
165168

166169
fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra;
167170

compiler/rustc_query_system/src/query/plumbing.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ use std::fmt::Debug;
77
use std::hash::Hash;
88
use std::mem;
99

10+
use hashbrown::HashTable;
1011
use hashbrown::hash_table::Entry;
1112
use rustc_data_structures::fingerprint::Fingerprint;
1213
use rustc_data_structures::sharded::{self, Sharded};
1314
use rustc_data_structures::stack::ensure_sufficient_stack;
15+
use rustc_data_structures::sync::LockGuard;
1416
use rustc_data_structures::{outline, sync};
1517
use rustc_errors::{Diag, FatalError, StashKey};
1618
use rustc_span::{DUMMY_SP, Span};
@@ -63,22 +65,33 @@ where
6365
self.active.lock_shards().all(|shard| shard.is_empty())
6466
}
6567

66-
pub fn try_collect_active_jobs<Qcx: Copy>(
68+
pub fn collect_active_jobs<Qcx: Copy>(
6769
&self,
6870
qcx: Qcx,
6971
make_query: fn(Qcx, K) -> QueryStackFrame<I>,
7072
jobs: &mut QueryMap<I>,
73+
require_complete: bool,
7174
) -> Option<()> {
7275
let mut active = Vec::new();
7376

74-
// We use try_lock_shards here since we are called from the
75-
// deadlock handler, and this shouldn't be locked.
76-
for shard in self.active.try_lock_shards() {
77-
for (k, v) in shard?.iter() {
77+
let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult<I>)>>| {
78+
for (k, v) in iter.iter() {
7879
if let QueryResult::Started(ref job) = *v {
79-
active.push((*k, (*job).clone()));
80+
active.push((*k, job.clone()));
8081
}
8182
}
83+
};
84+
85+
if require_complete {
86+
for shard in self.active.lock_shards() {
87+
collect(shard);
88+
}
89+
} else {
90+
// We use try_lock_shards here since we are called from the
91+
// deadlock handler, and this shouldn't be locked.
92+
for shard in self.active.try_lock_shards() {
93+
collect(shard?);
94+
}
8295
}
8396

8497
// Call `make_query` while we're not holding a `self.active` lock as `make_query` may call
@@ -271,7 +284,7 @@ where
271284
{
272285
// Ensure there was no errors collecting all active jobs.
273286
// We need the complete map to ensure we find a cycle to break.
274-
let query_map = qcx.collect_active_jobs().ok().expect("failed to collect active queries");
287+
let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries");
275288

276289
let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span);
277290
(mk_cycle(query, qcx, error.lift(qcx)), None)

0 commit comments

Comments
 (0)