aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mschaller <mschaller@google.com>2018-06-28 11:53:36 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-06-28 11:55:30 -0700
commita8926b7010f7bbbe6e1d9b558bac88b73be28250 (patch)
treeff7d2e2a7a89b1de404b8881abc9ce58d4ab4045
parente25edb1e8f5bfd4c2e6d7cc068567c35dd48db7b (diff)
Convert directDeps to a map of SkyValues
SkyFunctionEnvironment only cares about directDeps' values, not other NodeEntry data. This reduces the space of code which could be sensitive to nodes which transition from done to dirty during evaluation. To prevent check-then-act races in the refactored code (and only there; other code will be fixed in future refactorings), instead of checking deps' isDone() methods before accessing their value, allow getValueMaybeWithMetadata to be called when not done, and have it return null when not done. (Note that done->dirty node transitions during evaluation are planned, but not yet possible.) RELNOTES: None. PiperOrigin-RevId: 202518781
-rw-r--r--src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java18
-rw-r--r--src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java2
-rw-r--r--src/main/java/com/google/devtools/build/skyframe/NodeEntry.java9
-rw-r--r--src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java83
4 files changed, 78 insertions, 34 deletions
diff --git a/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java b/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java
index b607e2fa20..abca3b25c4 100644
--- a/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java
+++ b/src/main/java/com/google/devtools/build/skyframe/AbstractParallelEvaluator.java
@@ -424,20 +424,10 @@ public abstract class AbstractParallelEvaluator {
}
}
- Map<SkyKey, ? extends NodeEntry> newlyRequestedDeps =
- evaluatorContext.getBatchValues(
- skyKey, Reason.DONE_CHECKING, env.getNewlyRequestedDeps());
- boolean isTransitivelyTransient = reifiedBuilderException.isTransient();
- for (NodeEntry depEntry :
- Iterables.concat(env.getDirectDepsValues(), newlyRequestedDeps.values())) {
- if (!isDoneForBuild(depEntry)) {
- continue;
- }
- ErrorInfo depError = depEntry.getErrorInfo();
- if (depError != null) {
- isTransitivelyTransient |= depError.isTransitivelyTransient();
- }
- }
+ boolean isTransitivelyTransient =
+ reifiedBuilderException.isTransient()
+ || env.isAnyDirectDepErrorTransitivelyTransient()
+ || env.isAnyNewlyRequestedDepErrorTransitivelyTransient();
ErrorInfo errorInfo = evaluatorContext.getErrorInfoManager().fromException(
skyKey,
reifiedBuilderException,
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 bb6a2d1d4b..859f531666 100644
--- a/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/InMemoryNodeEntry.java
@@ -194,8 +194,8 @@ public class InMemoryNodeEntry implements NodeEntry {
}
@Override
+ @Nullable
public SkyValue getValueMaybeWithMetadata() {
- Preconditions.checkState(isDone(), "no value until done: %s", this);
return value;
}
diff --git a/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java b/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java
index aee68b3b91..a9f1d78bfd 100644
--- a/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java
+++ b/src/main/java/com/google/devtools/build/skyframe/NodeEntry.java
@@ -119,12 +119,17 @@ public interface NodeEntry extends ThinNodeEntry {
/**
* Returns raw {@link SkyValue} stored in this entry, which may include metadata associated with
- * it (like events and errors). This method may only be called after the evaluation of this node
- * is complete, i.e., after {@link #setValue} has been called.
+ * it (like events and errors).
+ *
+ * <p>This method returns {@code null} if the evaluation of this node is not complete, i.e.,
+ * after node creation or dirtying and before {@link #setValue} has been called. Callers should
+ * assert that the returned value is not {@code null} whenever they expect the node should be
+ * done.
*
* <p>Use the static methods of {@link ValueWithMetadata} to extract metadata if necessary.
*/
@ThreadSafe
+ @Nullable
SkyValue getValueMaybeWithMetadata() throws InterruptedException;
/** Returns the value, even if dirty or changed. Returns null otherwise. */
diff --git a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
index 638a7615ff..333b0e41de 100644
--- a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
@@ -14,11 +14,11 @@
package com.google.devtools.build.skyframe;
import static com.google.devtools.build.skyframe.AbstractParallelEvaluator.isDoneForBuild;
-import static com.google.devtools.build.skyframe.ParallelEvaluator.maybeGetValueFromError;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -37,11 +37,11 @@ import com.google.devtools.build.skyframe.ParallelEvaluatorContext.EnqueueParent
import com.google.devtools.build.skyframe.QueryableGraph.Reason;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import javax.annotation.Nullable;
@@ -71,8 +71,15 @@ class SkyFunctionEnvironment extends AbstractSkyFunctionEnvironment {
private SkyValue value = null;
private ErrorInfo errorInfo = null;
private final Map<SkyKey, ValueWithMetadata> bubbleErrorInfo;
- /** The values previously declared as dependencies. */
- private final Map<SkyKey, NodeEntry> directDeps;
+
+ /**
+ * The values previously declared as dependencies.
+ *
+ * <p>Values in this map are either {@link #NULL_MARKER} or were retrieved via {@link
+ * NodeEntry#getValueMaybeWithMetadata}. In the latter case, they should be processed using the
+ * static methods of {@link ValueWithMetadata}.
+ */
+ private final Map<SkyKey, SkyValue> directDeps;
/**
* The grouped list of values requested during this build as dependencies. On a subsequent build,
@@ -132,9 +139,7 @@ class SkyFunctionEnvironment extends AbstractSkyFunctionEnvironment {
this.oldDeps = oldDeps;
this.evaluatorContext = evaluatorContext;
this.directDeps =
- Collections.<SkyKey, NodeEntry>unmodifiableMap(
- batchPrefetch(
- skyKey, directDeps, oldDeps, /*assertDone=*/ bubbleErrorInfo == null, skyKey));
+ batchPrefetch(skyKey, directDeps, oldDeps, /*assertDone=*/ bubbleErrorInfo == null, skyKey);
this.bubbleErrorInfo = bubbleErrorInfo;
Preconditions.checkState(
!this.directDeps.containsKey(ErrorTransienceValue.KEY),
@@ -142,7 +147,7 @@ class SkyFunctionEnvironment extends AbstractSkyFunctionEnvironment {
skyKey);
}
- private Map<SkyKey, ? extends NodeEntry> batchPrefetch(
+ private Map<SkyKey, SkyValue> batchPrefetch(
SkyKey requestor,
GroupedList<SkyKey> depKeys,
Set<SkyKey> oldDeps,
@@ -176,13 +181,18 @@ class SkyFunctionEnvironment extends AbstractSkyFunctionEnvironment {
+ ": "
+ Sets.difference(depKeys.toSet(), batchMap.keySet()));
}
- if (assertDone) {
- for (Map.Entry<SkyKey, ? extends NodeEntry> entry : batchMap.entrySet()) {
- Preconditions.checkState(
- entry.getValue().isDone(), "%s had not done %s", keyForDebugging, entry);
+ ImmutableMap.Builder<SkyKey, SkyValue> depValuesBuilder =
+ ImmutableMap.builderWithExpectedSize(batchMap.size());
+ for (Entry<SkyKey, ? extends NodeEntry> entry : batchMap.entrySet()) {
+ SkyValue valueMaybeWithMetadata = entry.getValue().getValueMaybeWithMetadata();
+ if (assertDone) {
+ Preconditions.checkNotNull(
+ valueMaybeWithMetadata, "%s had not done %s", keyForDebugging, entry);
}
+ depValuesBuilder.put(
+ entry.getKey(), valueMaybeWithMetadata == null ? NULL_MARKER : valueMaybeWithMetadata);
}
- return batchMap;
+ return depValuesBuilder.build();
}
private void checkActive() {
@@ -339,8 +349,14 @@ class SkyFunctionEnvironment extends AbstractSkyFunctionEnvironment {
}
@Nullable
- private SkyValue maybeGetValueFromErrorOrDeps(SkyKey key) throws InterruptedException {
- return maybeGetValueFromError(key, directDeps.get(key), bubbleErrorInfo);
+ private SkyValue maybeGetValueFromErrorOrDeps(SkyKey key) {
+ if (bubbleErrorInfo != null) {
+ ValueWithMetadata bubbleErrorInfoValue = bubbleErrorInfo.get(key);
+ if (bubbleErrorInfoValue != null) {
+ return bubbleErrorInfoValue;
+ }
+ }
+ return directDeps.get(key);
}
private static SkyValue getValueOrNullMarker(@Nullable NodeEntry nodeEntry)
@@ -476,8 +492,41 @@ class SkyFunctionEnvironment extends AbstractSkyFunctionEnvironment {
return newlyRequestedDeps;
}
- Collection<NodeEntry> getDirectDepsValues() {
- return directDeps.values();
+ boolean isAnyDirectDepErrorTransitivelyTransient() {
+ Preconditions.checkState(
+ bubbleErrorInfo == null,
+ "Checking dep error transitive transience during error bubbling for: %s",
+ skyKey);
+ for (SkyValue skyValue : directDeps.values()) {
+ ErrorInfo maybeErrorInfo = ValueWithMetadata.getMaybeErrorInfo(skyValue);
+ if (maybeErrorInfo != null && maybeErrorInfo.isTransitivelyTransient()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isAnyNewlyRequestedDepErrorTransitivelyTransient() throws InterruptedException {
+ // TODO(mschaller): consider collecting SkyValues of newly requested deps as they're requested
+ // which would allow this code to avoid graph queries for nodes already queried.
+ //
+ // This will also be necessary for correct behavior in the presence of node re-dirtying.
+ Preconditions.checkState(
+ bubbleErrorInfo == null,
+ "Checking dep error transitive transience during error bubbling for: %s",
+ skyKey);
+ Map<SkyKey, ? extends NodeEntry> newlyRequestedDeps =
+ evaluatorContext.getBatchValues(skyKey, Reason.DONE_CHECKING, getNewlyRequestedDeps());
+ for (NodeEntry depEntry : newlyRequestedDeps.values()) {
+ if (!isDoneForBuild(depEntry)) {
+ continue;
+ }
+ ErrorInfo depError = depEntry.getErrorInfo();
+ if (depError != null && depError.isTransitivelyTransient()) {
+ return true;
+ }
+ }
+ return false;
}
Collection<ErrorInfo> getChildErrorInfos() {