Permit marking dirty/changed a node more than once
This functionality will be useful for node restarting. More than one
parent node may request the restart of a shared child node, and this
should not fail.
Instead, the returned MarkedDirtyResult indicates whether the dirtying
was redundant, and the calling code can assert on that.
RELNOTES: None.
PiperOrigin-RevId: 201005663
diff --git a/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java b/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
index 00f52dd..bb6a2d1 100644
--- a/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
@@ -475,21 +475,19 @@
DirtyBuildingState.create(isChanged, GroupedList.<SkyKey>create(directDeps), value);
value = null;
directDeps = null;
- return new MarkedDirtyResult(ReverseDepsUtility.getReverseDeps(this));
+ return new FromCleanMarkedDirtyResult(ReverseDepsUtility.getReverseDeps(this));
}
- // The caller may be simultaneously trying to mark this node dirty and changed, and the dirty
- // thread may have lost the race, but it is the caller's responsibility not to try to mark
- // this node changed twice. The end result of racing markers must be a changed node, since one
- // of the markers is trying to mark the node changed.
- Preconditions.checkState(isChanged != isChanged(),
- "Cannot mark node dirty twice or changed twice: %s", this);
+
Preconditions.checkState(value == null, "Value should have been reset already %s", this);
- if (isChanged) {
- // If the changed marker lost the race, we just need to mark changed in this method -- all
- // other work was done by the dirty marker.
- getDirtyBuildingState().markChanged();
+ if (isChanged != isChanged()) {
+ if (isChanged) {
+ getDirtyBuildingState().markChanged();
+ }
+ // If !isChanged, then this call made no changes to the node, but redundancy is a property of
+ // the sequence of markDirty calls, not their effects.
+ return FromDirtyMarkedDirtyResult.NOT_REDUNDANT;
}
- return null;
+ return FromDirtyMarkedDirtyResult.REDUNDANT;
}
@Override
diff --git a/src/main/java/com/google/devtools/build/skyframe/InvalidatingNodeVisitor.java b/src/main/java/com/google/devtools/build/skyframe/InvalidatingNodeVisitor.java
index ceae771..9d2d811 100644
--- a/src/main/java/com/google/devtools/build/skyframe/InvalidatingNodeVisitor.java
+++ b/src/main/java/com/google/devtools/build/skyframe/InvalidatingNodeVisitor.java
@@ -207,7 +207,7 @@
}
}
- public static class DirtyingInvalidationState extends InvalidationState {
+ static class DirtyingInvalidationState extends InvalidationState {
public DirtyingInvalidationState() {
super(InvalidationType.CHANGED);
}
@@ -477,7 +477,7 @@
// method.
// Any exception thrown should be unrecoverable.
// This entry remains in the graph in this dirty state until it is re-evaluated.
- MarkedDirtyResult markedDirtyResult = null;
+ MarkedDirtyResult markedDirtyResult;
try {
markedDirtyResult = entry.markDirty(isChanged);
} catch (InterruptedException e) {
@@ -487,8 +487,13 @@
// visitation, so we can resume next time.
return;
}
- if (markedDirtyResult == null) {
- // Another thread has already dirtied this node. Don't do anything in this thread.
+ Preconditions.checkState(
+ !markedDirtyResult.wasCallRedundant(),
+ "Node unexpectedly marked dirty or changed twice: %s",
+ entry);
+ if (!markedDirtyResult.wasClean()) {
+ // Another thread has already handled this node's rdeps. Don't do anything in this
+ // thread.
if (supportInterruptions) {
pendingVisitations.remove(Pair.of(key, invalidationType));
}
@@ -496,7 +501,10 @@
}
// Propagate dirtiness upwards and mark this node dirty/changed. Reverse deps should
// only be marked dirty (because only a dependency of theirs has changed).
- visit(markedDirtyResult.getReverseDepsUnsafe(), InvalidationType.DIRTIED, key);
+ visit(
+ markedDirtyResult.getReverseDepsUnsafeIfWasClean(),
+ InvalidationType.DIRTIED,
+ key);
progressReceiver.invalidated(key,
EvaluationProgressReceiver.InvalidationState.DIRTY);
diff --git a/src/main/java/com/google/devtools/build/skyframe/ThinNodeEntry.java b/src/main/java/com/google/devtools/build/skyframe/ThinNodeEntry.java
index eef5487..c063ce6 100644
--- a/src/main/java/com/google/devtools/build/skyframe/ThinNodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/ThinNodeEntry.java
@@ -15,11 +15,10 @@
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import javax.annotation.Nullable;
/**
* A node in the graph without the means to access its value. All operations on this class are
- * thread-safe. Note, however, the warning on the return value of {@link #markDirty}.
+ * thread-safe (note, however, the warning on the return value of {@link #markDirty}).
*
* <p>This interface is public only for the benefit of alternative graph implementations outside of
* the package.
@@ -45,45 +44,101 @@
boolean isChanged();
/**
- * Marks this node dirty, or changed if {@code isChanged} is true. The node is put in the
- * just-created state. It will be re-evaluated if necessary during the evaluation phase, but if it
- * has not changed, it will not force a re-evaluation of its parents.
+ * Marks this node dirty, or changed if {@code isChanged} is true.
*
- * <p>{@code markDirty(b)} must not be called on an undone node if {@code isChanged() == b}. It is
- * the caller's responsibility to ensure that this does not happen. Calling {@code
- * markDirty(false)} when {@code isChanged() == true} has no effect. The idea here is that the
- * caller will only ever want to call {@code markDirty()} a second time if a transition from a
- * dirty-unchanged state to a dirty-changed state is required.
+ * <p>A dirty node P is re-evaluated during the evaluation phase if it's requested and directly
+ * depends on some node C whose value changed since the last evaluation of P. If it's requested
+ * and there is no such node C, P is marked clean.
*
- * @return A {@link ThinNodeEntry.MarkedDirtyResult} if the node was previously clean, and {@code
- * null} if it was already dirty. If it was already dirty, the caller should abort its
- * handling of this node, since another thread is already dealing with it. Note the warning on
- * {@link ThinNodeEntry.MarkedDirtyResult} regarding the collection it provides.
+ * <p>A changed node is re-evaluated during the evaluation phase if it's requested (regardless of
+ * the state of its dependencies).
+ *
+ * @return a {@link MarkedDirtyResult} indicating whether the call was redundant and which may
+ * include the node's reverse deps
*/
- @Nullable
@ThreadSafe
MarkedDirtyResult markDirty(boolean isChanged) throws InterruptedException;
- /**
- * Returned by {@link #markDirty} if that call changed the node from clean to dirty. Contains an
- * iterable of the node's reverse deps for efficiency, because the single use case for {@link
- * #markDirty} is during invalidation, and if such an invalidation call wins, the invalidator
- * must immediately afterwards schedule the invalidation of the node's reverse deps.
- *
- * <p>Warning: {@link #getReverseDepsUnsafe()} may return a live view of the reverse deps
- * collection of the marked-dirty node. The consumer of this data must be careful only to
- * iterate over and consume its values while that collection is guaranteed not to change. This
- * is true during invalidation, because reverse deps don't change during invalidation.
- */
- class MarkedDirtyResult {
+ /** Returned by {@link #markDirty}. */
+ interface MarkedDirtyResult {
+
+ /** Returns true iff the node was clean prior to the {@link #markDirty} call. */
+ boolean wasClean();
+
+ /**
+ * Returns true iff the call to {@link #markDirty} was the same as some previous call to {@link
+ * #markDirty} (i.e., sharing the same {@code isChanged} parameter value) since the last time
+ * the node was clean.
+ *
+ * <p>More specifically, this returns true iff the call was {@code n.markDirty(b)} and prior to
+ * the call {@code n.isDirty() && n.isChanged() == b}).
+ */
+ boolean wasCallRedundant();
+
+ /**
+ * If {@code wasClean()}, this returns an iterable of the node's reverse deps for efficiency,
+ * because the {@link #markDirty} caller may be doing graph invalidation, and after dirtying a
+ * node, the invalidation process may want to dirty the node's reverse deps.
+ *
+ * <p>If {@code !wasClean()}, this must not be called. It will throw {@link
+ * IllegalStateException}.
+ *
+ * <p>Warning: the returned iterable may be a live view of the reverse deps collection of the
+ * marked-dirty node. The consumer of this data must be careful only to iterate over and consume
+ * its values while that collection is guaranteed not to change. This is true during
+ * invalidation, because reverse deps don't change during invalidation.
+ */
+ Iterable<SkyKey> getReverseDepsUnsafeIfWasClean();
+ }
+
+ /** A {@link MarkedDirtyResult} returned when {@link #markDirty} is called on a clean node. */
+ class FromCleanMarkedDirtyResult implements MarkedDirtyResult {
private final Iterable<SkyKey> reverseDepsUnsafe;
- public MarkedDirtyResult(Iterable<SkyKey> reverseDepsUnsafe) {
+ public FromCleanMarkedDirtyResult(Iterable<SkyKey> reverseDepsUnsafe) {
this.reverseDepsUnsafe = Preconditions.checkNotNull(reverseDepsUnsafe);
}
- public Iterable<SkyKey> getReverseDepsUnsafe() {
+ @Override
+ public boolean wasClean() {
+ return true;
+ }
+
+ @Override
+ public boolean wasCallRedundant() {
+ return false;
+ }
+
+ @Override
+ public Iterable<SkyKey> getReverseDepsUnsafeIfWasClean() {
return reverseDepsUnsafe;
}
}
+
+ /** A {@link MarkedDirtyResult} returned when {@link #markDirty} is called on a dirty node. */
+ class FromDirtyMarkedDirtyResult implements MarkedDirtyResult {
+ static final FromDirtyMarkedDirtyResult REDUNDANT = new FromDirtyMarkedDirtyResult(true);
+ static final FromDirtyMarkedDirtyResult NOT_REDUNDANT = new FromDirtyMarkedDirtyResult(false);
+
+ private final boolean redundant;
+
+ private FromDirtyMarkedDirtyResult(boolean redundant) {
+ this.redundant = redundant;
+ }
+
+ @Override
+ public boolean wasClean() {
+ return false;
+ }
+
+ @Override
+ public boolean wasCallRedundant() {
+ return redundant;
+ }
+
+ @Override
+ public Iterable<SkyKey> getReverseDepsUnsafeIfWasClean() {
+ throw new IllegalStateException();
+ }
+ }
}
diff --git a/src/test/java/com/google/devtools/build/skyframe/GraphTest.java b/src/test/java/com/google/devtools/build/skyframe/GraphTest.java
index 378f58c..1f843a7 100644
--- a/src/test/java/com/google/devtools/build/skyframe/GraphTest.java
+++ b/src/test/java/com/google/devtools/build/skyframe/GraphTest.java
@@ -222,7 +222,7 @@
graph = getGraph(getNextVersion(startingVersion));
NodeEntry sameEntry = Preconditions.checkNotNull(graph.get(null, Reason.OTHER, key));
// Mark the node as dirty again and check that the reverse deps have been preserved.
- sameEntry.markDirty(true);
+ assertThat(sameEntry.markDirty(true).wasCallRedundant()).isFalse();
startEvaluation(sameEntry);
sameEntry.markRebuilding();
sameEntry.setValue(new StringValue("foo2"), getNextVersion(startingVersion));
@@ -368,7 +368,7 @@
throw new IllegalStateException(e);
}
try {
- entry.markDirty(true);
+ assertThat(entry.markDirty(true).wasCallRedundant()).isFalse();
// Make some changes, like adding a dep and rdep.
entry.addReverseDepAndCheckIfDone(key("rdep"));
diff --git a/src/test/java/com/google/devtools/build/skyframe/InMemoryNodeEntryTest.java b/src/test/java/com/google/devtools/build/skyframe/InMemoryNodeEntryTest.java
index b16206c..4712cfd 100644
--- a/src/test/java/com/google/devtools/build/skyframe/InMemoryNodeEntryTest.java
+++ b/src/test/java/com/google/devtools/build/skyframe/InMemoryNodeEntryTest.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.skyframe.NodeEntry.DependencyState;
import com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException;
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.ThinNodeEntry.MarkedDirtyResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -183,7 +184,9 @@
setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isFalse();
assertThat(entry.isDone()).isFalse();
@@ -215,7 +218,9 @@
setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/true);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/true);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isTrue();
assertThat(entry.isDone()).isFalse();
@@ -243,11 +248,15 @@
setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult firstResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(firstResult.wasCallRedundant()).isFalse();
+ assertThat(firstResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isFalse();
assertThat(entry.isDone()).isFalse();
- entry.markDirty(/*isChanged=*/true);
+ MarkedDirtyResult secondResult = entry.markDirty(/*isChanged=*/true);
+ assertThat(secondResult.wasCallRedundant()).isFalse();
+ assertThat(secondResult.wasClean()).isFalse();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isTrue();
assertThat(entry.isDone()).isFalse();
@@ -267,11 +276,15 @@
setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/true);
+ MarkedDirtyResult firstResult = entry.markDirty(/*isChanged=*/true);
+ assertThat(firstResult.wasCallRedundant()).isFalse();
+ assertThat(firstResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isTrue();
assertThat(entry.isDone()).isFalse();
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult secondResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(secondResult.wasCallRedundant()).isFalse();
+ assertThat(secondResult.wasClean()).isFalse();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isTrue();
assertThat(entry.isDone()).isFalse();
@@ -282,35 +295,60 @@
}
@Test
- public void crashOnTwiceMarkedChanged() throws InterruptedException {
+ public void markChangedTwice() throws InterruptedException {
NodeEntry entry = new InMemoryNodeEntry();
entry.addReverseDepAndCheckIfDone(null); // Start evaluation.
- setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
+
+ setValue(entry, new SkyValue() {}, /*errorInfo=*/ null, /*graphVersion=*/ 0L);
assertThat(entry.isDirty()).isFalse();
+ assertThat(entry.isChanged()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/true);
- try {
- entry.markDirty(/*isChanged=*/true);
- fail("Cannot mark entry changed twice");
- } catch (IllegalStateException e) {
- // Expected.
- }
+
+ MarkedDirtyResult firstResult = entry.markDirty(/*isChanged=*/ true);
+ assertThat(firstResult.getReverseDepsUnsafeIfWasClean()).isNotNull();
+ assertThat(firstResult.wasCallRedundant()).isFalse();
+ assertThat(firstResult.wasClean()).isTrue();
+ assertThat(entry.isDirty()).isTrue();
+ assertThat(entry.isChanged()).isTrue();
+ assertThat(entry.isDone()).isFalse();
+
+ MarkedDirtyResult secondResult = entry.markDirty(/*isChanged=*/ true);
+ assertThat(secondResult.wasClean()).isFalse();
+ assertThat(secondResult.wasCallRedundant()).isTrue();
+ assertThat(entry.isDirty()).isTrue();
+ assertThat(entry.isChanged()).isTrue();
+ assertThat(entry.isDone()).isFalse();
}
@Test
- public void crashOnTwiceMarkedDirty() throws InterruptedException {
+ public void markDirtyTwice() throws InterruptedException {
NodeEntry entry = new InMemoryNodeEntry();
entry.addReverseDepAndCheckIfDone(null); // Start evaluation.
+
+ // To successfully mark a node dirty (but not changed), that node must have at least one
+ // dependency. The node sanity-checks its deps while being marked dirty.
addTemporaryDirectDep(entry, key("dep"));
entry.signalDep();
- setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
- entry.markDirty(/*isChanged=*/false);
- try {
- entry.markDirty(/*isChanged=*/false);
- fail("Cannot mark entry dirty twice");
- } catch (IllegalStateException e) {
- // Expected.
- }
+
+ setValue(entry, new SkyValue() {}, /*errorInfo=*/ null, /*graphVersion=*/ 0L);
+ assertThat(entry.isDirty()).isFalse();
+ assertThat(entry.isChanged()).isFalse();
+ assertThat(entry.isDone()).isTrue();
+
+ MarkedDirtyResult firstResult = entry.markDirty(/*isChanged=*/ false);
+ assertThat(firstResult.getReverseDepsUnsafeIfWasClean()).isNotNull();
+ assertThat(firstResult.wasCallRedundant()).isFalse();
+ assertThat(firstResult.wasClean()).isTrue();
+ assertThat(entry.isDirty()).isTrue();
+ assertThat(entry.isChanged()).isFalse();
+ assertThat(entry.isDone()).isFalse();
+
+ MarkedDirtyResult secondResult = entry.markDirty(/*isChanged=*/ false);
+ assertThat(secondResult.wasClean()).isFalse();
+ assertThat(secondResult.wasCallRedundant()).isTrue();
+ assertThat(entry.isDirty()).isTrue();
+ assertThat(entry.isChanged()).isFalse();
+ assertThat(entry.isDone()).isFalse();
}
@Test
@@ -373,7 +411,9 @@
setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isFalse();
assertThat(entry.isDone()).isFalse();
@@ -419,7 +459,9 @@
addTemporaryDirectDep(entry, dep);
entry.signalDep();
setValue(entry, new IntegerValue(5), /*errorInfo=*/null, /*graphVersion=*/0L);
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
entry.addReverseDepAndCheckIfDone(null); // Start evaluation.
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.CHECK_DEPENDENCIES);
entry.addReverseDepAndCheckIfDone(null); // Start evaluation.
@@ -444,7 +486,9 @@
setValue(entry, new IntegerValue(5), /*errorInfo=*/null, /*graphVersion=*/0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isFalse();
assertThat(entry.isDone()).isFalse();
@@ -487,7 +531,9 @@
setValue(entry, new IntegerValue(5), /*errorInfo=*/ null, /*graphVersion=*/ 0L);
assertThat(entry.isDirty()).isFalse();
assertThat(entry.isDone()).isTrue();
- entry.markDirty(/*isChanged=*/ false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/ false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
assertThat(entry.isDirty()).isTrue();
assertThat(entry.isChanged()).isFalse();
assertThat(entry.isDone()).isFalse();
@@ -525,7 +571,9 @@
key("cause"));
ErrorInfo errorInfo = ErrorInfo.fromException(exception, false);
setValue(entry, /*value=*/null, errorInfo, /*graphVersion=*/0L);
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
entry.addReverseDepAndCheckIfDone(null); // Restart evaluation.
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.CHECK_DEPENDENCIES);
assertThat(entry.getNextDirtyDirectDeps()).containsExactly(dep);
@@ -553,7 +601,9 @@
entry.signalDep();
entry.signalDep();
setValue(entry, /*value=*/new IntegerValue(5), null, 0L);
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
entry.addReverseDepAndCheckIfDone(null); // Restart evaluation.
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.CHECK_DEPENDENCIES);
assertThat(entry.getNextDirtyDirectDeps()).containsExactly(dep, dep2);
@@ -584,7 +634,9 @@
new GenericFunctionException(new SomeErrorException("oops"), Transience.PERSISTENT),
key("key"));
setValue(entry, null, ErrorInfo.fromException(exception, false), 0L);
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
entry.addReverseDepAndCheckIfDone(null); // Restart evaluation.
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.CHECK_DEPENDENCIES);
assertThat(entry.getNextDirtyDirectDeps()).containsExactly(dep);
@@ -602,7 +654,9 @@
addTemporaryDirectDep(entry, dep);
entry.signalDep();
setValue(entry, new IntegerValue(5), /*errorInfo=*/null, /*graphVersion=*/0L);
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
entry.addReverseDepAndCheckIfDone(null); // Start evaluation.
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.CHECK_DEPENDENCIES);
assertThat(entry.getNextDirtyDirectDeps()).containsExactly(dep);
@@ -630,7 +684,9 @@
entry.signalDep();
}
setValue(entry, new IntegerValue(5), /*errorInfo=*/null, /*graphVersion=*/0L);
- entry.markDirty(/*isChanged=*/false);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/false);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
entry.addReverseDepAndCheckIfDone(null); // Start new evaluation.
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.CHECK_DEPENDENCIES);
for (int ii = 0; ii < 10; ii++) {
@@ -650,7 +706,9 @@
NodeEntry entry = new InMemoryNodeEntry();
entry.addReverseDepAndCheckIfDone(key("parent"));
setValue(entry, new SkyValue() {}, /*errorInfo=*/null, /*graphVersion=*/0L);
- entry.markDirty(/*isChanged=*/true);
+ MarkedDirtyResult markedDirtyResult = entry.markDirty(/*isChanged=*/true);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
SkyKey newParent = key("new parent");
entry.addReverseDepAndCheckIfDone(newParent);
assertThat(entry.getDirtyState()).isEqualTo(NodeEntry.DirtyState.NEEDS_REBUILDING);
@@ -678,7 +736,9 @@
entry.removeReverseDep(key("parent1"));
entry.removeReverseDep(key("parent2"));
IntegerValue updatedValue = new IntegerValue(52);
- clone2.markDirty(true);
+ MarkedDirtyResult markedDirtyResult = clone2.markDirty(true);
+ assertThat(markedDirtyResult.wasCallRedundant()).isFalse();
+ assertThat(markedDirtyResult.wasClean()).isTrue();
clone2.addReverseDepAndCheckIfDone(null);
SkyKey newChild = key("newchild");
addTemporaryDirectDep(clone2, newChild);