#Centipede Run the fuzzing loop in the current thread when num_threads == 1.

More context is in the comment.

PiperOrigin-RevId: 623475379
diff --git a/centipede/centipede_interface.cc b/centipede/centipede_interface.cc
index 0c15224..45f3c3d 100644
--- a/centipede/centipede_interface.cc
+++ b/centipede/centipede_interface.cc
@@ -264,8 +264,18 @@
   }
   CoverageLogger coverage_logger(binary_info.pc_table, binary_info.symbols);
 
-  auto thread_callback = [&](Environment &my_env, std::atomic<Stats> &stats) {
-    CreateLocalDirRemovedAtExit(TemporaryLocalDirPath());  // creates temp dir.
+  std::vector<Environment> envs(env.num_threads, env);
+  std::vector<std::atomic<Stats>> stats_vec(env.num_threads);
+  std::atomic<bool> stats_thread_continue_running = true;
+
+  std::thread stats_thread(ReportStatsThread,
+                           std::ref(stats_thread_continue_running),
+                           std::ref(stats_vec), std::ref(envs));
+
+  auto fuzzing_worker = [&](Environment &my_env, std::atomic<Stats> &stats,
+                            bool create_tmpdir) {
+    if (create_tmpdir) CreateLocalDirRemovedAtExit(TemporaryLocalDirPath());
+    my_env.UpdateForExperiment();
     my_env.seed = GetRandomSeed(env.seed);  // uses TID, call in this thread.
     my_env.pcs_file_path = pcs_file_path;   // same for all threads.
 
@@ -277,28 +287,29 @@
     centipede.FuzzingLoop();
   };
 
-  std::vector<Environment> envs(env.num_threads, env);
-  std::vector<std::atomic<Stats>> stats_vec(env.num_threads);
-  std::vector<std::thread> threads(env.num_threads);
-  std::atomic<bool> stats_thread_continue_running(true);
-
-  // Create threads.
-  for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) {
-    Environment &my_env = envs[thread_idx];
-    my_env.my_shard_index = env.my_shard_index + thread_idx;
-    my_env.UpdateForExperiment();
-    threads[thread_idx] = std::thread(thread_callback, std::ref(my_env),
-                                      std::ref(stats_vec[thread_idx]));
+  if (env.num_threads == 1) {
+    // When fuzzing with one thread, run fuzzing loop in the current
+    // thread. This is because FuzzTest/Centipede's single-process
+    // fuzzing requires the test body, which is invoked by the fuzzing
+    // loop, to run in the main thread.
+    //
+    // Here, the fuzzing worker should not re-create the tmpdir since the path
+    // is thread-local and it has been created in the current function.
+    fuzzing_worker(envs[0], stats_vec[0], /*create_tmpdir=*/false);
+  } else {
+    std::vector<std::thread> fuzzing_worker_threads(env.num_threads);
+    for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) {
+      Environment &my_env = envs[thread_idx];
+      my_env.my_shard_index = env.my_shard_index + thread_idx;
+      fuzzing_worker_threads[thread_idx] =
+          std::thread(fuzzing_worker, std::ref(my_env),
+                      std::ref(stats_vec[thread_idx]), /*create_tmpdir=*/true);
+    }
+    for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) {
+      fuzzing_worker_threads[thread_idx].join();
+    }
   }
 
-  std::thread stats_thread(ReportStatsThread,
-                           std::ref(stats_thread_continue_running),
-                           std::ref(stats_vec), std::ref(envs));
-
-  // Join threads.
-  for (size_t thread_idx = 0; thread_idx < env.num_threads; thread_idx++) {
-    threads[thread_idx].join();
-  }
   stats_thread_continue_running = false;
   stats_thread.join();
 
diff --git a/centipede/centipede_test.cc b/centipede/centipede_test.cc
index 1bfa1cd..cfdea96 100644
--- a/centipede/centipede_test.cc
+++ b/centipede/centipede_test.cc
@@ -22,6 +22,7 @@
 #include <set>
 #include <string>
 #include <string_view>
+#include <thread>  // NOLINT(build/c++11)
 #include <vector>
 
 #include "gmock/gmock.h"
@@ -893,4 +894,50 @@
   EXPECT_FALSE(found_startup_cmp_entry);
 }
 
+namespace {
+
+class FakeCentipedeCallbacksForThreadChecking : public CentipedeCallbacks {
+ public:
+  FakeCentipedeCallbacksForThreadChecking(const Environment &env,
+                                          std::thread::id execute_thread_id)
+      : CentipedeCallbacks(env), execute_thread_id_(execute_thread_id) {}
+
+  bool Execute(std::string_view binary, const std::vector<ByteArray> &inputs,
+               BatchResult &batch_result) override {
+    batch_result.ClearAndResize(inputs.size());
+    thread_check_passed_ = thread_check_passed_ &&
+                           std::this_thread::get_id() == execute_thread_id_;
+    return true;
+  }
+
+  void Mutate(const std::vector<MutationInputRef> &inputs, size_t num_mutants,
+              std::vector<ByteArray> &mutants) override {
+    mutants.resize(num_mutants, {0});
+  }
+
+  bool thread_check_passed() { return thread_check_passed_; }
+
+ private:
+  std::thread::id execute_thread_id_;
+  bool thread_check_passed_ = true;
+};
+
+}  // namespace
+
+TEST(Centipede, RunsExecuteCallbackInTheCurrentThreadWhenFuzzingWithOneThread) {
+  TempDir temp_dir{test_info_->name()};
+  Environment env;
+  env.workdir = temp_dir.path();
+  env.require_pc_table = false;
+  ASSERT_EQ(env.num_threads, 1);
+  FakeCentipedeCallbacksForThreadChecking callbacks(env,
+                                                    std::this_thread::get_id());
+  BatchResult batch_result;
+  const std::vector<ByteArray> inputs = {{0}};
+  env.num_runs = 100;
+  MockFactory factory(callbacks);
+  EXPECT_EQ(CentipedeMain(env, factory), EXIT_SUCCESS);
+  EXPECT_TRUE(callbacks.thread_check_passed());
+}
+
 }  // namespace centipede