From 653e7cdd94986aeec8db3bfb07e49d029c980898 Mon Sep 17 00:00:00 2001 From: TCMalloc Team Date: Wed, 7 Oct 2026 09:12:02 -0700 Subject: [PATCH] Batch LinkedSample deletion under metadata_lock_ in ~StackTraceTable Add a callback-based batch overload to MetadataObjectAllocator::Delete(absl::FunctionRef callback) and use it in StackTraceTable::~StackTraceTable() so the singly-linked list of LinkedSample nodes is drained under a single metadata_lock_ acquisition instead of bouncing the lock on every node. PiperOrigin-RevId: 995150983 --- tcmalloc/metadata_object_allocator.h | 10 ++++++++++ tcmalloc/stack_trace_table.cc | 13 +++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/tcmalloc/metadata_object_allocator.h b/tcmalloc/metadata_object_allocator.h index 9b7f982db..cfd394cc1 100644 --- a/tcmalloc/metadata_object_allocator.h +++ b/tcmalloc/metadata_object_allocator.h @@ -23,8 +23,10 @@ #include "absl/base/const_init.h" #include "absl/base/dynamic_annotations.h" #include "absl/base/internal/spinlock.h" +#include "absl/base/nullability.h" #include "absl/base/optimization.h" #include "absl/base/thread_annotations.h" +#include "absl/functional/function_ref.h" #include "absl/types/span.h" #include "tcmalloc/arena.h" #include "tcmalloc/common.h" @@ -88,6 +90,14 @@ class MetadataObjectAllocator { } } + void Delete(absl::FunctionRef callback) { + AllocationGuardSpinLockHolder l(metadata_lock_); + while (T* p = callback()) { + p->~T(); + DeleteMemory(p); + } + } + [[nodiscard]] AllocatorStats stats() const { AllocationGuardSpinLockHolder l(metadata_lock_); diff --git a/tcmalloc/stack_trace_table.cc b/tcmalloc/stack_trace_table.cc index 6afbfcf0f..d96ec66f2 100644 --- a/tcmalloc/stack_trace_table.cc +++ b/tcmalloc/stack_trace_table.cc @@ -40,11 +40,16 @@ StackTraceTable::StackTraceTable(ProfileType type) StackTraceTable::~StackTraceTable() { LinkedSample* cur = all_; - while (cur != nullptr) { - LinkedSample* next = cur->next; - tc_globals.linked_sample_allocator().Delete(cur); - cur = next; + if (cur == nullptr) { + return; } + tc_globals.linked_sample_allocator().Delete([&]() -> LinkedSample* { + LinkedSample* s = cur; + if (s != nullptr) { + cur = s->next; + } + return s; + }); all_ = nullptr; }