Search more aggressively for open slots in absl::internal_stacktrace::BorrowedFixupBuffer This lets us avoid heap allocations in more situations than before. PiperOrigin-RevId: 841851984 Change-Id: Ibd16b0b87c250c504c8d1119d9b9eb4031067c28
diff --git a/absl/debugging/internal/borrowed_fixup_buffer.cc b/absl/debugging/internal/borrowed_fixup_buffer.cc index dae78a7..507a0a2 100644 --- a/absl/debugging/internal/borrowed_fixup_buffer.cc +++ b/absl/debugging/internal/borrowed_fixup_buffer.cc
@@ -21,6 +21,7 @@ #include <atomic> #include <iterator> +#include <utility> #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -46,28 +47,27 @@ BorrowedFixupBuffer::~BorrowedFixupBuffer() { if (borrowed_) { - Unlock(); + std::move(*this).Unlock(); } else { base_internal::LowLevelAlloc::Free(frames_); } } -BorrowedFixupBuffer::BorrowedFixupBuffer(size_t length) { - FixupStackBuffer* fixup_buffer = - 0 < length && length <= FixupStackBuffer::kMaxStackElements ? TryLock() - : nullptr; - borrowed_ = fixup_buffer != nullptr; +BorrowedFixupBuffer::BorrowedFixupBuffer(size_t length) + : borrowed_(0 < length && length <= FixupStackBuffer::kMaxStackElements + ? TryLock() + : nullptr) { if (borrowed_) { - InitViaBorrow(fixup_buffer); + InitViaBorrow(); } else { InitViaAllocation(length); } } -void BorrowedFixupBuffer::InitViaBorrow(FixupStackBuffer* borrowed_buffer) { +void BorrowedFixupBuffer::InitViaBorrow() { assert(borrowed_); - frames_ = borrowed_buffer->frames; - sizes_ = borrowed_buffer->sizes; + frames_ = borrowed_->frames; + sizes_ = borrowed_->sizes; } void BorrowedFixupBuffer::InitViaAllocation(size_t length) { @@ -92,25 +92,25 @@ length * sizeof(*frames_))) int[length]; } -BorrowedFixupBuffer::FixupStackBuffer* BorrowedFixupBuffer::Find() { - size_t i = absl::Hash<const void*>()(this) % - std::size(FixupStackBuffer::g_instances); - return &FixupStackBuffer::g_instances[i]; -} - [[nodiscard]] BorrowedFixupBuffer::FixupStackBuffer* BorrowedFixupBuffer::TryLock() { - FixupStackBuffer* instance = Find(); - // Use memory_order_acquire to ensure that no reads and writes on the borrowed - // buffer are reordered before the borrowing. - return !instance->in_use.test_and_set(std::memory_order_acquire) ? instance - : nullptr; + constexpr size_t kNumSlots = std::size(FixupStackBuffer::g_instances); + const size_t i = absl::Hash<const void*>()(this) % kNumSlots; + for (size_t k = 0; k < kNumSlots; ++k) { + auto* instance = &FixupStackBuffer::g_instances[(i + k) % kNumSlots]; + // Use memory_order_acquire to ensure that no reads and writes on the + // borrowed buffer are reordered before the borrowing. + if (!instance->in_use.test_and_set(std::memory_order_acquire)) { + return instance; + } + } + return nullptr; } -void BorrowedFixupBuffer::Unlock() { +void BorrowedFixupBuffer::Unlock() && { // Use memory_order_release to ensure that no reads and writes on the borrowed // buffer are reordered after the borrowing. - Find()->in_use.clear(std::memory_order_release); + borrowed_->in_use.clear(std::memory_order_release); } } // namespace internal_stacktrace
diff --git a/absl/debugging/internal/borrowed_fixup_buffer.h b/absl/debugging/internal/borrowed_fixup_buffer.h index c5ea7a3..a8f00c8 100644 --- a/absl/debugging/internal/borrowed_fixup_buffer.h +++ b/absl/debugging/internal/borrowed_fixup_buffer.h
@@ -42,26 +42,23 @@ int* sizes() const { return sizes_; } private: + struct FixupStackBuffer; + uintptr_t* frames_; int* sizes_; - // Have we borrowed a pre-existing buffer (vs. allocated our own)? - bool borrowed_; + // The borrowed pre-existing buffer, if any (if we haven't allocated our own) + FixupStackBuffer* const borrowed_; - struct FixupStackBuffer; - - void InitViaBorrow(FixupStackBuffer* borrowed_buffer); + void InitViaBorrow(); void InitViaAllocation(size_t length); - // Returns a non-null pointer to a buffer that could be potentially borrowed. - FixupStackBuffer* Find(); - // Attempts to opportunistically borrow a small buffer in a thread- and // signal-safe manner. Returns nullptr on failure. [[nodiscard]] FixupStackBuffer* TryLock(); // Returns the borrowed buffer. - void Unlock(); + void Unlock() &&; BorrowedFixupBuffer(const BorrowedFixupBuffer&) = delete; BorrowedFixupBuffer& operator=(const BorrowedFixupBuffer&) = delete;