Release `graveyard_.init_mu` early in `SampleRecorder<T>::PopDead` right before `sample->PrepareForSampling(...)`. Holding the global `graveyard_.init_mu` during the full `PrepareForSampling(...)` execution (which unwinds stack traces and records timestamps) causes heavy lock contention across threads when creating/destroying hashtables frequently. Releasing `graveyard_.init_mu` early while keeping `sample->init_mu` allows multiple threads to perform sampling setup in parallel. PiperOrigin-RevId: 958747232 Change-Id: I9fd5a09e7874f7e8ec7b289ab9d2da479e1761fd
diff --git a/absl/profiling/internal/sample_recorder.h b/absl/profiling/internal/sample_recorder.h index 88a4b27..c0e4ebc 100644 --- a/absl/profiling/internal/sample_recorder.h +++ b/absl/profiling/internal/sample_recorder.h
@@ -168,7 +168,7 @@ template <typename T> template <typename... Targs> T* SampleRecorder<T>::PopDead(Targs... args) { - absl::MutexLock graveyard_lock(graveyard_.init_mu); + absl::ReleasableMutexLock graveyard_lock(graveyard_.init_mu); // The list is circular, so eventually it collapses down to // graveyard_.dead == &graveyard_ @@ -178,6 +178,11 @@ absl::MutexLock sample_lock(sample->init_mu); graveyard_.dead = sample->dead; + // Release the global graveyard lock early, before the potentially slow + // preparation. + graveyard_lock.Release(); + // Prepare the sample while still holding the per-sample lock. + // `Iterate` will wait for the lock to be released. sample->dead = nullptr; sample->PrepareForSampling(std::forward<Targs>(args)...); return sample;