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
This commit is contained in:
Abseil Team
2025-12-08 11:38:14 -08:00
committed by Copybara-Service
parent f17f9070d2
commit 88c48235c8
2 changed files with 29 additions and 32 deletions

View File

@@ -21,6 +21,7 @@
#include <atomic>
#include <iterator>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
@@ -46,28 +47,27 @@ ABSL_CONST_INIT BorrowedFixupBuffer::FixupStackBuffer
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 @@ void BorrowedFixupBuffer::InitViaAllocation(size_t length) {
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

View File

@@ -42,26 +42,23 @@ class BorrowedFixupBuffer {
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;