Skip to content

Commit

Permalink
pw_allocator: Add dlmalloc-style allocator
Browse files Browse the repository at this point in the history
In order to allow for dlmalloc-style deferral of deallocation, some
changes to buckets were needed. Additionally, the TLSF allocator has
been refactored slightly in order to make it and this allocator
structurally similar.

Change-Id: I37cd38c8412efd6002224bf4b1306105d3ffa35e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/234717
Presubmit-Verified: CQ Bot Account <[email protected]>
Lint: Lint 🤖 <[email protected]>
Reviewed-by: Alexei Frolov <[email protected]>
Reviewed-by: Wyatt Hepler <[email protected]>
Commit-Queue: Aaron Green <[email protected]>
  • Loading branch information
nopsledder authored and CQ Bot Account committed Feb 22, 2025
1 parent b1a5820 commit 7eb28ce
Show file tree
Hide file tree
Showing 20 changed files with 775 additions and 104 deletions.
1 change: 1 addition & 0 deletions docs/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ _doxygen_input_files = [ # keep-sorted: start
"$dir_pw_allocator/public/pw_allocator/chunk_pool.h",
"$dir_pw_allocator/public/pw_allocator/config.h",
"$dir_pw_allocator/public/pw_allocator/deallocator.h",
"$dir_pw_allocator/public/pw_allocator/dl_allocator.h",
"$dir_pw_allocator/public/pw_allocator/fallback_allocator.h",
"$dir_pw_allocator/public/pw_allocator/first_fit.h",
"$dir_pw_allocator/public/pw_allocator/fragmentation.h",
Expand Down
33 changes: 33 additions & 0 deletions pw_allocator/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,20 @@ cc_library(
],
)

cc_library(
name = "dl_allocator",
hdrs = ["public/pw_allocator/dl_allocator.h"],
strip_include_prefix = "public",
deps = [
":block_allocator",
":config",
"//pw_allocator/block:detailed_block",
"//pw_allocator/bucket:fast_sorted",
"//pw_allocator/bucket:unordered",
"//third_party/fuchsia:stdcompat",
],
)

# TODO(b/376730645): Remove deprecated interfaces.
cc_library(
name = "dual_first_fit_block_allocator",
Expand Down Expand Up @@ -606,6 +620,16 @@ pw_cc_test(
],
)

pw_cc_test(
name = "dl_allocator_test",
srcs = ["dl_allocator_test.cc"],
deps = [
":block_allocator_testing",
":dl_allocator",
"//pw_unit_test",
],
)

pw_cc_test(
name = "fallback_allocator_test",
srcs = ["fallback_allocator_test.cc"],
Expand Down Expand Up @@ -793,6 +817,7 @@ filegroup(
"public/pw_allocator/chunk_pool.h",
"public/pw_allocator/config.h",
"public/pw_allocator/deallocator.h",
"public/pw_allocator/dl_allocator.h",
"public/pw_allocator/fallback_allocator.h",
"public/pw_allocator/first_fit.h",
"public/pw_allocator/fragmentation.h",
Expand Down Expand Up @@ -845,6 +870,13 @@ pw_size_diff(
target = "//pw_allocator/size_report:bucket_allocator",
)

pw_size_diff(
name = "dl_allocator_size_report",
base = "//pw_allocator/size_report:detailed_block",
label = "DlAllocator",
target = "//pw_allocator/size_report:dl_allocator",
)

pw_size_diff(
name = "first_fit_size_report",
base = "//pw_allocator/size_report:detailed_block",
Expand All @@ -871,6 +903,7 @@ pw_size_table(
reports = [
":best_fit_size_report",
":bucket_allocator_size_report",
":dl_allocator_size_report",
":first_fit_size_report",
":tlsf_allocator_size_report",
":worst_fit_size_report",
Expand Down
27 changes: 27 additions & 0 deletions pw_allocator/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,19 @@ pw_source_set("deallocator") {
]
}

pw_source_set("dl_allocator") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_allocator/dl_allocator.h" ]
public_deps = [
":block_allocator",
":config",
"$dir_pw_third_party/fuchsia:stdcompat",
"block:detailed_block",
"bucket:fast_sorted",
"bucket:unordered",
]
}

# TODO(b/376730645): Remove deprecated interfaces.
pw_source_set("dual_first_fit_block_allocator") {
public_configs = [ ":public_include_path" ]
Expand Down Expand Up @@ -559,6 +572,14 @@ pw_test("chunk_pool_test") {
sources = [ "chunk_pool_test.cc" ]
}

pw_test("dl_allocator_test") {
deps = [
":block_allocator_testing",
":dl_allocator",
]
sources = [ "dl_allocator_test.cc" ]
}

pw_test("fallback_allocator_test") {
deps = [
":fallback_allocator",
Expand Down Expand Up @@ -721,6 +742,7 @@ pw_test_group("tests") {
":buffer_test",
":bump_allocator_test",
":chunk_pool_test",
":dl_allocator_test",
":fallback_allocator_test",
":first_fit_test",
":fragmentation_test",
Expand Down Expand Up @@ -844,6 +866,11 @@ pw_size_diff("block_allocators_size_report") {
base = "size_report:detailed_block"
label = "BucketAllocator"
},
{
target = "size_report:dl_allocator"
base = "size_report:detailed_block"
label = "DlAllocator"
},
{
target = "size_report:first_fit"
base = "size_report:detailed_block"
Expand Down
29 changes: 27 additions & 2 deletions pw_allocator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,21 @@ pw_add_library(pw_allocator.deallocator STATIC
pw_status
)

# TODO(b/376730645): Remove deprecated interfaces.
pw_add_library(pw_allocator.dl_allocator INTERFACE
HEADERS
public/pw_allocator/dl_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.block_allocator
pw_allocator.block.detailed_block
pw_allocator.bucket.fast_sorted
pw_allocator.bucket.unordered
pw_allocator.config
pw_third_party.fuchsia.stdcompat
)

# TODO(b/376730645): Remove deprecated interfaces.
pw_add_library(pw_allocator.dual_first_fit_block_allocator INTERFACE
HEADERS
public/pw_allocator/dual_first_fit_block_allocator.h
Expand Down Expand Up @@ -616,6 +630,17 @@ pw_add_test(pw_allocator.chunk_pool_test
pw_allocator
)

pw_add_test(pw_allocator.dl_allocator_test
SOURCES
dl_allocator_test.cc
PRIVATE_DEPS
pw_allocator.block_allocator_testing
pw_allocator.dl_allocator
GROUPS
modules
pw_allocator
)

pw_add_test(pw_allocator.fallback_allocator_test
PRIVATE_DEPS
pw_allocator.testing
Expand Down Expand Up @@ -753,7 +778,7 @@ pw_add_test(pw_allocator.synchronized_allocator_test

pw_add_test(pw_allocator.tlsf_allocator_test
SOURCES
tlsf_allocator_test.cc
tlsf_allocator_test.cc
PRIVATE_DEPS
pw_allocator.block_allocator_testing
pw_allocator.tlsf_allocator
Expand Down
7 changes: 7 additions & 0 deletions pw_allocator/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ BucketAllocator
.. doxygenclass:: pw::allocator::BucketAllocator
:members:

.. _module-pw_allocator-api-dl_allocator:

DlAllocator
-------------
.. doxygenclass:: pw::allocator::DlAllocator
:members:

.. _module-pw_allocator-api-first_fit_allocator:

FirstFitAllocator
Expand Down
27 changes: 14 additions & 13 deletions pw_allocator/block/public/pw_allocator/block/testing.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ struct Preallocation {
static constexpr size_t kSizeRemaining = std::numeric_limits<size_t>::max();
};

template <typename BlockType>
template <typename BlockType, size_t kBufferSize = kDefaultCapacity>
class BlockTestUtilities {
public:
BlockTestUtilities() : buffer_(), bytes_(buffer_.as_span()) {}
Expand Down Expand Up @@ -131,35 +131,35 @@ class BlockTestUtilities {
size_t GetFirstAlignedOffset(Layout layout);

private:
AlignedBuffer<kDefaultCapacity, BlockType::kAlignment> buffer_;
BlockAlignedBuffer<BlockType, kBufferSize> buffer_;
ByteSpan bytes_;
};

// Template method implementations.

template <typename BlockType>
constexpr void BlockTestUtilities<BlockType>::TrimBytes(size_t offset,
size_t length) {
template <typename BlockType, size_t kBufferSize>
constexpr void BlockTestUtilities<BlockType, kBufferSize>::TrimBytes(
size_t offset, size_t length) {
bytes_ = bytes_.subspan(offset, length);
}

template <typename BlockType>
void BlockTestUtilities<BlockType>::TrimAligned(size_t extra) {
template <typename BlockType, size_t kBufferSize>
void BlockTestUtilities<BlockType, kBufferSize>::TrimAligned(size_t extra) {
size_t offset =
GetAlignedOffsetAfter(bytes_.data(), kAlign, BlockType::kBlockOverhead) +
extra;
bytes_ = bytes_.subspan(offset);
}

template <typename BlockType>
constexpr size_t BlockTestUtilities<BlockType>::GetOuterSize(
template <typename BlockType, size_t kBufferSize>
constexpr size_t BlockTestUtilities<BlockType, kBufferSize>::GetOuterSize(
size_t min_inner_size) {
return BlockType::kBlockOverhead +
AlignUp(min_inner_size, BlockType::kAlignment);
}

template <typename BlockType>
BlockType* BlockTestUtilities<BlockType>::Preallocate(
template <typename BlockType, size_t kBufferSize>
BlockType* BlockTestUtilities<BlockType, kBufferSize>::Preallocate(
std::initializer_list<Preallocation> preallocs) {
static_assert(is_allocatable_v<BlockType>);

Expand Down Expand Up @@ -218,8 +218,9 @@ BlockType* BlockTestUtilities<BlockType>::Preallocate(
return next;
}

template <typename BlockType>
size_t BlockTestUtilities<BlockType>::GetFirstAlignedOffset(Layout layout) {
template <typename BlockType, size_t kBufferSize>
size_t BlockTestUtilities<BlockType, kBufferSize>::GetFirstAlignedOffset(
Layout layout) {
size_t min_block = BlockType::kBlockOverhead + 1;
size_t offset = GetAlignedOffsetAfter(bytes().data(),
layout.alignment(),
Expand Down
4 changes: 2 additions & 2 deletions pw_allocator/block_allocator_testing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ void BlockAllocatorTestBase::UseMemory(void* ptr, size_t size) {

// Unit tests.

void BlockAllocatorTestBase::GetCapacity() {
void BlockAllocatorTestBase::GetCapacity(size_t expected) {
Allocator& allocator = GetGenericAllocator();
StatusWithSize capacity = allocator.GetCapacity();
EXPECT_EQ(capacity.status(), OkStatus());
EXPECT_EQ(capacity.size(), kCapacity);
EXPECT_EQ(capacity.size(), expected);
}

void BlockAllocatorTestBase::AllocateLarge() {
Expand Down
2 changes: 1 addition & 1 deletion pw_allocator/bucket/public/pw_allocator/bucket/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class BucketBase {
/// null. Exactly which block is returned depends on the specific bucket
/// implementation.
[[nodiscard]] BlockType* RemoveAny() {
return derived()->DoRemoveCompatible(Layout(1, 1));
return empty() ? nullptr : derived()->DoRemoveAny();
}

/// If the given `block` is in this bucket, removes it and returns true;
Expand Down
16 changes: 16 additions & 0 deletions pw_allocator/bucket/public/pw_allocator/bucket/fast_sorted.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ class FastSortedBucket
items_.insert(*item);
}

/// @copydoc `BucketBase::RemoveAny`
BlockType* DoRemoveAny() {
auto iter = items_.begin();
FastSortedItem<BlockType>& item = *iter;
items_.erase(iter);
return BlockType::FromUsableSpace(&item);
}

/// @copydoc `BucketBase::Remove`
bool DoRemove(BlockType& block) {
FastSortedItem<BlockType>& item_to_remove = Base::GetItemFrom(block);
Expand Down Expand Up @@ -146,6 +154,14 @@ class ReverseFastSortedBucket
/// @copydoc `BucketBase::Add`
void DoAdd(BlockType& block) { impl_.DoAdd(block); }

/// @copydoc `BucketBase::RemoveAny`
BlockType* DoRemoveAny() {
auto iter = items_.begin();
FastSortedItem<BlockType>& item = *iter;
items_.erase(iter);
return BlockType::FromUsableSpace(&item);
}

/// @copydoc `BucketBase::Remove`
bool DoRemove(BlockType& block) { return impl_.DoRemove(block); }

Expand Down
7 changes: 7 additions & 0 deletions pw_allocator/bucket/public/pw_allocator/bucket/sequenced.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ class SequencedBucket : public internal::BucketBase<SequencedBucket<BlockType>,
items_.insert(iter, *item_to_add);
}

/// @copydoc `BucketBase::RemoveAny`
BlockType* DoRemoveAny() {
SequencedItem& item = items_.front();
items_.pop_front();
return BlockType::FromUsableSpace(&item);
}

/// @copydoc `BucketBase::Remove`
bool DoRemove(BlockType& block) {
SequencedItem& item_to_remove = Base::GetItemFrom(block);
Expand Down
7 changes: 7 additions & 0 deletions pw_allocator/bucket/public/pw_allocator/bucket/sorted.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ class SortedBucketBase : public BucketBase<Derived, BlockType, SortedItem> {
items_.insert_after(prev, *item_to_add);
}

/// @copydoc `BucketBase::RemoveAny`
BlockType* DoRemoveAny() {
SortedItem& item = items_.front();
items_.pop_front();
return BlockType::FromUsableSpace(&item);
}

/// @copydoc `BucketBase::Remove`
bool DoRemove(BlockType& block) {
return items_.remove(Base::GetItemFrom(block));
Expand Down
7 changes: 7 additions & 0 deletions pw_allocator/bucket/public/pw_allocator/bucket/unordered.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ class UnorderedBucket : public internal::BucketBase<UnorderedBucket<BlockType>,
items_.push_front(*item);
}

/// @copydoc `BucketBase::RemoveAny`
BlockType* DoRemoveAny() {
UnorderedItem& item = items_.front();
items_.pop_front();
return BlockType::FromUsableSpace(&item);
}

/// @copydoc `BucketBase::Remove`
bool DoRemove(BlockType& block) {
return items_.remove(Base::GetItemFrom(block));
Expand Down
Loading

0 comments on commit 7eb28ce

Please sign in to comment.