Don't let multiple threads queue up before the synchronized-block in
ExperimentalEventHandler. Especially, when a lot of actions are in flight
(which happens with the experimental async execution), the runtime of
addProgressBar() might exceed the update rate limit leading to more and more
threads queuing up. Instead, use tryLock to only update if no other update is
in flight. This is probably more a work-around than a fix.

RELNOTES: None.
PiperOrigin-RevId: 252994129
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java
index 2d58308..9a048be 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java
@@ -68,6 +68,8 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Logger;
 import javax.annotation.Nullable;
 
@@ -115,6 +117,7 @@
   private boolean progressBarNeedsRefresh;
   private volatile boolean shutdown;
   private final AtomicReference<Thread> updateThread;
+  private final Lock updateLock;
   private byte[] stdoutBuffer;
   private byte[] stderrBuffer;
   private final Set<String> messagesSeen;
@@ -282,6 +285,7 @@
     this.stderrBuffer = new byte[] {};
     this.dateShown = false;
     this.updateThread = new AtomicReference<>();
+    this.updateLock = new ReentrantLock();
     // The progress bar has not been updated yet.
     ignoreRefreshLimitOnce();
   }
@@ -911,35 +915,39 @@
     }
     long nowMillis = clock.currentTimeMillis();
     if (lastRefreshMillis + minimalDelayMillis < nowMillis) {
-      synchronized (this) {
+      if (updateLock.tryLock()) {
         try {
-          if (showProgress && (progressBarNeedsRefresh || timeBasedRefresh())) {
-            progressBarNeedsRefresh = false;
-            clearProgressBar();
-            addProgressBar();
-            terminal.flush();
-            double remaining = remainingCapacity();
-            if (remaining < CAPACITY_INCREASE_UPDATE_DELAY) {
-              // Increase the update interval if the start producing too much output
-              minimalDelayMillis = Math.max(minimalDelayMillis, 1000);
-              if (remaining < CAPACITY_UPDATE_DELAY_5_SECONDS) {
-                minimalDelayMillis = Math.max(minimalDelayMillis, 5000);
+          synchronized (this) {
+            if (showProgress && (progressBarNeedsRefresh || timeBasedRefresh())) {
+              progressBarNeedsRefresh = false;
+              clearProgressBar();
+              addProgressBar();
+              terminal.flush();
+              double remaining = remainingCapacity();
+              if (remaining < CAPACITY_INCREASE_UPDATE_DELAY) {
+                // Increase the update interval if the start producing too much output
+                minimalDelayMillis = Math.max(minimalDelayMillis, 1000);
+                if (remaining < CAPACITY_UPDATE_DELAY_5_SECONDS) {
+                  minimalDelayMillis = Math.max(minimalDelayMillis, 5000);
+                }
               }
-            }
-            if (!cursorControl || remaining < CAPACITY_UPDATE_DELAY_AS_NO_CURSES) {
-              // If we can't update the progress bar in place, make sure we increase the update
-              // interval as time progresses, to avoid too many progress messages in place.
-              minimalDelayMillis =
-                  Math.max(
-                      minimalDelayMillis,
-                      Math.round(
-                          NO_CURSES_MINIMAL_RELATIVE_PROGRESS_RATE_LMIT
-                              * (clock.currentTimeMillis() - uiStartTimeMillis)));
-              minimalUpdateInterval = Math.max(minimalDelayMillis, MAXIMAL_UPDATE_DELAY_MILLIS);
+              if (!cursorControl || remaining < CAPACITY_UPDATE_DELAY_AS_NO_CURSES) {
+                // If we can't update the progress bar in place, make sure we increase the update
+                // interval as time progresses, to avoid too many progress messages in place.
+                minimalDelayMillis =
+                    Math.max(
+                        minimalDelayMillis,
+                        Math.round(
+                            NO_CURSES_MINIMAL_RELATIVE_PROGRESS_RATE_LMIT
+                                * (clock.currentTimeMillis() - uiStartTimeMillis)));
+                minimalUpdateInterval = Math.max(minimalDelayMillis, MAXIMAL_UPDATE_DELAY_MILLIS);
+              }
             }
           }
         } catch (IOException e) {
           logger.warning("IO Error writing to output stream: " + e);
+        } finally {
+          updateLock.unlock();
         }
       }
     } else {