NullSocketServer::Wait: Don't warn if we have to wait a long time for messages

Make the warning timeout for Event::Wait configurable, and let
NullSocketServer::Wait pass kForever to completely eliminate the
warning.

3000 ms is a good default warning timeout for Event::Wait, but in some
cases---such as when a message queue is waiting for a message to
arrive---we don't want the warning, since a long wait isn't a reliable
indicator that the system is deadlocked. It might just be that no one
is posting messages.

Bug: webrtc:10531
Change-Id: Ic5969b8bfedb96376bd6d6a72ba6a4591750a920
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/132017
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27574}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 6a2c5aa..05cecd1 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -265,6 +265,7 @@
       ":checks",
       "synchronization:yield_policy",
       "system:warn_current_thread_is_deadlocked",
+      "//third_party/abseil-cpp/absl/types:optional",
     ]
   }
 }
diff --git a/rtc_base/event.cc b/rtc_base/event.cc
index 4b8e9ff..67c8746 100644
--- a/rtc_base/event.cc
+++ b/rtc_base/event.cc
@@ -21,6 +21,7 @@
 #error "Must define either WEBRTC_WIN or WEBRTC_POSIX."
 #endif
 
+#include "absl/types/optional.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/synchronization/yield_policy.h"
 #include "rtc_base/system/warn_current_thread_is_deadlocked.h"
@@ -50,9 +51,9 @@
   ResetEvent(event_handle_);
 }
 
-bool Event::Wait(int milliseconds) {
+bool Event::Wait(const int give_up_after_ms, int /*warn_after_ms*/) {
   ScopedYieldPolicy::YieldExecution();
-  DWORD ms = (milliseconds == kForever) ? INFINITE : milliseconds;
+  const DWORD ms = give_up_after_ms == kForever ? INFINITE : give_up_after_ms;
   return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0);
 }
 
@@ -135,34 +136,54 @@
 
 }  // namespace
 
-bool Event::Wait(const int milliseconds) {
-  // Set a timeout for the given number of milliseconds, or 3000 ms if the
-  // caller asked for kForever.
-  const timespec ts =
-      GetTimespec(milliseconds == kForever ? 3000 : milliseconds);
+bool Event::Wait(const int give_up_after_ms, const int warn_after_ms) {
+  // Instant when we'll log a warning message (because we've been waiting so
+  // long it might be a bug), but not yet give up waiting. nullopt if we
+  // shouldn't log a warning.
+  const absl::optional<timespec> warn_ts =
+      warn_after_ms == kForever ||
+              (give_up_after_ms != kForever && warn_after_ms > give_up_after_ms)
+          ? absl::nullopt
+          : absl::make_optional(GetTimespec(warn_after_ms));
+
+  // Instant when we'll stop waiting and return an error. nullopt if we should
+  // never give up.
+  const absl::optional<timespec> give_up_ts =
+      give_up_after_ms == kForever
+          ? absl::nullopt
+          : absl::make_optional(GetTimespec(give_up_after_ms));
 
   ScopedYieldPolicy::YieldExecution();
   pthread_mutex_lock(&event_mutex_);
 
-  // Wait for the event to trigger or the timeout to expire, whichever comes
-  // first.
-  int error = 0;
-  while (!event_status_ && error == 0) {
-#if USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
-    error =
-        pthread_cond_timedwait_monotonic_np(&event_cond_, &event_mutex_, &ts);
-#else
-    error = pthread_cond_timedwait(&event_cond_, &event_mutex_, &ts);
-#endif
-  }
-
-  if (milliseconds == kForever && error == ETIMEDOUT) {
-    // Our 3000 ms timeout expired, but the caller asked us to wait forever, so
-    // do that.
-    webrtc::WarnThatTheCurrentThreadIsProbablyDeadlocked();
-    error = 0;
+  // Wait for `event_cond_` to trigger and `event_status_` to be set, with the
+  // given timeout (or without a timeout if none is given).
+  const auto wait = [&](const absl::optional<timespec> timeout_ts) {
+    int error = 0;
     while (!event_status_ && error == 0) {
-      error = pthread_cond_wait(&event_cond_, &event_mutex_);
+      if (timeout_ts == absl::nullopt) {
+        error = pthread_cond_wait(&event_cond_, &event_mutex_);
+      } else {
+#if USE_PTHREAD_COND_TIMEDWAIT_MONOTONIC_NP
+        error = pthread_cond_timedwait_monotonic_np(&event_cond_, &event_mutex_,
+                                                    &*timeout_ts);
+#else
+        error =
+            pthread_cond_timedwait(&event_cond_, &event_mutex_, &*timeout_ts);
+#endif
+      }
+    }
+    return error;
+  };
+
+  int error;
+  if (warn_ts == absl::nullopt) {
+    error = wait(give_up_ts);
+  } else {
+    error = wait(warn_ts);
+    if (error == ETIMEDOUT) {
+      webrtc::WarnThatTheCurrentThreadIsProbablyDeadlocked();
+      error = wait(give_up_ts);
     }
   }
 
diff --git a/rtc_base/event.h b/rtc_base/event.h
index 3f5f8b3..584ad5d 100644
--- a/rtc_base/event.h
+++ b/rtc_base/event.h
@@ -34,9 +34,21 @@
   void Set();
   void Reset();
 
-  // Wait for the event to become signaled, for the specified number of
-  // |milliseconds|.  To wait indefinetly, pass kForever.
-  bool Wait(int milliseconds);
+  // Waits for the event to become signaled, but logs a warning if it takes more
+  // than `warn_after_ms` milliseconds, and gives up completely if it takes more
+  // than `give_up_after_ms` milliseconds. (If `warn_after_ms >=
+  // give_up_after_ms`, no warning will be logged.) Either or both may be
+  // `kForever`, which means wait indefinitely.
+  //
+  // Returns true if the event was signaled, false if there was a timeout or
+  // some other error.
+  bool Wait(int give_up_after_ms, int warn_after_ms);
+
+  // Waits with the given timeout and a reasonable default warning timeout.
+  bool Wait(int give_up_after_ms) {
+    return Wait(give_up_after_ms,
+                give_up_after_ms == kForever ? 3000 : kForever);
+  }
 
  private:
 #if defined(WEBRTC_WIN)
diff --git a/rtc_base/null_socket_server.cc b/rtc_base/null_socket_server.cc
index f446b3d..b2071e3 100644
--- a/rtc_base/null_socket_server.cc
+++ b/rtc_base/null_socket_server.cc
@@ -17,7 +17,10 @@
 NullSocketServer::~NullSocketServer() {}
 
 bool NullSocketServer::Wait(int cms, bool process_io) {
-  event_.Wait(cms);
+  // Wait with the given timeout. Do not log a warning if we end up waiting for
+  // a long time; that just means no one has any work for us, which is perfectly
+  // legitimate.
+  event_.Wait(/*give_up_after_ms=*/cms, /*warn_after_ms=*/Event::kForever);
   return true;
 }