Send ActionScanningCompletedEvent

...for action-discovering shared actions that were not actually run.

PiperOrigin-RevId: 247893549
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 c02d2c9..7b60063 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
@@ -18,7 +18,6 @@
 import com.google.devtools.build.lib.actions.Action;
 import com.google.devtools.build.lib.actions.ActionExecutionException;
 import com.google.devtools.build.lib.actions.ActionLookupData;
-import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor.ActionCompletedReceiver;
 import com.google.devtools.build.skyframe.SkyFunction;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.GuardedBy;
@@ -70,7 +69,7 @@
       SkyFunction.Environment env,
       ActionLookupData actionLookupData,
       Action action,
-      ActionCompletedReceiver actionCompletedReceiver)
+      SharedActionCallback sharedActionCallback)
       throws ActionExecutionException, InterruptedException {
     if (this.actionLookupData.equals(actionLookupData)) {
       // This continuation is owned by the Skyframe node executed by the current thread, so we use
@@ -87,6 +86,9 @@
         if (completionFuture == null) {
           completionFuture = SettableFuture.create();
         }
+        // We expect to only call this once per shared action; this method should only be called
+        // again after the future is completed.
+        sharedActionCallback.actionStarted();
         env.dependOnFuture(completionFuture);
         // No other thread can access completionFuture until we exit the synchronized block.
         Preconditions.checkState(!completionFuture.isDone(), state);
@@ -95,7 +97,7 @@
       }
       result = state.get();
     }
-    actionCompletedReceiver.actionCompleted(actionLookupData);
+    sharedActionCallback.actionCompleted();
     return result.transformForSharedAction(action.getOutputs());
   }
 
@@ -134,6 +136,18 @@
     return current.get();
   }
 
+  /** A callback to receive events for shared actions that are not executed. */
+  public interface SharedActionCallback {
+    /** Called if the action is shared and the primary action is already executing. */
+    void actionStarted();
+
+    /**
+     * Called when the primary action is done (on the next call to {@link
+     * #getResultOrDependOnFuture}.
+     */
+    void actionCompleted();
+  }
+
   /**
    * A state machine where instances of this interface either represent an intermediate state that
    * requires more work to be done (possibly waiting for a ListenableFuture to complete) or the