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;