Skip to content

Commit cb7eef1

Browse files
committed
Streamline active job collection.
In `gather_active_jobs`: - Remove the `require_complete` parameter. We now always use `try_lock_shards` and let the caller abort on incompleteness - Remove `active`. The comment about `make_frame` is out of date, and `create_deferred_query_stack_frame` *is* safe to call with the query state locked. - Don't stop on the first failed shard; continue to get as much of the job map as possible. In `collect_active_jobs_from_all_queries`: - Remove the `require_complete` parameter. - Change the return type. Having the same thing in both the `Ok` and `Err` cases was strange.
1 parent b41f22d commit cb7eef1

4 files changed

Lines changed: 49 additions & 74 deletions

File tree

compiler/rustc_interface/src/util.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,13 @@ internal compiler error: query cycle handler thread panicked, aborting process";
254254
|| {
255255
// Ensure there were no errors collecting all active jobs.
256256
// We need the complete map to ensure we find a cycle to break.
257-
collect_active_jobs_from_all_queries(tcx, false).expect(
257+
let (job_map, collect_ok) =
258+
collect_active_jobs_from_all_queries(tcx);
259+
assert!(
260+
collect_ok,
258261
"failed to collect active queries in deadlock handler",
259-
)
262+
);
263+
job_map
260264
},
261265
);
262266
break_query_cycles(job_map, &registry);

compiler/rustc_query_impl/src/execution.rs

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::hash::Hash;
22
use std::mem;
33

4-
use rustc_data_structures::hash_table::{Entry, HashTable};
4+
use rustc_data_structures::hash_table::Entry;
55
use rustc_data_structures::stack::ensure_sufficient_stack;
66
use rustc_data_structures::sync::{DynSend, DynSync};
77
use rustc_data_structures::{outline, sharded, sync};
@@ -19,7 +19,9 @@ use rustc_span::{DUMMY_SP, Span};
1919
use crate::collect_active_jobs_from_all_queries;
2020
use crate::dep_graph::{DepNode, DepNodeIndex};
2121
use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle};
22-
use crate::plumbing::{current_query_job, next_job_id, start_query};
22+
use crate::plumbing::{
23+
create_deferred_query_stack_frame, current_query_job, next_job_id, start_query,
24+
};
2325

2426
#[inline]
2527
fn equivalent_key<K: Eq, V>(k: K) -> impl Fn(&(K, V)) -> bool {
@@ -42,6 +44,8 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
4244
}
4345

4446
/// Internal plumbing for collecting the set of active jobs for this query.
47+
/// Returns `false` if we failed to collect all jobs, which can happen if some
48+
/// query cache shards could not be locked.
4549
///
4650
/// Should only be called from `collect_active_jobs_from_all_queries`.
4751
///
@@ -51,57 +55,36 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
5155
pub(crate) fn gather_active_jobs<'tcx, C>(
5256
query: &'tcx QueryVTable<'tcx, C>,
5357
tcx: TyCtxt<'tcx>,
54-
require_complete: bool,
5558
job_map_out: &mut QueryJobMap<'tcx>, // Out-param; job info is gathered into this map
56-
) -> Option<()>
59+
) -> bool
5760
where
5861
C: QueryCache<Key: QueryKey + DynSend + DynSync>,
5962
QueryVTable<'tcx, C>: DynSync,
6063
{
61-
let mut active = Vec::new();
62-
63-
// Helper to gather active jobs from a single shard.
64-
let mut gather_shard_jobs = |shard: &HashTable<(C::Key, ActiveKeyStatus<'tcx>)>| {
65-
for (k, v) in shard.iter() {
66-
if let ActiveKeyStatus::Started(ref job) = *v {
67-
active.push((*k, job.clone()));
68-
}
69-
}
70-
};
71-
72-
// Lock shards and gather jobs from each shard.
73-
if require_complete {
74-
for shard in query.state.active.lock_shards() {
75-
gather_shard_jobs(&shard);
76-
}
77-
} else {
78-
// We use try_lock_shards here since we are called from the
79-
// deadlock handler, and this shouldn't be locked.
80-
for shard in query.state.active.try_lock_shards() {
81-
// This can be called during unwinding, and the function has a `try_`-prefix, so
82-
// don't `unwrap()` here, just manually check for `None` and do best-effort error
83-
// reporting.
84-
match shard {
85-
None => {
86-
tracing::warn!(
87-
"Failed to collect active jobs for query with name `{}`!",
88-
query.name
89-
);
90-
return None;
64+
let mut ok = true;
65+
66+
for shard in query.state.active.try_lock_shards() {
67+
match shard {
68+
Some(shard) => {
69+
for (key, status) in shard.iter() {
70+
if let ActiveKeyStatus::Started(job) = status {
71+
// This function to call with query state locked because it is very simple.
72+
let frame = create_deferred_query_stack_frame(tcx, query, *key);
73+
job_map_out.insert(job.id, QueryJobInfo { frame, job: job.clone() });
74+
}
9175
}
92-
Some(shard) => gather_shard_jobs(&shard),
76+
}
77+
None => {
78+
tracing::warn!(
79+
"Failed to collect active jobs for query with name `{}`!",
80+
query.name
81+
);
82+
ok = false;
9383
}
9484
}
9585
}
9686

97-
// Call `make_frame` while we're not holding a `state.active` lock as `make_frame` may call
98-
// queries leading to a deadlock.
99-
for (key, job) in active {
100-
let frame = crate::plumbing::create_deferred_query_stack_frame(tcx, query, key);
101-
job_map_out.insert(job.id, QueryJobInfo { frame, job });
102-
}
103-
104-
Some(())
87+
ok
10588
}
10689

10790
/// Guard object representing the responsibility to execute a query job and
@@ -220,9 +203,8 @@ fn cycle_error<'tcx, C: QueryCache>(
220203
) -> (C::Value, Option<DepNodeIndex>) {
221204
// Ensure there was no errors collecting all active jobs.
222205
// We need the complete map to ensure we find a cycle to break.
223-
let job_map = collect_active_jobs_from_all_queries(tcx, false)
224-
.ok()
225-
.expect("failed to collect active queries");
206+
let (job_map, collect_ok) = collect_active_jobs_from_all_queries(tcx);
207+
assert!(collect_ok, "failed to collect active queries for cycle error");
226208

227209
let error = find_cycle_in_stack(try_execute, job_map, &current_query_job(), span);
228210
(mk_cycle(query, tcx, error.lift()), None)

compiler/rustc_query_impl/src/job.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,8 @@ pub fn print_query_stack<'tcx>(
384384
let mut count_printed = 0;
385385
let mut count_total = 0;
386386

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

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

compiler/rustc_query_impl/src/plumbing.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ use crate::job::find_dep_kind_root;
3636
use crate::{GetQueryVTable, collect_active_jobs_from_all_queries};
3737

3838
fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) {
39-
let job_map =
40-
collect_active_jobs_from_all_queries(tcx, true).expect("failed to collect active queries");
39+
let (job_map, collect_ok) = collect_active_jobs_from_all_queries(tcx);
40+
assert!(collect_ok, "failed to collect active queries for depth limit error");
41+
4142
let (info, depth) = find_dep_kind_root(job, job_map);
4243

4344
let suggested_limit = match tcx.recursion_limit() {
@@ -512,36 +513,25 @@ macro_rules! define_queries {
512513
}
513514
}
514515

515-
/// Returns a map of currently active query jobs, collected from all queries.
516-
///
517-
/// If `require_complete` is `true`, this function locks all shards of the
518-
/// query results to produce a complete map, which always returns `Ok`.
519-
/// Otherwise, it may return an incomplete map as an error if any shard
520-
/// lock cannot be acquired.
521-
///
522-
/// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
523-
/// especially when called from within a deadlock handler, unless a
524-
/// complete map is needed and no deadlock is possible at this call site.
516+
/// Returns a map of currently active query jobs, collected from all queries, and a success
517+
/// indicator. If the indicator is false, the map will be incomplete because one or more
518+
/// shard locks could not be acquired.
525519
pub fn collect_active_jobs_from_all_queries<'tcx>(
526520
tcx: TyCtxt<'tcx>,
527-
require_complete: bool,
528-
) -> Result<QueryJobMap<'tcx>, QueryJobMap<'tcx>> {
529-
let mut job_map_out = QueryJobMap::default();
530-
let mut complete = true;
521+
) -> (QueryJobMap<'tcx>, bool) {
522+
let mut job_map = QueryJobMap::default();
523+
let mut all_ok = true;
531524

532525
$(
533-
let res = crate::execution::gather_active_jobs(
526+
let query_ok = crate::execution::gather_active_jobs(
534527
&tcx.query_system.query_vtables.$name,
535528
tcx,
536-
require_complete,
537-
&mut job_map_out,
529+
&mut job_map,
538530
);
539-
if res.is_none() {
540-
complete = false;
541-
}
531+
all_ok = all_ok && query_ok;
542532
)*
543533

544-
if complete { Ok(job_map_out) } else { Err(job_map_out) }
534+
(job_map, all_ok)
545535
}
546536

547537
/// All self-profiling events generated by the query engine use

0 commit comments

Comments
 (0)