Remove batch timeout handling in the runner.

Centipede has been able to handle the batch timeout instead.

PiperOrigin-RevId: 949737426
diff --git a/centipede/BUILD b/centipede/BUILD
index 683b2af..518d7a6 100644
--- a/centipede/BUILD
+++ b/centipede/BUILD
@@ -217,6 +217,7 @@
         "@abseil-cpp//absl/strings",
         "@abseil-cpp//absl/strings:str_format",
         "@abseil-cpp//absl/synchronization",
+        "@abseil-cpp//absl/time",
         "@abseil-cpp//absl/types:span",
         "@com_google_fuzztest//common:defs",
         "@com_google_fuzztest//common:hash",
@@ -1394,6 +1395,7 @@
         ":thread_pool",
         ":util",
         "@abseil-cpp//absl/container:flat_hash_map",
+        "@abseil-cpp//absl/time",
         "@com_google_fuzztest//common:defs",
         "@com_google_fuzztest//common:hash",
         "@com_google_fuzztest//common:logging",
diff --git a/centipede/centipede_callbacks.cc b/centipede/centipede_callbacks.cc
index b03b48a..c615992 100644
--- a/centipede/centipede_callbacks.cc
+++ b/centipede/centipede_callbacks.cc
@@ -62,8 +62,6 @@
 
 namespace fuzztest::internal {
 
-constexpr auto kPollMinimalTimeout = absl::Milliseconds(1);
-
 class CentipedeCallbacks::PersistentModeServer {
  public:
   explicit PersistentModeServer(std::string server_path)
@@ -194,9 +192,7 @@
     int poll_ret = -1;
     do {
       poll_fd = {fd, static_cast<short>(event)};
-      const int poll_timeout_ms = static_cast<int>(absl::ToInt64Milliseconds(
-          std::max(deadline - absl::Now(), kPollMinimalTimeout)));
-      poll_ret = poll(&poll_fd, 1, poll_timeout_ms);
+      poll_ret = poll(&poll_fd, 1, PollTimeoutMs(deadline - absl::Now()));
     } while (poll_ret < 0 && errno == EINTR);
     if (poll_ret == 1 && (poll_fd.revents & (event | POLLHUP)) == event) {
       return true;
@@ -343,7 +339,6 @@
   std::vector<std::string> flags = {
       "CENTIPEDE_RUNNER_FLAGS=",
       absl::StrCat("timeout_per_input=", env_.timeout_per_input),
-      absl::StrCat("timeout_per_batch=", env_.timeout_per_batch),
       absl::StrCat("address_space_limit_mb=", env_.address_space_limit_mb),
       absl::StrCat("rss_limit_mb=", env_.rss_limit_mb),
       absl::StrCat("stack_limit_kb=", env_.stack_limit_kb),
@@ -565,8 +560,12 @@
   }
 
   // Run.
+  const auto batch_start_time = absl::Now();
   const int exit_code = RunBatchForBinary(binary);
   inputs_blobseq_.ReleaseSharedMemory();  // Inputs are already consumed.
+  const bool batch_timed_out =
+      env_.timeout_per_batch > 0 &&
+      absl::Now() - batch_start_time > absl::Seconds(env_.timeout_per_batch);
 
   // Get results.
   batch_result.exit_code() = exit_code;
@@ -591,6 +590,16 @@
 
   if (env_.print_runner_log) PrintExecutionLog();
 
+  if (batch_timed_out) {
+    FUZZTEST_LOG(INFO)
+        << "Batch timeout detected. Replacing the original exit code: "
+        << exit_code
+        << ", failure description: " << batch_result.failure_description();
+    batch_result.failure_description() = kExecutionFailurePerBatchTimeout;
+    batch_result.exit_code() = EXIT_FAILURE;
+    return EXIT_FAILURE;
+  }
+
   // TODO: b/467103298 - Handle failures when the exit code is zero, e.g., when
   // the target exits via `std::_Exit(0)`.
   if (exit_code != EXIT_SUCCESS) {
diff --git a/centipede/command.cc b/centipede/command.cc
index 4afab0a..06cc79f 100644
--- a/centipede/command.cc
+++ b/centipede/command.cc
@@ -146,9 +146,7 @@
           /*fd=*/pipe_[1],
           /*events=*/POLLIN,
       };
-      const int poll_timeout_ms = static_cast<int>(absl::ToInt64Milliseconds(
-          std::max(deadline - absl::Now(), absl::Milliseconds(1))));
-      poll_ret = poll(&poll_fd, 1, poll_timeout_ms);
+      poll_ret = poll(&poll_fd, 1, PollTimeoutMs(deadline - absl::Now()));
       // The `poll()` syscall can get interrupted: it sets errno==EINTR in that
       // case. We should tolerate that.
     } while (poll_ret < 0 && errno == EINTR);
diff --git a/centipede/puzzles/run_puzzle.sh b/centipede/puzzles/run_puzzle.sh
index 0aee624..d6d12c8 100755
--- a/centipede/puzzles/run_puzzle.sh
+++ b/centipede/puzzles/run_puzzle.sh
@@ -94,7 +94,6 @@
 # Expects that Centipede found a per-batch timeout.
 function ExpectPerBatchTimeout() {
   echo "======= ${FUNCNAME[0]}"
-  fuzztest::internal::assert_regex_in_file "Per-batch timeout exceeded" "${log}"
   fuzztest::internal::assert_regex_in_file "Failure.*: per-batch-timeout-exceeded" "${log}"
   fuzztest::internal::assert_regex_in_file \
     "Failure applies to entire batch: not executing inputs one-by-one" "${log}"
diff --git a/centipede/runner.cc b/centipede/runner.cc
index e085b54..82b2b49 100644
--- a/centipede/runner.cc
+++ b/centipede/runner.cc
@@ -132,8 +132,7 @@
     const char *failure;
   };
   const uint64_t input_start_time = state->input_start_time;
-  const uint64_t batch_start_time = state->batch_start_time;
-  if (input_start_time == 0 || batch_start_time == 0) return;
+  if (input_start_time == 0) return;
   const Resource resources[] = {
       {Resource{
           /*what=*/"Per-input timeout",
@@ -145,15 +144,6 @@
           /*failure=*/kExecutionFailurePerInputTimeout.data(),
       }},
       {Resource{
-          /*what=*/"Per-batch timeout",
-          /*units=*/"sec",
-          /*value=*/curr_time - batch_start_time,
-          /*limit=*/state->run_time_flags.timeout_per_batch,
-          /*ignore_report=*/
-          state->run_time_flags.ignore_timeout_reports != 0,
-          /*failure=*/kExecutionFailurePerBatchTimeout.data(),
-      }},
-      {Resource{
           /*what=*/"RSS limit",
           /*units=*/"MB",
           /*value=*/GetPeakRSSMb(),
@@ -240,10 +230,10 @@
 void GlobalRunnerState::StartWatchdogThread() {
   fprintf(stderr,
           "Starting watchdog thread: timeout_per_input: %" PRIu64
-          " sec; timeout_per_batch: %" PRIu64 " sec; rss_limit_mb: %" PRIu64
-          " MB; stack_limit_kb: %" PRIu64 " KB\n",
+          " sec; rss_limit_mb: %" PRIu64 " MB; stack_limit_kb: %" PRIu64
+          " KB\n",
           run_time_flags.timeout_per_input.load(),
-          run_time_flags.timeout_per_batch, run_time_flags.rss_limit_mb.load(),
+          run_time_flags.rss_limit_mb.load(),
           state->run_time_flags.stack_limit_kb.load());
   pthread_t watchdog_thread;
   pthread_create(&watchdog_thread, nullptr, WatchdogThread, nullptr);
@@ -257,11 +247,6 @@
 void GlobalRunnerState::ResetTimers() {
   const auto curr_time = time(nullptr);
   state->input_start_time = curr_time;
-  // batch_start_time is set only once -- just before the first input of the
-  // batch is about to start running.
-  if (batch_start_time == 0) {
-    batch_start_time = curr_time;
-  }
 }
 
 // Byte array mutation fallback for a custom mutator, as defined here:
@@ -994,7 +979,6 @@
   }
   in_execution_batch = false;
   fuzztest::internal::state->input_start_time = 0;
-  fuzztest::internal::state->batch_start_time = 0;
 }
 
 extern "C" void CentipedePrepareProcessing() {
diff --git a/centipede/runner.h b/centipede/runner.h
index fbe0941..5ddc3b3 100644
--- a/centipede/runner.h
+++ b/centipede/runner.h
@@ -33,7 +33,6 @@
 // Flags derived from CENTIPEDE_RUNNER_FLAGS.
 struct RunTimeFlags {
   std::atomic<uint64_t> timeout_per_input;
-  uint64_t timeout_per_batch;
   std::atomic<uint64_t> rss_limit_mb;
   uint64_t crossover_level;
   uint64_t ignore_timeout_reports : 1;
@@ -67,7 +66,6 @@
   // flags can change later (if wrapped with std::atomic).
   RunTimeFlags run_time_flags = {
       /*timeout_per_input=*/flag_helper.HasIntFlag(":timeout_per_input=", 0),
-      /*timeout_per_batch=*/flag_helper.HasIntFlag(":timeout_per_batch=", 0),
       /*rss_limit_mb=*/flag_helper.HasIntFlag(":rss_limit_mb=", 0),
       /*crossover_level=*/flag_helper.HasIntFlag(":crossover_level=", 50),
       /*ignore_timeout_reports=*/
@@ -112,10 +110,6 @@
   // time.
   std::atomic<time_t> input_start_time;
 
-  // Per-batch timer. Initially, zero. ResetInputTimer() sets it to the current
-  // time before the first input and never resets it.
-  std::atomic<time_t> batch_start_time;
-
   // The Watchdog thread sets this to true.
   std::atomic<bool> watchdog_thread_started;
 };
diff --git a/centipede/util.cc b/centipede/util.cc
index 83da9e7..ab2eb19 100644
--- a/centipede/util.cc
+++ b/centipede/util.cc
@@ -32,6 +32,7 @@
 #include <fstream>
 #include <functional>
 #include <ios>
+#include <limits>
 #include <queue>
 #include <random>
 #include <sstream>
@@ -50,6 +51,7 @@
 #include "absl/strings/str_replace.h"
 #include "absl/strings/str_split.h"
 #include "absl/synchronization/mutex.h"
+#include "absl/time/time.h"
 #include "absl/types/span.h"
 #include "./centipede/feature.h"
 #include "./common/defs.h"
@@ -369,4 +371,16 @@
   FUZZTEST_CHECK_EQ(result, 0);
 }
 
+int PollTimeoutMs(absl::Duration timeout) {
+  if (timeout == absl::InfiniteDuration()) {
+    return -1;
+  }
+  const auto ms = absl::ToInt64Milliseconds(absl::Ceil(
+      std::max(timeout, absl::Milliseconds(1)), absl::Milliseconds(1)));
+  if (ms > std::numeric_limits<int>::max()) {
+    return std::numeric_limits<int>::max();
+  }
+  return static_cast<int>(ms);
+}
+
 }  // namespace fuzztest::internal
diff --git a/centipede/util.h b/centipede/util.h
index ac5e963..4905c68 100644
--- a/centipede/util.h
+++ b/centipede/util.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "absl/base/nullability.h"
+#include "absl/time/time.h"
 #include "absl/types/span.h"
 #include "./centipede/feature.h"
 #include "./common/defs.h"
@@ -192,6 +193,9 @@
   uint8_t *array_;
 };
 
+// Converts `timeout` to an integer value of milliseconds suitable for `poll()`.
+int PollTimeoutMs(absl::Duration timeout);
+
 }  // namespace fuzztest::internal
 
 #endif  // THIRD_PARTY_CENTIPEDE_UTIL_H_
diff --git a/centipede/util_test.cc b/centipede/util_test.cc
index 7be77e8..1629f92 100644
--- a/centipede/util_test.cc
+++ b/centipede/util_test.cc
@@ -17,6 +17,7 @@
 #include <cstdint>
 #include <cstdlib>
 #include <filesystem>  // NOLINT
+#include <limits>
 #include <map>
 #include <string>
 #include <vector>
@@ -24,6 +25,7 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include "absl/container/flat_hash_map.h"
+#include "absl/time/time.h"
 #include "./centipede/feature.h"
 #include "./centipede/thread_pool.h"
 #include "./common/defs.h"
@@ -291,4 +293,15 @@
               testing::ElementsAre(std::vector<int>{1}, std::vector<int>{3}));
 }
 
+TEST(UtilTest, PollTimeoutMsWorks) {
+  EXPECT_GT(PollTimeoutMs(absl::ZeroDuration()), 0);
+  EXPECT_GT(PollTimeoutMs(-absl::InfiniteDuration()), 0);
+  EXPECT_EQ(PollTimeoutMs(absl::InfiniteDuration()), -1);
+  EXPECT_EQ(PollTimeoutMs(absl::Milliseconds(123)), 123);
+  const auto long_finite_duration =
+      absl::Seconds(std::numeric_limits<int64_t>::max());
+  FUZZTEST_CHECK_LT(long_finite_duration, absl::InfiniteDuration());
+  EXPECT_GT(PollTimeoutMs(long_finite_duration), 0);
+}
+
 }  // namespace fuzztest::internal