Skip to content

Commit 834b7e7

Browse files
committed
Streamline active job collection.
`collect_active_jobs_from_all_queries` takes a `require_complete` bool, and then some callers `expect` a full map result while others allow a partial map result. The end result is four possible combinations, but only three of them are used/make sense. This commit introduces `CollectActiveJobsKind`, a three-value enum that describes the three sensible combinations, and rewrites `collect_active_jobs_from_all_queries` around it. This makes it and its call sites much clearer, and removes the weird `Option<()>` and `Result<QueryJobMap, QueryJobMap>` return types. Other changes of note. - `active` is removed. The comment about `make_frame` is out of date, and `create_deferred_query_stack_frame` *is* safe to call with the query state locked. - When shard locking failure is allowed, collection no longer stops on the first failed shard.
1 parent eaf4e74 commit 834b7e7

5 files changed

Lines changed: 66 additions & 62 deletions

File tree

compiler/rustc_interface/src/util.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_data_structures::sync;
1818
use rustc_metadata::{DylibError, EncodedMetadata, load_symbol_from_dylib};
1919
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
2020
use rustc_middle::ty::{CurrentGcx, TyCtxt};
21-
use rustc_query_impl::collect_active_jobs_from_all_queries;
21+
use rustc_query_impl::{CollectActiveJobsKind, collect_active_jobs_from_all_queries};
2222
use rustc_session::config::{
2323
Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple,
2424
};
@@ -253,9 +253,11 @@ internal compiler error: query cycle handler thread panicked, aborting process";
253253
unsafe { &*(session_globals as *const SessionGlobals) },
254254
|| {
255255
// Ensure there were no errors collecting all active jobs.
256-
// We need the complete map to ensure we find a cycle to break.
257-
collect_active_jobs_from_all_queries(tcx, false).expect(
258-
"failed to collect active queries in deadlock handler",
256+
// We need the complete map to ensure we find a cycle to
257+
// break.
258+
collect_active_jobs_from_all_queries(
259+
tcx,
260+
CollectActiveJobsKind::FullNoContention,
259261
)
260262
},
261263
);

compiler/rustc_query_impl/src/execution.rs

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_middle::query::{
1515
use rustc_middle::ty::TyCtxt;
1616
use rustc_middle::verify_ich::incremental_verify_ich;
1717
use rustc_span::{DUMMY_SP, Span};
18+
use tracing::warn;
1819

1920
use crate::dep_graph::{DepNode, DepNodeIndex};
2021
use crate::for_each_query_vtable;
@@ -30,6 +31,21 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
3031
state.active.lock_shards().all(|shard| shard.is_empty())
3132
}
3233

34+
#[derive(Clone, Copy)]
35+
pub enum CollectActiveJobsKind {
36+
/// We need the full query job map, and we are willing to wait to obtain the query state
37+
/// shard lock(s).
38+
Full,
39+
40+
/// We need the full query job map, and we shouldn't need to wait to obtain the shard lock(s),
41+
/// because we are in a place where nothing else could hold the shard lock(s).
42+
FullNoContention,
43+
44+
/// We can get by without the full query job map, so we won't bother waiting to obtain the
45+
/// shard lock(s) if they're not already unlocked.
46+
PartialAllowed,
47+
}
48+
3349
/// Returns a map of currently active query jobs, collected from all queries.
3450
///
3551
/// If `require_complete` is `true`, this function locks all shards of the
@@ -42,19 +58,15 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
4258
/// complete map is needed and no deadlock is possible at this call site.
4359
pub fn collect_active_jobs_from_all_queries<'tcx>(
4460
tcx: TyCtxt<'tcx>,
45-
require_complete: bool,
46-
) -> Result<QueryJobMap<'tcx>, QueryJobMap<'tcx>> {
47-
let mut job_map_out = QueryJobMap::default();
48-
let mut complete = true;
61+
collect_kind: CollectActiveJobsKind,
62+
) -> QueryJobMap<'tcx> {
63+
let mut job_map = QueryJobMap::default();
4964

5065
for_each_query_vtable!(ALL, tcx, |query| {
51-
let res = gather_active_jobs(query, require_complete, &mut job_map_out);
52-
if res.is_none() {
53-
complete = false;
54-
}
66+
gather_active_jobs(query, collect_kind, &mut job_map);
5567
});
5668

57-
if complete { Ok(job_map_out) } else { Err(job_map_out) }
69+
job_map
5870
}
5971

6072
/// Internal plumbing for collecting the set of active jobs for this query.
@@ -64,59 +76,49 @@ pub fn collect_active_jobs_from_all_queries<'tcx>(
6476
/// (We arbitrarily use the word "gather" when collecting the jobs for
6577
/// each individual query, so that we have distinct function names to
6678
/// grep for.)
79+
///
80+
/// Aborts if jobs can't be gathered as specified by `collect_kind`.
6781
fn gather_active_jobs<'tcx, C>(
6882
query: &'tcx QueryVTable<'tcx, C>,
69-
require_complete: bool,
70-
job_map_out: &mut QueryJobMap<'tcx>, // Out-param; job info is gathered into this map
71-
) -> Option<()>
72-
where
83+
collect_kind: CollectActiveJobsKind,
84+
job_map: &mut QueryJobMap<'tcx>,
85+
) where
7386
C: QueryCache<Key: QueryKey + DynSend + DynSync>,
7487
QueryVTable<'tcx, C>: DynSync,
7588
{
76-
let mut active = Vec::new();
77-
78-
// Helper to gather active jobs from a single shard.
7989
let mut gather_shard_jobs = |shard: &HashTable<(C::Key, ActiveKeyStatus<'tcx>)>| {
80-
for (k, v) in shard.iter() {
81-
if let ActiveKeyStatus::Started(ref job) = *v {
82-
active.push((*k, job.clone()));
90+
for (key, status) in shard.iter() {
91+
if let ActiveKeyStatus::Started(job) = status {
92+
// This function is safe to call with the shard locked because it is very simple.
93+
let frame = crate::plumbing::create_query_stack_frame(query, *key);
94+
job_map.insert(job.id, QueryJobInfo { frame, job: job.clone() });
8395
}
8496
}
8597
};
8698

87-
// Lock shards and gather jobs from each shard.
88-
if require_complete {
89-
for shard in query.state.active.lock_shards() {
90-
gather_shard_jobs(&shard);
99+
match collect_kind {
100+
CollectActiveJobsKind::Full => {
101+
for shard in query.state.active.lock_shards() {
102+
gather_shard_jobs(&shard);
103+
}
91104
}
92-
} else {
93-
// We use try_lock_shards here since we are called from the
94-
// deadlock handler, and this shouldn't be locked.
95-
for shard in query.state.active.try_lock_shards() {
96-
// This can be called during unwinding, and the function has a `try_`-prefix, so
97-
// don't `unwrap()` here, just manually check for `None` and do best-effort error
98-
// reporting.
99-
match shard {
100-
None => {
101-
tracing::warn!(
102-
"Failed to collect active jobs for query with name `{}`!",
103-
query.name
104-
);
105-
return None;
105+
CollectActiveJobsKind::FullNoContention => {
106+
for shard in query.state.active.try_lock_shards() {
107+
match shard {
108+
Some(shard) => gather_shard_jobs(&shard),
109+
None => panic!("Failed to collect active jobs for query `{}`!", query.name),
110+
}
111+
}
112+
}
113+
CollectActiveJobsKind::PartialAllowed => {
114+
for shard in query.state.active.try_lock_shards() {
115+
match shard {
116+
Some(shard) => gather_shard_jobs(&shard),
117+
None => warn!("Failed to collect active jobs for query `{}`!", query.name),
106118
}
107-
Some(shard) => gather_shard_jobs(&shard),
108119
}
109120
}
110121
}
111-
112-
// Call `make_frame` while we're not holding a `state.active` lock as `make_frame` may call
113-
// queries leading to a deadlock.
114-
for (key, job) in active {
115-
let frame = crate::plumbing::create_query_stack_frame(query, key);
116-
job_map_out.insert(job.id, QueryJobInfo { frame, job });
117-
}
118-
119-
Some(())
120122
}
121123

122124
#[cold]
@@ -223,11 +225,10 @@ fn cycle_error<'tcx, C: QueryCache>(
223225
try_execute: QueryJobId,
224226
span: Span,
225227
) -> (C::Value, Option<DepNodeIndex>) {
226-
// Ensure there was no errors collecting all active jobs.
228+
// Ensure there were no errors collecting all active jobs.
227229
// We need the complete map to ensure we find a cycle to break.
228-
let job_map = collect_active_jobs_from_all_queries(tcx, false)
229-
.ok()
230-
.expect("failed to collect active queries");
230+
let job_map =
231+
collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::FullNoContention);
231232

232233
let error = find_cycle_in_stack(try_execute, job_map, &current_query_job(), span);
233234
(mk_cycle(query, tcx, key, error), None)

compiler/rustc_query_impl/src/job.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::query::{
1212
use rustc_middle::ty::TyCtxt;
1313
use rustc_span::{DUMMY_SP, Span};
1414

15-
use crate::execution::collect_active_jobs_from_all_queries;
15+
use crate::{CollectActiveJobsKind, collect_active_jobs_from_all_queries};
1616

1717
/// Map from query job IDs to job information collected by
1818
/// `collect_active_jobs_from_all_queries`.
@@ -383,8 +383,7 @@ pub fn print_query_stack<'tcx>(
383383
let mut count_total = 0;
384384

385385
// Make use of a partial query job map if we fail to take locks collecting active queries.
386-
let job_map: QueryJobMap<'_> = collect_active_jobs_from_all_queries(tcx, false)
387-
.unwrap_or_else(|partial_job_map| partial_job_map);
386+
let job_map = collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::PartialAllowed);
388387

389388
if let Some(ref mut file) = file {
390389
let _ = writeln!(file, "\n\nquery stack during panic:");

compiler/rustc_query_impl/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_middle::ty::TyCtxt;
1818
use rustc_span::Span;
1919

2020
pub use crate::dep_kind_vtables::make_dep_kind_vtables;
21-
pub use crate::execution::collect_active_jobs_from_all_queries;
21+
pub use crate::execution::{CollectActiveJobsKind, collect_active_jobs_from_all_queries};
2222
pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack};
2323

2424
#[macro_use]

compiler/rustc_query_impl/src/plumbing.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ use rustc_span::def_id::LOCAL_CRATE;
2929
use crate::error::{QueryOverflow, QueryOverflowNote};
3030
use crate::execution::{all_inactive, force_query};
3131
use crate::job::find_dep_kind_root;
32-
use crate::{GetQueryVTable, collect_active_jobs_from_all_queries, for_each_query_vtable};
32+
use crate::{
33+
CollectActiveJobsKind, GetQueryVTable, collect_active_jobs_from_all_queries,
34+
for_each_query_vtable,
35+
};
3336

3437
fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) {
35-
let job_map =
36-
collect_active_jobs_from_all_queries(tcx, true).expect("failed to collect active queries");
38+
let job_map = collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::Full);
3739
let (info, depth) = find_dep_kind_root(job, job_map);
3840

3941
let suggested_limit = match tcx.recursion_limit() {

0 commit comments

Comments
 (0)