No public description

PiperOrigin-RevId: 900320823
diff --git a/centipede/rusage_profiler.cc b/centipede/rusage_profiler.cc
index 5e41de5..540855b 100644
--- a/centipede/rusage_profiler.cc
+++ b/centipede/rusage_profiler.cc
@@ -372,7 +372,7 @@
     return kEmpty;
   }
 
-  absl::WriterMutexLock lock{mutex_};
+  absl::WriterMutexLock lock{snapshots_mutex_};
 
   RUsageTiming snap_timing = RUsageTiming::Zero();
   RUsageTiming delta_timing = RUsageTiming::Zero();
@@ -420,7 +420,7 @@
     absl::Duration interval,          //
     bool also_log,                    //
     std::string title) {
-  absl::WriterMutexLock lock{mutex_};
+  absl::WriterMutexLock lock{recorder_mutex_};
   FUZZTEST_CHECK(!timelapse_recorder_) << "StopTimelapse() wasn't called";
   timelapse_recorder_ = std::make_unique<PeriodicAction>(
       [this, loc = std::move(loc), title = std::move(title), also_log]() {
@@ -431,7 +431,7 @@
 }
 
 void RUsageProfiler::StopTimelapse() {
-  absl::WriterMutexLock lock{mutex_};
+  absl::WriterMutexLock lock{recorder_mutex_};
   FUZZTEST_CHECK(timelapse_recorder_) << "StartTimelapse() wasn't called";
   timelapse_recorder_.reset();
 }
@@ -483,7 +483,7 @@
 
 void RUsageProfiler::GenerateReport(
     ReportSink* absl_nonnull report_sink) const {
-  absl::ReaderMutexLock lock{mutex_};
+  absl::ReaderMutexLock lock{snapshots_mutex_};
   // Prevent interleaved reports from multiple concurrent RUsageProfilers.
   ABSL_CONST_INIT static absl::Mutex report_generation_mutex_{absl::kConstInit};
   absl::WriterMutexLock logging_lock{report_generation_mutex_};
diff --git a/centipede/rusage_profiler.h b/centipede/rusage_profiler.h
index 756ebb5..1fcd0c7 100644
--- a/centipede/rusage_profiler.h
+++ b/centipede/rusage_profiler.h
@@ -427,17 +427,19 @@
   // The sequential ID of this profiler. Used to annotate all log
   const int id_;
 
-  // Mutex for the mutable data further below.
-  mutable absl::Mutex mutex_;
+  // Mutexes for the mutable data further below.
+  mutable absl::Mutex snapshots_mutex_;
+  mutable absl::Mutex recorder_mutex_;
 
   // Chronological snapshots. Using std::deque gives a better-than-vector
   // average insertion speed, preserves iterators across insertions, and strikes
   // a balance between vector's and list's additional storage.
-  std::deque<Snapshot> snapshots_ ABSL_GUARDED_BY(mutex_);
+  std::deque<Snapshot> snapshots_ ABSL_GUARDED_BY(snapshots_mutex_);
   // A temporarily lived periodic action that records and optionally logs
   // timelapse snapshots. (Re)created by each new call to StartTimelapse() and
   // terminated by StopTimelapse() or the dtor, whichever comes first.
-  std::unique_ptr<PeriodicAction> timelapse_recorder_ ABSL_GUARDED_BY(mutex_);
+  std::unique_ptr<PeriodicAction> timelapse_recorder_
+      ABSL_GUARDED_BY(recorder_mutex_);
 
   // An auto-starting timer passed to RUsageTiming::Snapshot() in order to track
   // this RUsageProfiler object's lifetime stats rather than the process's
diff --git a/centipede/rusage_profiler_test.cc b/centipede/rusage_profiler_test.cc
index c8c0b9f..1ad46eb 100644
--- a/centipede/rusage_profiler_test.cc
+++ b/centipede/rusage_profiler_test.cc
@@ -245,4 +245,24 @@
   rprof.GenerateReport(&report_capture);
 }
 
+TEST(RUsageProfilerTest, DeadlockReproduction) {
+  const auto rusage_scope = RUsageScope::ThisProcess();
+  for (int i = 0; i < 1000; ++i) {
+    RUsageProfiler rprof{rusage_scope,
+                         RUsageProfiler::kAllMetrics,
+                         RUsageProfiler::kRaiiOff,
+                         {__FILE__, __LINE__}};
+    // Use a tiny interval to trigger frequent snapshots
+    rprof.StartTimelapse({__FILE__, __LINE__}, absl::Microseconds(10),
+                         /*also_log=*/false, "Timelapse");
+
+    // Briefly wait to ensure the background thread starts running
+    absl::SleepFor(absl::Microseconds(10));
+
+    // This call frequently deadlocks because it holds the mutex while
+    // waiting for the background thread to finish its TakeSnapshot() call.
+    rprof.StopTimelapse();
+  }
+}
+
 }  // namespace fuzztest::internal