Control rewinding via SkyFunction.Environment

SkyFunction implementations need to know whether to return Restart or
fail (with a thrown exception). The Environment provides this
configuration, driven by the GraphInconsistencyReceiver in use during
the evaluation.

RELNOTES: None.
PiperOrigin-RevId: 308191628
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
index e2b1436..cc31b9e 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
@@ -57,6 +57,7 @@
   private final ActionInputPrefetcher actionInputPrefetcher;
   private final ActionKeyContext actionKeyContext;
   private final MetadataHandler metadataHandler;
+  private final boolean rewindingEnabled;
   private final LostInputsCheck lostInputsCheck;
   private final FileOutErr fileOutErr;
   private final ExtendedEventHandler eventHandler;
@@ -79,6 +80,7 @@
       ActionInputPrefetcher actionInputPrefetcher,
       ActionKeyContext actionKeyContext,
       MetadataHandler metadataHandler,
+      boolean rewindingEnabled,
       LostInputsCheck lostInputsCheck,
       FileOutErr fileOutErr,
       ExtendedEventHandler eventHandler,
@@ -93,6 +95,7 @@
     this.actionInputPrefetcher = actionInputPrefetcher;
     this.actionKeyContext = actionKeyContext;
     this.metadataHandler = metadataHandler;
+    this.rewindingEnabled = rewindingEnabled;
     this.lostInputsCheck = lostInputsCheck;
     this.fileOutErr = fileOutErr;
     this.eventHandler = eventHandler;
@@ -115,6 +118,7 @@
       ActionInputPrefetcher actionInputPrefetcher,
       ActionKeyContext actionKeyContext,
       MetadataHandler metadataHandler,
+      boolean rewindingEnabled,
       LostInputsCheck lostInputsCheck,
       FileOutErr fileOutErr,
       ExtendedEventHandler eventHandler,
@@ -130,6 +134,7 @@
         actionInputPrefetcher,
         actionKeyContext,
         metadataHandler,
+        rewindingEnabled,
         lostInputsCheck,
         fileOutErr,
         eventHandler,
@@ -148,6 +153,7 @@
       ActionInputPrefetcher actionInputPrefetcher,
       ActionKeyContext actionKeyContext,
       MetadataHandler metadataHandler,
+      boolean rewindingEnabled,
       LostInputsCheck lostInputsCheck,
       FileOutErr fileOutErr,
       ExtendedEventHandler eventHandler,
@@ -161,6 +167,7 @@
         actionInputPrefetcher,
         actionKeyContext,
         metadataHandler,
+        rewindingEnabled,
         lostInputsCheck,
         fileOutErr,
         eventHandler,
@@ -203,6 +210,10 @@
     return actionFileSystem;
   }
 
+  public boolean isRewindingEnabled() {
+    return rewindingEnabled;
+  }
+
   public void checkForLostInputs() throws LostInputsActionExecutionException {
     lostInputsCheck.checkForLostInputs();
   }
@@ -356,6 +367,7 @@
         actionInputPrefetcher,
         actionKeyContext,
         metadataHandler,
+        rewindingEnabled,
         lostInputsCheck,
         fileOutErr,
         eventHandler,
diff --git a/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
index 5eede88..d6e4b94 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
@@ -306,6 +306,11 @@
     }
 
     @Override
+    public boolean isRewindingEnabled() {
+      return actionExecutionContext.isRewindingEnabled();
+    }
+
+    @Override
     public void checkForLostInputs() throws LostInputsExecException {
       try {
         actionExecutionContext.checkForLostInputs();
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
index ba026da..78b90a6 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
@@ -217,7 +217,10 @@
     @Nullable
     <T extends ActionContext> T getContext(Class<T> identifyingType);
 
-    /** Throws if lost inputs have been detected. */
+    /** Returns whether rewinding is enabled. */
+    boolean isRewindingEnabled();
+
+    /** Throws if rewinding is enabled and lost inputs have been detected. */
     void checkForLostInputs() throws LostInputsExecException;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteOutputService.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteOutputService.java
index ff77d9a..15c0e1f 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteOutputService.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteOutputService.java
@@ -59,7 +59,8 @@
       String relativeOutputPath,
       ImmutableList<Root> sourceRoots,
       ActionInputMap inputArtifactData,
-      Iterable<Artifact> outputArtifacts) {
+      Iterable<Artifact> outputArtifacts,
+      boolean trackFailedRemoteReads) {
     Preconditions.checkNotNull(actionInputFetcher, "actionInputFetcher");
     return new RemoteActionFileSystem(
         sourceDelegate,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
index 2a6d083..9f11c09 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -55,6 +55,7 @@
 import com.google.devtools.build.lib.actions.extra.EnvironmentVariable;
 import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
 import com.google.devtools.build.lib.analysis.skylark.Args;
+import com.google.devtools.build.lib.bugreport.BugReport;
 import com.google.devtools.build.lib.cmdline.LabelConstants;
 import com.google.devtools.build.lib.collect.CollectionUtils;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -605,7 +606,12 @@
       try {
         modules = actionExecutionContext.getNestedSetExpander().toListInterruptibly(transitive);
       } catch (TimeoutException e) {
-        throw handleTimedOutNestedSetExpansion(entry.getKey(), iterator, e);
+        if (actionExecutionContext.isRewindingEnabled()) {
+          throw lostInputsExceptionForTimedOutNestedSetExpansion(entry.getKey(), iterator, e);
+        }
+        BugReport.sendBugReport(e);
+        throw new ActionExecutionException(
+            "Timed out expanding modules", this, /*catastrophe=*/ false);
       }
 
       for (Artifact module : modules) {
@@ -648,7 +654,7 @@
    * we use the .pcm file's exec path since rewinding only uses the digest to detect multiple
    * rewinds of the same input.
    */
-  private LostInputsActionExecutionException handleTimedOutNestedSetExpansion(
+  private LostInputsActionExecutionException lostInputsExceptionForTimedOutNestedSetExpansion(
       Artifact timedOut,
       Iterator<Map.Entry<Artifact, NestedSet<? extends Artifact>>> remainingModules,
       TimeoutException e) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
index 865d850..732de14 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
@@ -284,7 +284,8 @@
             skyframeActionExecutor.createActionFileSystem(
                 directories.getRelativeOutputPath(),
                 checkedInputs.actionInputMap,
-                action.getOutputs());
+                action.getOutputs(),
+                env.restartPermitted());
       }
     }
 
@@ -888,7 +889,8 @@
             ImmutableMap.copyOf(state.topLevelFilesets),
             state.actionFileSystem,
             skyframeDepsResult,
-            env.getListener());
+            env.getListener(),
+            env.restartPermitted());
     ActionExecutionValue result;
     try {
       result =
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ProgressEventSuppressingEnvironment.java b/src/main/java/com/google/devtools/build/lib/skyframe/ProgressEventSuppressingEnvironment.java
index 6983f69..4d23fee 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ProgressEventSuppressingEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ProgressEventSuppressingEnvironment.java
@@ -222,4 +222,9 @@
   public void dependOnFuture(ListenableFuture<?> future) {
     delegate.dependOnFuture(future);
   }
+
+  @Override
+  public boolean restartPermitted() {
+    return delegate.restartPermitted();
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctionEnvironmentForTesting.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctionEnvironmentForTesting.java
index 70ccb5a..894c544 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctionEnvironmentForTesting.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctionEnvironmentForTesting.java
@@ -70,4 +70,9 @@
   public void dependOnFuture(ListenableFuture<?> future) {
     throw new UnsupportedOperationException();
   }
+
+  @Override
+  public boolean restartPermitted() {
+    return false;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
index cc0fa15..125309d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
@@ -296,14 +296,16 @@
   FileSystem createActionFileSystem(
       String relativeOutputPath,
       ActionInputMap inputArtifactData,
-      Iterable<Artifact> outputArtifacts) {
+      Iterable<Artifact> outputArtifacts,
+      boolean trackFailedRemoteReads) {
     return outputService.createActionFileSystem(
         executorEngine.getFileSystem(),
         executorEngine.getExecRoot().asFragment(),
         relativeOutputPath,
         sourceRootSupplier.get(),
         inputArtifactData,
-        outputArtifacts);
+        outputArtifacts,
+        trackFailedRemoteReads);
   }
 
   void updateActionFileSystemContext(
@@ -445,7 +447,8 @@
       ImmutableMap<Artifact, ImmutableList<FilesetOutputSymlink>> topLevelFilesets,
       @Nullable FileSystem actionFileSystem,
       @Nullable Object skyframeDepsResult,
-      ExtendedEventHandler skyframeCachingEventHandler) {
+      ExtendedEventHandler skyframeCachingEventHandler,
+      boolean rewindingEnabled) {
     ArtifactPathResolver artifactPathResolver =
         ArtifactPathResolver.createPathResolver(actionFileSystem, executorEngine.getExecRoot());
     FileOutErr fileOutErr;
@@ -467,6 +470,7 @@
         actionInputPrefetcher,
         actionKeyContext,
         metadataHandler,
+        rewindingEnabled,
         lostInputsCheck(actionFileSystem, action, outputService),
         fileOutErr,
         replayActionOutErr && progressEventBehavior.equals(ProgressEventBehavior.EMIT)
@@ -630,6 +634,7 @@
             actionInputPrefetcher,
             actionKeyContext,
             metadataHandler,
+            env.restartPermitted(),
             lostInputsCheck(actionFileSystem, action, outputService),
             actionLogBufferPathGenerator.generate(
                 ArtifactPathResolver.createPathResolver(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/StateInformingSkyFunctionEnvironment.java b/src/main/java/com/google/devtools/build/lib/skyframe/StateInformingSkyFunctionEnvironment.java
index e3ffb33..59d8182 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/StateInformingSkyFunctionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/StateInformingSkyFunctionEnvironment.java
@@ -267,12 +267,17 @@
     delegate.injectVersionForNonHermeticFunction(version);
   }
 
-  interface Informee {
-    void inform() throws InterruptedException;
-  }
-
   @Override
   public void dependOnFuture(ListenableFuture<?> future) {
     delegate.dependOnFuture(future);
   }
+
+  @Override
+  public boolean restartPermitted() {
+    return delegate.restartPermitted();
+  }
+
+  interface Informee {
+    void inform() throws InterruptedException;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java b/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java
index 7361264..70dc472 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java
@@ -156,6 +156,7 @@
    *     com.google.devtools.build.lib.pkgcache.PathPackageLocator})
    * @param inputArtifactData information about required inputs to the action
    * @param outputArtifacts required outputs of the action
+   * @param trackFailedRemoteReads whether to track failed remote reads to make LostInput exceptions
    * @return an action-scoped filesystem if {@link #supportsActionFileSystem} is not {@code NONE}
    */
   @Nullable
@@ -165,7 +166,8 @@
       String relativeOutputPath,
       ImmutableList<Root> sourceRoots,
       ActionInputMap inputArtifactData,
-      Iterable<Artifact> outputArtifacts) {
+      Iterable<Artifact> outputArtifacts,
+      boolean trackFailedRemoteReads) {
     return null;
   }
 
diff --git a/src/main/java/com/google/devtools/build/skyframe/GraphInconsistencyReceiver.java b/src/main/java/com/google/devtools/build/skyframe/GraphInconsistencyReceiver.java
index ab83ab2..0e80106 100644
--- a/src/main/java/com/google/devtools/build/skyframe/GraphInconsistencyReceiver.java
+++ b/src/main/java/com/google/devtools/build/skyframe/GraphInconsistencyReceiver.java
@@ -46,4 +46,8 @@
     return StringUtil.listItemsWithLimit(new StringBuilder(), LOGGED_CHILDREN_LIMIT, children)
         .toString();
   }
+
+  default boolean restartPermitted() {
+    return false;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/skyframe/ParallelEvaluatorContext.java b/src/main/java/com/google/devtools/build/skyframe/ParallelEvaluatorContext.java
index bdd16b6..f14fb42 100644
--- a/src/main/java/com/google/devtools/build/skyframe/ParallelEvaluatorContext.java
+++ b/src/main/java/com/google/devtools/build/skyframe/ParallelEvaluatorContext.java
@@ -34,6 +34,7 @@
  * <p>Also used during cycle detection.
  */
 class ParallelEvaluatorContext {
+
   enum EnqueueParentBehavior {
     ENQUEUE,
     SIGNAL,
@@ -200,6 +201,10 @@
     return evaluationVersionBehavior;
   }
 
+  boolean restartPermitted() {
+    return graphInconsistencyReceiver.restartPermitted();
+  }
+
   /** Receives the events from the NestedSet and delegates to the reporter. */
   private static class NestedSetEventReceiver implements NestedSetVisitor.Receiver<TaggedEvents> {
 
diff --git a/src/main/java/com/google/devtools/build/skyframe/QueryableGraphBackedSkyFunctionEnvironment.java b/src/main/java/com/google/devtools/build/skyframe/QueryableGraphBackedSkyFunctionEnvironment.java
index 659960e..c07e510 100644
--- a/src/main/java/com/google/devtools/build/skyframe/QueryableGraphBackedSkyFunctionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/skyframe/QueryableGraphBackedSkyFunctionEnvironment.java
@@ -83,4 +83,9 @@
   public boolean inErrorBubblingForTesting() {
     return false;
   }
+
+  @Override
+  public boolean restartPermitted() {
+    return false;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java b/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java
index 01de512..4b55081 100644
--- a/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java
@@ -289,4 +289,9 @@
   public void dependOnFuture(ListenableFuture<?> future) {
     delegate.dependOnFuture(future);
   }
+
+  @Override
+  public boolean restartPermitted() {
+    return delegate.restartPermitted();
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/skyframe/SkyFunction.java b/src/main/java/com/google/devtools/build/skyframe/SkyFunction.java
index 61643a6..1117bc8 100644
--- a/src/main/java/com/google/devtools/build/skyframe/SkyFunction.java
+++ b/src/main/java/com/google/devtools/build/skyframe/SkyFunction.java
@@ -90,6 +90,10 @@
    *
    * <p>All {@link ListenableFuture}s used in calls to {@link Environment#dependOnFuture} which were
    * not already complete will be cancelled.
+   *
+   * <p>This may only be returned by {@link #compute} if {@link Environment#restartPermitted} is
+   * true. If restarting is not permitted, {@link #compute} should throw an appropriate {@link
+   * SkyFunctionException}.
    */
   interface Restart extends SkyValue {
     ImmutableGraph<SkyKey> EMPTY_SKYKEY_GRAPH =
@@ -375,5 +379,11 @@
      * thread pool without blocking the current Skyframe thread.
      */
     void dependOnFuture(ListenableFuture<?> future);
+
+    /**
+     * A {@link SkyFunction#compute} call may return {@link Restart} only if this returns {@code
+     * true}.
+     */
+    boolean restartPermitted();
   }
 }
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 cacc76b..8cacd67 100644
--- a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
@@ -153,6 +153,7 @@
           }
         }
       };
+
   private final ParallelEvaluatorContext evaluatorContext;
 
   SkyFunctionEnvironment(
@@ -870,6 +871,11 @@
         .toString();
   }
 
+  @Override
+  public boolean restartPermitted() {
+    return evaluatorContext.restartPermitted();
+  }
+
   /** Thrown during environment construction if previously requested deps are no longer done. */
   static class UndonePreviouslyRequestedDeps extends Exception {
     private final ImmutableList<SkyKey> depKeys;
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
index f64993b..8c9c672 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
@@ -68,6 +68,7 @@
         ActionInputPrefetcher.NONE,
         actionKeyContext,
         /*metadataHandler=*/ null,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         outErr,
         /*eventHandler=*/ null,
diff --git a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
index 1f73d08..792fe3a 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
@@ -157,6 +157,7 @@
         ActionInputPrefetcher.NONE,
         actionKeyContext,
         metadataHandler,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         fileOutErr,
         eventHandler,
@@ -178,6 +179,7 @@
         ActionInputPrefetcher.NONE,
         new ActionKeyContext(),
         /*metadataHandler=*/ null,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         /*fileOutErr=*/ null,
         eventHandler,
@@ -204,6 +206,7 @@
         ActionInputPrefetcher.NONE,
         actionKeyContext,
         metadataHandler,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         fileOutErr,
         eventHandler,
@@ -321,6 +324,11 @@
     public boolean inErrorBubblingForTesting() {
       return false;
     }
+
+    @Override
+    public boolean restartPermitted() {
+      return false;
+    }
   }
 
   static class NullArtifactOwner implements ArtifactOwner {
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/FileWriteActionTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/FileWriteActionTestCase.java
index 912d858..7ded773 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/FileWriteActionTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/FileWriteActionTestCase.java
@@ -70,6 +70,7 @@
             ActionInputPrefetcher.NONE,
             actionKeyContext,
             /*metadataHandler=*/ null,
+            /*rewindingEnabled=*/ false,
             LostInputsCheck.NONE,
             new FileOutErr(),
             new StoredEventHandler(),
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java
index 8b3fd57..bf5e221 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java
@@ -209,6 +209,7 @@
         ActionInputPrefetcher.NONE,
         actionKeyContext,
         /*metadataHandler=*/ null,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         new FileOutErr(),
         new StoredEventHandler(),
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java
index 4c5b88a..8665a2f 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkActionTest.java
@@ -91,6 +91,7 @@
                 ActionInputPrefetcher.NONE,
                 actionKeyContext,
                 /*metadataHandler=*/ null,
+                /*rewindingEnabled=*/ false,
                 LostInputsCheck.NONE,
                 /*fileOutErr=*/ null,
                 new StoredEventHandler(),
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java
index ba35d5d..3385026 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java
@@ -196,6 +196,7 @@
         ActionInputPrefetcher.NONE,
         actionKeyContext,
         /*metadataHandler=*/ null,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         new FileOutErr(),
         new StoredEventHandler(),
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 0a01821..115bf3e 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -2288,6 +2288,7 @@
           /*actionInputPrefetcher=*/ null,
           actionKeyContext,
           /*metadataHandler=*/ null,
+          /*rewindingEnabled=*/ false,
           LostInputsCheck.NONE,
           actionLogBufferPathGenerator.generate(ArtifactPathResolver.IDENTITY),
           reporter,
diff --git a/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java b/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
index 2c5db86..562ba1a 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
@@ -127,6 +127,7 @@
           ActionInputPrefetcher.NONE,
           new ActionKeyContext(),
           /*metadataHandler=*/ null,
+          /*rewindingEnabled=*/ false,
           LostInputsCheck.NONE,
           fileOutErr,
           /*eventHandler=*/ null,
diff --git a/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java
index aea170a..6bde2b5 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java
@@ -279,6 +279,11 @@
     }
 
     @Override
+    public boolean isRewindingEnabled() {
+      return false;
+    }
+
+    @Override
     public void checkForLostInputs() {}
 
     @Override
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
index 50fe92c..c33b937 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
@@ -209,6 +209,11 @@
         }
 
         @Override
+        public boolean isRewindingEnabled() {
+          return false;
+        }
+
+        @Override
         public void checkForLostInputs() {}
 
         @Override
diff --git a/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java b/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
index 5bab193..67f7d30 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
@@ -165,5 +165,10 @@
   }
 
   @Override
+  public boolean isRewindingEnabled() {
+    return false;
+  }
+
+  @Override
   public void checkForLostInputs() {}
 }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java
index bcdb9c6..e1f6ebc 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java
@@ -128,6 +128,7 @@
         /*actionInputPrefetcher=*/ null,
         /*actionKeyContext=*/ null,
         /*metadataHandler=*/ null,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         /*fileOutErr=*/ null,
         new StoredEventHandler(),
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LtoBackendActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LtoBackendActionTest.java
index 71d0b39..63c6189 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/LtoBackendActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LtoBackendActionTest.java
@@ -91,6 +91,7 @@
             ActionInputPrefetcher.NONE,
             actionKeyContext,
             /*metadataHandler=*/ null,
+            /*rewindingEnabled=*/ false,
             LostInputsCheck.NONE,
             new FileOutErr(),
             new StoredEventHandler(),
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
index 2c93856..a2b4fe4 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/BazelJ2ObjcLibraryTest.java
@@ -796,6 +796,7 @@
             ActionInputPrefetcher.NONE,
             actionKeyContext,
             /*metadataHandler=*/ null,
+            /*rewindingEnabled=*/ false,
             LostInputsCheck.NONE,
             /*fileOutErr=*/ null,
             /*eventHandler=*/ null,
@@ -850,6 +851,7 @@
             ActionInputPrefetcher.NONE,
             actionKeyContext,
             /*metadataHandler=*/ null,
+            /*rewindingEnabled=*/ false,
             LostInputsCheck.NONE,
             /*fileOutErr=*/ null,
             /*eventHandler=*/ null,
diff --git a/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
index 054d413..ff66eb2 100644
--- a/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
@@ -201,6 +201,7 @@
         ActionInputPrefetcher.NONE,
         new ActionKeyContext(),
         /*metadataHandler=*/ null,
+        /*rewindingEnabled=*/ false,
         LostInputsCheck.NONE,
         outErr,
         reporter,
diff --git a/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java b/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java
index 7681d03..bbd73d1 100644
--- a/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java
+++ b/src/test/java/com/google/devtools/build/skyframe/MemoizingEvaluatorTest.java
@@ -59,6 +59,7 @@
 import com.google.devtools.build.skyframe.proto.GraphInconsistency.Inconsistency;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -177,30 +178,31 @@
   protected Set<InconsistencyData> setupGraphInconsistencyReceiver(boolean allowDuplicates) {
     Set<InconsistencyData> inconsistencies = Sets.newConcurrentHashSet();
     tester.setGraphInconsistencyReceiver(
-        (key, otherKeys, inconsistency) -> {
-          if (otherKeys == null) {
-            Preconditions.checkState(
-                inconsistencies.add(
-                        InconsistencyData.create(key, /*otherKey=*/ null, inconsistency))
-                    || allowDuplicates,
-                "Duplicate inconsistency: (%s, %s, %s)\nexisting = %s",
-                key,
-                null,
-                inconsistency,
-                inconsistencies);
-          } else {
-            for (SkyKey otherKey : otherKeys) {
-              Preconditions.checkState(
-                  inconsistencies.add(InconsistencyData.create(key, otherKey, inconsistency))
-                      || allowDuplicates,
-                  "Duplicate inconsistency: (%s, %s, %s)\nexisting = %s",
-                  key,
-                  otherKey,
-                  inconsistency,
-                  inconsistencies);
-            }
-          }
-        });
+        restartEnabledInconsistencyReceiver(
+            (key, otherKeys, inconsistency) -> {
+              if (otherKeys == null) {
+                Preconditions.checkState(
+                    inconsistencies.add(
+                            InconsistencyData.create(key, /*otherKey=*/ null, inconsistency))
+                        || allowDuplicates,
+                    "Duplicate inconsistency: (%s, %s, %s)\nexisting = %s",
+                    key,
+                    null,
+                    inconsistency,
+                    inconsistencies);
+              } else {
+                for (SkyKey otherKey : otherKeys) {
+                  Preconditions.checkState(
+                      inconsistencies.add(InconsistencyData.create(key, otherKey, inconsistency))
+                          || allowDuplicates,
+                      "Duplicate inconsistency: (%s, %s, %s)\nexisting = %s",
+                      key,
+                      otherKey,
+                      inconsistency,
+                      inconsistencies);
+                }
+              }
+            }));
     // #initialize must be called after setting the GraphInconsistencyReceiver for the receiver to
     // be registered with the test's memoizing evaluator.
     tester.initialize(/*keepEdges=*/ true);
@@ -2413,12 +2415,14 @@
     StringValue expectedValue = new StringValue("done");
     AtomicInteger numInconsistencyCalls = new AtomicInteger(0);
     tester.setGraphInconsistencyReceiver(
-        (key, otherKey, inconsistency) -> {
-          Preconditions.checkState(otherKey == null, otherKey);
-          Preconditions.checkState(inconsistency == Inconsistency.RESET_REQUESTED, inconsistency);
-          Preconditions.checkState(restartingKey.equals(key), key);
-          numInconsistencyCalls.incrementAndGet();
-        });
+        restartEnabledInconsistencyReceiver(
+            (key, otherKey, inconsistency) -> {
+              Preconditions.checkState(otherKey == null, otherKey);
+              Preconditions.checkState(
+                  inconsistency == Inconsistency.RESET_REQUESTED, inconsistency);
+              Preconditions.checkState(restartingKey.equals(key), key);
+              numInconsistencyCalls.incrementAndGet();
+            }));
     tester.initialize(/*keepEdges=*/ true);
     AtomicInteger numFunctionCalls = new AtomicInteger(0);
     tester
@@ -2471,12 +2475,14 @@
     SkyKey restartingKey = GraphTester.skyKey("restart");
     AtomicInteger numInconsistencyCalls = new AtomicInteger(0);
     tester.setGraphInconsistencyReceiver(
-        (key, otherKey, inconsistency) -> {
-          Preconditions.checkState(otherKey == null, otherKey);
-          Preconditions.checkState(inconsistency == Inconsistency.RESET_REQUESTED, inconsistency);
-          Preconditions.checkState(restartingKey.equals(key), key);
-          numInconsistencyCalls.incrementAndGet();
-        });
+        restartEnabledInconsistencyReceiver(
+            (key, otherKey, inconsistency) -> {
+              Preconditions.checkState(otherKey == null, otherKey);
+              Preconditions.checkState(
+                  inconsistency == Inconsistency.RESET_REQUESTED, inconsistency);
+              Preconditions.checkState(restartingKey.equals(key), key);
+              numInconsistencyCalls.incrementAndGet();
+            }));
     tester.initialize(mode != RunResetNodeOnRequestWithDepsMode.NO_KEEP_EDGES_SO_NO_REEVALUATION);
     StringValue expectedValue = new StringValue("done");
     SkyKey alreadyRequestedDep = GraphTester.skyKey("alreadyRequested");
@@ -2573,18 +2579,19 @@
     SkyKey missingChild = GraphTester.skyKey("missing");
     AtomicInteger numInconsistencyCalls = new AtomicInteger(0);
     tester.setGraphInconsistencyReceiver(
-        (key, otherKeys, inconsistency) -> {
-          Preconditions.checkState(otherKeys.size() == 1, otherKeys);
-          Preconditions.checkState(
-              missingChild.equals(Iterables.getOnlyElement(otherKeys)),
-              "%s %s",
-              missingChild,
-              otherKeys);
-          Preconditions.checkState(
-              inconsistency == Inconsistency.DIRTY_PARENT_HAD_MISSING_CHILD, inconsistency);
-          Preconditions.checkState(topKey.equals(key), key);
-          numInconsistencyCalls.incrementAndGet();
-        });
+        restartEnabledInconsistencyReceiver(
+            (key, otherKeys, inconsistency) -> {
+              Preconditions.checkState(otherKeys.size() == 1, otherKeys);
+              Preconditions.checkState(
+                  missingChild.equals(Iterables.getOnlyElement(otherKeys)),
+                  "%s %s",
+                  missingChild,
+                  otherKeys);
+              Preconditions.checkState(
+                  inconsistency == Inconsistency.DIRTY_PARENT_HAD_MISSING_CHILD, inconsistency);
+              Preconditions.checkState(topKey.equals(key), key);
+              numInconsistencyCalls.incrementAndGet();
+            }));
     tester.initialize(/*keepEdges=*/ true);
     tester.getOrCreate(missingChild).setConstantValue(new StringValue("will go missing"));
     SkyKey presentChild = GraphTester.nonHermeticKey("present");
@@ -5430,4 +5437,20 @@
       return null;
     }
   }
+
+  private static GraphInconsistencyReceiver restartEnabledInconsistencyReceiver(
+      GraphInconsistencyReceiver delegate) {
+    return new GraphInconsistencyReceiver() {
+      @Override
+      public void noteInconsistencyAndMaybeThrow(
+          SkyKey key, @Nullable Collection<SkyKey> otherKeys, Inconsistency inconsistency) {
+        delegate.noteInconsistencyAndMaybeThrow(key, otherKeys, inconsistency);
+      }
+
+      @Override
+      public boolean restartPermitted() {
+        return true;
+      }
+    };
+  }
 }