about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-12 17:03:44 +0000
committerbors <bors@rust-lang.org>2023-03-12 17:03:44 +0000
commit938afba8996fe058b91c61b23ef5d000cb9ac169 (patch)
tree4997a8f66428c96eb69f135be696f2a4aafdb8b9
parentf41927f30943e4d57af62cfcedc9f07b819013e7 (diff)
parent955549955f0143191de9c68ea4bee01823ebebf7 (diff)
downloadrust-938afba8996fe058b91c61b23ef5d000cb9ac169.tar.gz
rust-938afba8996fe058b91c61b23ef5d000cb9ac169.zip
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`
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs27
1 files changed, 21 insertions, 6 deletions
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index a3656f4ebe6..005512cf53e 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -17,7 +17,7 @@ use rustc_data_structures::profiling::TimingGuard;
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sharded::Sharded;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_data_structures::sync::Lock;
+use rustc_data_structures::sync::{Lock, LockGuard};
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
 use rustc_session::Session;
 use rustc_span::{Span, DUMMY_SP};
@@ -178,16 +178,13 @@ where
     fn try_start<'b, Qcx>(
         qcx: &'b Qcx,
         state: &'b QueryState<K, Qcx::DepKind>,
+        mut state_lock: LockGuard<'b, FxHashMap<K, QueryResult<Qcx::DepKind>>>,
         span: Span,
         key: K,
     ) -> TryGetJob<'b, K, D>
     where
         Qcx: QueryContext + HasDepContext<DepKind = D>,
     {
-        #[cfg(parallel_compiler)]
-        let mut state_lock = state.active.get_shard_by_value(&key).lock();
-        #[cfg(not(parallel_compiler))]
-        let mut state_lock = state.active.lock();
         let lock = &mut *state_lock;
         let current_job_id = qcx.current_query_job();
 
@@ -362,7 +359,25 @@ where
     Qcx: QueryContext,
 {
     let state = query.query_state(qcx);
-    match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, span, key) {
+    #[cfg(parallel_compiler)]
+    let state_lock = state.active.get_shard_by_value(&key).lock();
+    #[cfg(not(parallel_compiler))]
+    let state_lock = state.active.lock();
+
+    // For the parallel compiler we need to check both the query cache and query state structures
+    // while holding the state lock to ensure that 1) the query has not yet completed and 2) the
+    // query is not still executing. Without checking the query cache here, we can end up
+    // re-executing the query since `try_start` only checks that the query is not currently
+    // executing, but another thread may have already completed the query and stores it result
+    // in the query cache.
+    if cfg!(parallel_compiler) && qcx.dep_context().sess().threads() > 1 {
+        if let Some((value, index)) = query.query_cache(qcx).lookup(&key) {
+            qcx.dep_context().profiler().query_cache_hit(index.into());
+            return (value, Some(index));
+        }
+    }
+
+    match JobOwner::<'_, Q::Key, Qcx::DepKind>::try_start(&qcx, state, state_lock, span, key) {
         TryGetJob::NotYetStarted(job) => {
             let (result, dep_node_index) = execute_job(query, qcx, key.clone(), dep_node, job.id);
             let cache = query.query_cache(qcx);