Implement basic support for rewinding top-level outputs.
Note that this is all still no-op because nothing overrides the installation of `ImportantOutputHandler#NO_OP`, except in tests.
`ActionRewindStrategy` now offers two public `prepareRewindPlan*` methods: one for lost inputs (called from `ActionExecutionFunction`) and one for lost top-level outputs (called from `CompletionFunction`). They both delegate to the same helper method to compute the rewind graph.
This change only supports regular output files. Notably, tree artifacts and filesets will require further work.
PiperOrigin-RevId: 605428885
Change-Id: Ibd8bfd613eb1e1cecec72ec2d84ff09f2680974f
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionState.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionState.java
index b9dca2f..3f59158 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionState.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionState.java
@@ -27,6 +27,7 @@
import com.google.devtools.build.lib.skyframe.ActionExecutionValue.ActionTransformException;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
+import com.google.devtools.build.skyframe.SkyKey;
import com.google.errorprone.annotations.DoNotCall;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
@@ -201,48 +202,46 @@
* with a reference to this state will restart, and signals to coalesced shared actions that they
* should re-evaluate.
*/
- void obsolete(
- ActionLookupData requester,
+ synchronized void obsolete(
+ SkyKey requester,
ConcurrentMap<OwnerlessArtifactWrapper, ActionExecutionState> buildActionMap,
OwnerlessArtifactWrapper ownerlessArtifactWrapper) {
- synchronized (this) {
- if (actionLookupData.equals(requester)) {
- // An action state's owner only obsoletes it when rewinding. The lost inputs exception
- // thrown from ActionStepOrResult#run left its state undone.
- Preconditions.checkState(
- !state.isDone(), "owner unexpectedly obsoleted done state: %s", actionLookupData);
- ActionExecutionState removedState = buildActionMap.remove(ownerlessArtifactWrapper);
- Preconditions.checkState(
- removedState == this,
- "owner removed unexpected state from buildActionMap; owner: %s, removed: %s",
- actionLookupData,
- removedState.actionLookupData);
- state = Obsolete.INSTANCE;
- if (completionFuture != null) {
- completionFuture.set(null);
- completionFuture = null;
- }
- return;
- }
- if (!state.isDone()) {
- // An action obsoletes other actions' states when rewinding its dependencies. It may race
- // with other actions to do so. Removing the buildActionMap entry must only be done by the
- // race's winner, to ensure the removal only happens once and removes this state.
- //
- // An action may also attempt to obsolete a dependency's not-done state, if it lost the race
- // with another rewinding action, and the dep started evaluating. If so, then do nothing,
- // because that dep is already doing what it needs to.
- return;
- }
+ if (actionLookupData.equals(requester)) {
+ // An action state's owner only obsoletes it when rewinding. The lost inputs exception thrown
+ // from ActionStepOrResult#run left its state undone.
+ Preconditions.checkState(
+ !state.isDone(), "owner unexpectedly obsoleted done state: %s", actionLookupData);
ActionExecutionState removedState = buildActionMap.remove(ownerlessArtifactWrapper);
Preconditions.checkState(
removedState == this,
- "removed unexpected state from buildActionMap; requester: %s, this: %s, removed: %s",
- requester,
+ "owner removed unexpected state from buildActionMap; owner: %s, removed: %s",
actionLookupData,
removedState.actionLookupData);
state = Obsolete.INSTANCE;
+ if (completionFuture != null) {
+ completionFuture.set(null);
+ completionFuture = null;
+ }
+ return;
}
+ if (!state.isDone()) {
+ // An action obsoletes other actions' states when rewinding its dependencies. It may race with
+ // other actions to do so. Removing the buildActionMap entry must only be done by the race's
+ // winner, to ensure the removal only happens once and removes this state.
+ //
+ // An action may also attempt to obsolete a dependency's not-done state, if it lost the race
+ // with another rewinding action, and the dep started evaluating. If so, then do nothing,
+ // because that dep is already doing what it needs to.
+ return;
+ }
+ ActionExecutionState removedState = buildActionMap.remove(ownerlessArtifactWrapper);
+ Preconditions.checkState(
+ removedState == this,
+ "removed unexpected state from buildActionMap; requester: %s, this: %s, removed: %s",
+ requester,
+ actionLookupData,
+ removedState.actionLookupData);
+ state = Obsolete.INSTANCE;
}
/** A callback to receive events for shared actions that are not executed. */