Skip to content

Commit 9e79395

Browse files
committed
Auto merge of #151509 - Zoxc:gen-color-race, r=zetanumbers,petrochenkov
Handle race when coloring nodes concurrently as both green and red This fixes a race where a duplicate dep node gets written to the dep graph if a node was marked as green and promoted during execution, then marked as red after execution. This can occur when a `no_hash` query A depends on a query B which cannot be forced so it was not colored when starting execution of query A. During the execution of query A it will execute query B and color it green. Before A finishes another thread tries to mark A green, this time succeeding as B is now green, and A gets promoted and written to metadata. Execution of A then finishes and because it's `no_hash` we assume the result changed and thus we color the node again, now as red and write it to metadata again. This doesn't happen with non-`no_hash` queries as they will be green if all their dependencies are green. This changes the code coloring nodes red to also use `compare_exchange` to deal with this race ensuring that the coloring of nodes only happens once. Fixes #150018 Fixes #142778 Fixes #141540
2 parents d00ba92 + d8d4d40 commit 9e79395

File tree

2 files changed

+37
-32
lines changed

2 files changed

+37
-32
lines changed

compiler/rustc_query_system/src/dep_graph/graph.rs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -788,17 +788,22 @@ impl<D: Deps> DepGraphData<D> {
788788
}
789789
}
790790

791-
fn promote_node_and_deps_to_current(&self, prev_index: SerializedDepNodeIndex) -> DepNodeIndex {
791+
fn promote_node_and_deps_to_current(
792+
&self,
793+
prev_index: SerializedDepNodeIndex,
794+
) -> Option<DepNodeIndex> {
792795
self.current.debug_assert_not_in_new_nodes(&self.previous, prev_index);
793796

794797
let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors);
795798

796799
#[cfg(debug_assertions)]
797-
self.current.record_edge(
798-
dep_node_index,
799-
*self.previous.index_to_node(prev_index),
800-
self.previous.fingerprint_by_index(prev_index),
801-
);
800+
if let Some(dep_node_index) = dep_node_index {
801+
self.current.record_edge(
802+
dep_node_index,
803+
*self.previous.index_to_node(prev_index),
804+
self.previous.fingerprint_by_index(prev_index),
805+
);
806+
}
802807

803808
dep_node_index
804809
}
@@ -1018,8 +1023,10 @@ impl<D: Deps> DepGraphData<D> {
10181023
// There may be multiple threads trying to mark the same dep node green concurrently
10191024

10201025
// We allocating an entry for the node in the current dependency graph and
1021-
// adding all the appropriate edges imported from the previous graph
1022-
let dep_node_index = self.promote_node_and_deps_to_current(prev_dep_node_index);
1026+
// adding all the appropriate edges imported from the previous graph.
1027+
//
1028+
// `no_hash` nodes may fail this promotion due to already being conservatively colored red.
1029+
let dep_node_index = self.promote_node_and_deps_to_current(prev_dep_node_index)?;
10231030

10241031
// ... and finally storing a "Green" entry in the color map.
10251032
// Multiple threads can all write the same color here
@@ -1390,26 +1397,27 @@ impl DepNodeColorMap {
13901397
}
13911398

13921399
/// This tries to atomically mark a node green and assign `index` as the new
1393-
/// index. This returns `Ok` if `index` gets assigned, otherwise it returns
1394-
/// the already allocated index in `Err`.
1395-
#[inline]
1396-
pub(super) fn try_mark_green(
1400+
/// index if `green` is true, otherwise it will try to atomicaly mark it red.
1401+
///
1402+
/// This returns `Ok` if `index` gets assigned or the node is marked red, otherwise it returns
1403+
/// the already allocated index in `Err` if it is green already. If it was already
1404+
/// red, `Err(None)` is returned.
1405+
#[inline(always)]
1406+
pub(super) fn try_mark(
13971407
&self,
13981408
prev_index: SerializedDepNodeIndex,
13991409
index: DepNodeIndex,
1400-
) -> Result<(), DepNodeIndex> {
1410+
green: bool,
1411+
) -> Result<(), Option<DepNodeIndex>> {
14011412
let value = &self.values[prev_index];
14021413
match value.compare_exchange(
14031414
COMPRESSED_UNKNOWN,
1404-
index.as_u32(),
1415+
if green { index.as_u32() } else { COMPRESSED_RED },
14051416
Ordering::Relaxed,
14061417
Ordering::Relaxed,
14071418
) {
14081419
Ok(_) => Ok(()),
1409-
Err(v) => Err({
1410-
assert_ne!(v, COMPRESSED_RED, "tried to mark a red node as green");
1411-
DepNodeIndex::from_u32(v)
1412-
}),
1420+
Err(v) => Err(if v == COMPRESSED_RED { None } else { Some(DepNodeIndex::from_u32(v)) }),
14131421
}
14141422
}
14151423

@@ -1432,7 +1440,7 @@ impl DepNodeColorMap {
14321440
pub(super) fn insert_red(&self, index: SerializedDepNodeIndex) {
14331441
let value = self.values[index].swap(COMPRESSED_RED, Ordering::Release);
14341442
// Sanity check for duplicate nodes
1435-
assert_eq!(value, COMPRESSED_UNKNOWN, "trying to encode a dep node twice");
1443+
assert_eq!(value, COMPRESSED_UNKNOWN, "tried to color an already colored node as red");
14361444
}
14371445
}
14381446

compiler/rustc_query_system/src/dep_graph/serialized.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -898,15 +898,12 @@ impl<D: Deps> GraphEncoder<D> {
898898

899899
let index = self.status.next_index(&mut *local);
900900

901-
if is_green {
902-
// Use `try_mark_green` to avoid racing when `send_promoted` is called concurrently
903-
// on the same index.
904-
match colors.try_mark_green(prev_index, index) {
905-
Ok(()) => (),
906-
Err(dep_node_index) => return dep_node_index,
907-
}
908-
} else {
909-
colors.insert_red(prev_index);
901+
// Use `try_mark` to avoid racing when `send_promoted` is called concurrently
902+
// on the same index.
903+
match colors.try_mark(prev_index, index, is_green) {
904+
Ok(()) => (),
905+
Err(None) => panic!("dep node {:?} is unexpectedly red", prev_index),
906+
Err(Some(dep_node_index)) => return dep_node_index,
910907
}
911908

912909
self.status.bump_index(&mut *local);
@@ -918,21 +915,21 @@ impl<D: Deps> GraphEncoder<D> {
918915
/// from the previous dep graph and expects all edges to already have a new dep node index
919916
/// assigned.
920917
///
921-
/// This will also ensure the dep node is marked green.
918+
/// This will also ensure the dep node is marked green if `Some` is returned.
922919
#[inline]
923920
pub(crate) fn send_promoted(
924921
&self,
925922
prev_index: SerializedDepNodeIndex,
926923
colors: &DepNodeColorMap,
927-
) -> DepNodeIndex {
924+
) -> Option<DepNodeIndex> {
928925
let _prof_timer = self.profiler.generic_activity("incr_comp_encode_dep_graph");
929926

930927
let mut local = self.status.local.borrow_mut();
931928
let index = self.status.next_index(&mut *local);
932929

933930
// Use `try_mark_green` to avoid racing when `send_promoted` or `send_and_color`
934931
// is called concurrently on the same index.
935-
match colors.try_mark_green(prev_index, index) {
932+
match colors.try_mark(prev_index, index, true) {
936933
Ok(()) => {
937934
self.status.bump_index(&mut *local);
938935
self.status.encode_promoted_node(
@@ -942,7 +939,7 @@ impl<D: Deps> GraphEncoder<D> {
942939
colors,
943940
&mut *local,
944941
);
945-
index
942+
Some(index)
946943
}
947944
Err(dep_node_index) => dep_node_index,
948945
}

0 commit comments

Comments
 (0)