Skip to content

Commit 938afba

Browse files
committed
Auto merge of #108845 - Zoxc:par-fix-2, r=cjgillot
Check that a query has not completed and is not executing before starting it This fixes a race in the query system where we only checked if the query was currently executing, but not if it was already completed, causing queries to re-execute. r? `@cjgillot`
2 parents f41927f + 9555499 commit 938afba

File tree

1 file changed

+21
-6
lines changed

1 file changed

+21
-6
lines changed

compiler/rustc_query_system/src/query/plumbing.rs

+21-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_data_structures::profiling::TimingGuard;
1717
#[cfg(parallel_compiler)]
1818
use rustc_data_structures::sharded::Sharded;
1919
use rustc_data_structures::stack::ensure_sufficient_stack;
20-
use rustc_data_structures::sync::Lock;
20+
use rustc_data_structures::sync::{Lock, LockGuard};
2121
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
2222
use rustc_session::Session;
2323
use rustc_span::{Span, DUMMY_SP};
@@ -178,16 +178,13 @@ where
178178
fn try_start<'b, Qcx>(
179179
qcx: &'b Qcx,
180180
state: &'b QueryState<K, Qcx::DepKind>,
181+
mut state_lock: LockGuard<'b, FxHashMap<K, QueryResult<Qcx::DepKind>>>,
181182
span: Span,
182183
key: K,
183184
) -> TryGetJob<'b, K, D>
184185
where
185186
Qcx: QueryContext + HasDepContext<DepKind = D>,
186187
{
187-
#[cfg(parallel_compiler)]
188-
let mut state_lock = state.active.get_shard_by_value(&key).lock();
189-
#[cfg(not(parallel_compiler))]
190-
let mut state_lock = state.active.lock();
191188
let lock = &mut *state_lock;
192189
let current_job_id = qcx.current_query_job();
193190

@@ -362,7 +359,25 @@ where
362359
Qcx: QueryContext,
363360
{
364361
let state = query.query_state(qcx);
365-
match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, span, key) {
362+
#[cfg(parallel_compiler)]
363+
let state_lock = state.active.get_shard_by_value(&key).lock();
364+
#[cfg(not(parallel_compiler))]
365+
let state_lock = state.active.lock();
366+
367+
// For the parallel compiler we need to check both the query cache and query state structures
368+
// while holding the state lock to ensure that 1) the query has not yet completed and 2) the
369+
// query is not still executing. Without checking the query cache here, we can end up
370+
// re-executing the query since `try_start` only checks that the query is not currently
371+
// executing, but another thread may have already completed the query and stores it result
372+
// in the query cache.
373+
if cfg!(parallel_compiler) && qcx.dep_context().sess().threads() > 1 {
374+
if let Some((value, index)) = query.query_cache(qcx).lookup(&key) {
375+
qcx.dep_context().profiler().query_cache_hit(index.into());
376+
return (value, Some(index));
377+
}
378+
}
379+
380+
match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, state_lock, span, key) {
366381
TryGetJob::NotYetStarted(job) => {
367382
let (result, dep_node_index) = execute_job(query, qcx, key.clone(), dep_node, job.id);
368383
let cache = query.query_cache(qcx);

0 commit comments

Comments
 (0)