Make the action completion watchdog wait instead of waiting for the first
possible action completion to reduce contention. This isn't very pretty, but I
think should be good enough.

RELNOTES: None.
PiperOrigin-RevId: 209111759
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java
index 484298c..4a5709d 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java
@@ -18,7 +18,6 @@
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
 import com.google.devtools.build.lib.actions.ActionExecutionStatusReporter;
 import com.google.devtools.build.lib.actions.ActionLookupData;
-import com.google.devtools.build.lib.clock.BlazeClock;
 import com.google.devtools.build.lib.skyframe.ActionExecutionInactivityWatchdog;
 import com.google.devtools.build.lib.skyframe.AspectCompletionValue;
 import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey;
@@ -61,7 +60,6 @@
   private final Set<ActionLookupData> completedActions = Sets.newConcurrentHashSet();
   private final Set<ActionLookupData> ignoredActions = Sets.newConcurrentHashSet();
 
-  private final Object activityIndicator = new Object();
   /** Number of exclusive tests. To be accounted for in progress messages. */
   private final int exclusiveTestsCount;
 
@@ -140,9 +138,6 @@
   public void actionCompleted(ActionLookupData actionLookupData) {
     if (!ignoredActions.contains(actionLookupData)) {
       completedActions.add(actionLookupData);
-      synchronized (activityIndicator) {
-        activityIndicator.notifyAll();
-      }
     }
   }
 
@@ -175,28 +170,17 @@
       }
 
       @Override
-      public int waitForNextCompletion(int timeoutMilliseconds) throws InterruptedException {
-        long rest = timeoutMilliseconds;
-        synchronized (activityIndicator) {
-          int before = completedActions.size();
-          long startTime = BlazeClock.instance().currentTimeMillis();
-          while (true) {
-            activityIndicator.wait(rest);
-
-            int completed = completedActions.size() - before;
-            long now = 0;
-            if (completed > 0
-                || (startTime + rest)
-                    <= (now = BlazeClock.instance().currentTimeMillis())) {
-              // Some actions completed, or timeout fully elapsed.
-              return completed;
-            } else {
-              // Spurious Wakeup -- no actions completed and there's still time to wait.
-              rest -= now - startTime; // account for elapsed wait time
-              startTime = now;
-            }
+      public int waitForNextCompletion(int timeoutSeconds) throws InterruptedException {
+        int before = completedActions.size();
+        // Otherwise, wake up once per second to see whether something completed.
+        for (int i = 0; i < timeoutSeconds; i++) {
+          Thread.sleep(1000);
+          int count = completedActions.size() - before;
+          if (count > 0) {
+            return count;
           }
         }
+        return 0;
       }
     };
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdog.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdog.java
index 94e12fb..35446dc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdog.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdog.java
@@ -45,7 +45,7 @@
      *
      * @return the number of actions completed during the wait
      */
-    int waitForNextCompletion(int timeoutMilliseconds) throws InterruptedException;
+    int waitForNextCompletion(int timeoutSeconds) throws InterruptedException;
   }
 
   /** An object that the watchdog can report inactivity to. */
@@ -145,7 +145,7 @@
         // Wait a while for any SkyFunction to finish. The returned number indicates how many
         // actions completed during the wait. It's possible that this is more than 1, since
         // this thread may not immediately regain control.
-        int completedActions = monitor.waitForNextCompletion(waitTime.next() * 1000);
+        int completedActions = monitor.waitForNextCompletion(waitTime.next());
         if (!isRunning.get()) {
           break;
         }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdogTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdogTest.java
index ee6f419..986166b 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdogTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionInactivityWatchdogTest.java
@@ -49,7 +49,7 @@
     InactivityMonitor monitor =
         new InactivityMonitor() {
           @Override
-          public int waitForNextCompletion(int timeoutMilliseconds) throws InterruptedException {
+          public int waitForNextCompletion(int timeoutSeconds) throws InterruptedException {
             // Simulate the following sequence of events (see actionCompletions):
             // 1. return in 5s (within timeout), 1 action completed; caller will sleep
             // 2. return in 10s (after timeout), 0 action completed; caller will wait