diff --git a/docs/BUILD.gn b/docs/BUILD.gn index eb051fa93..3af4d01c1 100644 --- a/docs/BUILD.gn +++ b/docs/BUILD.gn @@ -161,6 +161,7 @@ _doxygen_input_files = [ # keep-sorted: start "$dir_pw_allocator/block/public/pw_allocator/block/detailed_block.h", "$dir_pw_allocator/block/public/pw_allocator/block/iterable.h", "$dir_pw_allocator/block/public/pw_allocator/block/poisonable.h", + "$dir_pw_allocator/block/public/pw_allocator/block/result.h", "$dir_pw_allocator/block/public/pw_allocator/block/with_layout.h", "$dir_pw_allocator/public/pw_allocator/allocator.h", "$dir_pw_allocator/public/pw_allocator/allocator_as_pool.h", diff --git a/pw_allocator/CMakeLists.txt b/pw_allocator/CMakeLists.txt index 28d37c9d2..81494b137 100644 --- a/pw_allocator/CMakeLists.txt +++ b/pw_allocator/CMakeLists.txt @@ -123,10 +123,6 @@ pw_add_library(pw_allocator.bucket_allocator INTERFACE # TODO(b/376730645): Remove deprecated interfaces. pw_add_library(pw_allocator.bucket_block_allocator INTERFACE - HEADERS - public/pw_allocator/bucket_block_allocator.h - PUBLIC_INCLUDES - public PUBLIC_DEPS pw_allocator.bucket_allocator ) diff --git a/pw_allocator/api.rst b/pw_allocator/api.rst index 9fd13e470..9d6609fd8 100644 --- a/pw_allocator/api.rst +++ b/pw_allocator/api.rst @@ -288,6 +288,15 @@ PoisonableBlock .. doxygenclass:: pw::allocator::PoisonableBlock :members: +BlockResult +----------- +This type is not a block mix-in. It is used to communicate whether a method +succeeded, what block was produced or modified, and what side-effects the call +produced. + +.. doxygenclass:: pw::allocator::BlockResult + :members: + DetailedBlock ------------- This type is not a block mix-in. It is an example of a block implementation that diff --git a/pw_allocator/public/pw_allocator/best_fit_block_allocator.h b/pw_allocator/public/pw_allocator/best_fit_block_allocator.h index bffe0f599..4d215cd09 100644 --- a/pw_allocator/public/pw_allocator/best_fit_block_allocator.h +++ b/pw_allocator/public/pw_allocator/best_fit_block_allocator.h @@ -51,23 +51,21 @@ class BestFitBlockAllocator private: /// @copydoc Allocator::Allocate - BlockType* ChooseBlock(Layout layout) override { + BlockResult ChooseBlock(Layout layout) override { BlockType* best = nullptr; size_t best_size = std::numeric_limits::max(); - for (auto* block : Base::blocks()) { - size_t inner_size = block->InnerSize(); - if (block->IsFree() && inner_size < best_size && - block->CanAlloc(layout).ok()) { - best = block; + for (auto* candidate : Base::blocks()) { + size_t inner_size = candidate->InnerSize(); + if (candidate->IsFree() && inner_size < best_size && + candidate->CanAlloc(layout).ok()) { + best = candidate; best_size = inner_size; } } - if (best == nullptr) { - return nullptr; + if (best != nullptr) { + return BlockType::AllocFirst(std::move(best), layout); } - auto result = BlockType::AllocFirst(std::move(best), layout); - PW_ASSERT(result.ok()); - return result.block(); + return BlockResult(nullptr, Status::NotFound()); } }; diff --git a/pw_allocator/public/pw_allocator/block_allocator.h b/pw_allocator/public/pw_allocator/block_allocator.h index 73be86de3..8c2b8d7d0 100644 --- a/pw_allocator/public/pw_allocator/block_allocator.h +++ b/pw_allocator/public/pw_allocator/block_allocator.h @@ -171,6 +171,9 @@ class BlockAllocator : public internal::GenericBlockAllocator { Result FromUsableSpace(PtrType ptr) const; private: + using BlockResultPrev = internal::GenericBlockResult::Prev; + using BlockResultNext = internal::GenericBlockResult::Next; + /// @copydoc Allocator::Allocate void* DoAllocate(Layout layout) override; @@ -189,14 +192,12 @@ class BlockAllocator : public internal::GenericBlockAllocator { /// Selects a free block to allocate from. /// /// This method represents the allocator-specific strategy of choosing which - /// block should be used to satisfy allocation requests. - /// - /// If derived classes override ``ReserveBlock`` and ``RecycleBlock`` to - /// provide additional bookkeeping, the implementation of this method should - /// invoke those methods as needed. + /// block should be used to satisfy allocation requests. If the returned + /// result indicates success, `block` will be replaced by the chosen block. /// + /// @param block Used to return the chosen block. /// @param layout Same as ``Allocator::Allocate``. - virtual BlockType* ChooseBlock(Layout layout) = 0; + virtual BlockResult ChooseBlock(Layout layout) = 0; /// Indicates that a block will no longer be free. /// @@ -290,11 +291,27 @@ void BlockAllocator::Reset() { template void* BlockAllocator::DoAllocate(Layout layout) { + if (capacity_ == 0) { + // Not initialized. + return nullptr; + } + PW_ASSERT(last_->Next() == nullptr); - BlockType* block = ChooseBlock(layout); - if (block == nullptr) { + auto result = ChooseBlock(layout); + if (!result.ok()) { + // No valid block for request. return nullptr; } + BlockType* block = result.block(); + + // New free blocks may be created when allocating. + if (result.prev() == BlockResultPrev::kSplitNew) { + RecycleBlock(block->Prev()); + } + if (result.next() == BlockResultNext::kSplitNew) { + RecycleBlock(block->Next()); + } + UpdateLast(block); PW_ASSERT(block <= last_); return block->UsableSpace(); @@ -312,11 +329,11 @@ void BlockAllocator::DoDeallocate(void* ptr) { } // Neighboring blocks may be merged when freeing. - if (PrevIsFree(block)) { - ReserveBlock(block->Prev()); + if (auto* prev = block->Prev(); prev != nullptr && prev->IsFree()) { + ReserveBlock(prev); } - if (NextIsFree(block)) { - ReserveBlock(block->Next()); + if (auto* next = block->Next(); next != nullptr && next->IsFree()) { + ReserveBlock(next); } // Free the block and merge it with its neighbors, if possible. @@ -345,7 +362,7 @@ bool BlockAllocator::DoResize(void* ptr, BlockType* block = *result; // Neighboring blocks may be merged when resizing. - if (NextIsFree(block)) { + if (auto* next = block->Next(); next != nullptr && next->IsFree()) { ReserveBlock(block->Next()); } @@ -354,7 +371,7 @@ bool BlockAllocator::DoResize(void* ptr, } UpdateLast(block); - if (NextIsFree(block)) { + if (auto* next = block->Next(); next != nullptr && next->IsFree()) { RecycleBlock(block->Next()); } diff --git a/pw_allocator/public/pw_allocator/bucket_allocator.h b/pw_allocator/public/pw_allocator/bucket_allocator.h index 07f16a6f6..f643ed3be 100644 --- a/pw_allocator/public/pw_allocator/bucket_allocator.h +++ b/pw_allocator/public/pw_allocator/bucket_allocator.h @@ -89,7 +89,7 @@ class BucketAllocator : public BlockAllocator { private: /// @copydoc BlockAllocator::ChooseBlock - BlockType* ChooseBlock(Layout layout) override { + BlockResult ChooseBlock(Layout layout) override { layout = Layout(std::max(layout.size(), sizeof(internal::Bucket::Chunk)), std::max(layout.alignment(), alignof(internal::Bucket::Chunk))); @@ -105,28 +105,9 @@ class BucketAllocator : public BlockAllocator { continue; } BlockType* block = BlockType::FromUsableSpace(chosen); - auto result = BlockType::AllocLast(std::move(block), layout); - if (!result.ok()) { - break; - } - block = result.block(); - - using Prev = internal::GenericBlockResult::Prev; - if (result.prev() == Prev::kSplitNew) { - // The new free block needs to be added to a bucket. - BlockType* prev = block->Prev(); - RecycleBlock(prev); - } - - using Next = internal::GenericBlockResult::Next; - if (result.next() == Next::kSplitNew) { - // The new free block needs to be added to a bucket. - BlockType* next = block->Next(); - RecycleBlock(next); - } - return block; + return BlockType::AllocLast(std::move(block), layout); } - return nullptr; + return BlockResult(nullptr, Status::NotFound()); } /// @copydoc BlockAllocator::ReserveBlock diff --git a/pw_allocator/public/pw_allocator/dual_first_fit_block_allocator.h b/pw_allocator/public/pw_allocator/dual_first_fit_block_allocator.h index 3cc667e08..9fc4b8dbf 100644 --- a/pw_allocator/public/pw_allocator/dual_first_fit_block_allocator.h +++ b/pw_allocator/public/pw_allocator/dual_first_fit_block_allocator.h @@ -51,14 +51,14 @@ class DualFirstFitBlockAllocator void set_threshold(size_t threshold) { threshold_ = threshold; } private: - /// @copydoc Allocator::Allocate - BlockType* ChooseBlock(Layout layout) override { + /// @copydoc BlockAllocator::ChooseBlock + BlockResult ChooseBlock(Layout layout) override { if (layout.size() < threshold_) { // Search backwards for the last block that can hold this allocation. for (auto* block : Base::rblocks()) { auto result = BlockType::AllocLast(std::move(block), layout); if (result.ok()) { - return result.block(); + return result; } } } else { @@ -66,12 +66,12 @@ class DualFirstFitBlockAllocator for (auto* block : Base::blocks()) { auto result = BlockType::AllocFirst(std::move(block), layout); if (result.ok()) { - return result.block(); + return result; } } } // No valid block found. - return nullptr; + return BlockResult(nullptr, Status::NotFound()); } size_t threshold_ = 0; diff --git a/pw_allocator/public/pw_allocator/first_fit_block_allocator.h b/pw_allocator/public/pw_allocator/first_fit_block_allocator.h index 32ae17037..0cf1366f5 100644 --- a/pw_allocator/public/pw_allocator/first_fit_block_allocator.h +++ b/pw_allocator/public/pw_allocator/first_fit_block_allocator.h @@ -48,15 +48,15 @@ class FirstFitBlockAllocator private: /// @copydoc Allocator::Allocate - BlockType* ChooseBlock(Layout layout) override { + BlockResult ChooseBlock(Layout layout) override { // Search forwards for the first block that can hold this allocation. for (auto* block : Base::blocks()) { auto result = BlockType::AllocFirst(std::move(block), layout); if (result.ok()) { - return result.block(); + return result; } } - return nullptr; + return BlockResult(nullptr, Status::NotFound()); } }; diff --git a/pw_allocator/public/pw_allocator/last_fit_block_allocator.h b/pw_allocator/public/pw_allocator/last_fit_block_allocator.h index 4cb0fefe9..d83302f5a 100644 --- a/pw_allocator/public/pw_allocator/last_fit_block_allocator.h +++ b/pw_allocator/public/pw_allocator/last_fit_block_allocator.h @@ -48,15 +48,15 @@ class LastFitBlockAllocator private: /// @copydoc Allocator::Allocate - BlockType* ChooseBlock(Layout layout) override { + BlockResult ChooseBlock(Layout layout) override { // Search backwards for the last block that can hold this allocation. for (auto* block : Base::rblocks()) { auto result = BlockType::AllocLast(std::move(block), layout); if (result.ok()) { - return result.block(); + return result; } } - return nullptr; + return BlockResult(nullptr, Status::NotFound()); } }; diff --git a/pw_allocator/public/pw_allocator/worst_fit_block_allocator.h b/pw_allocator/public/pw_allocator/worst_fit_block_allocator.h index a5ebf97d0..6498801c3 100644 --- a/pw_allocator/public/pw_allocator/worst_fit_block_allocator.h +++ b/pw_allocator/public/pw_allocator/worst_fit_block_allocator.h @@ -50,7 +50,7 @@ class WorstFitBlockAllocator private: /// @copydoc Allocator::Allocate - BlockType* ChooseBlock(Layout layout) override { + BlockResult ChooseBlock(Layout layout) override { BlockType* worst = nullptr; size_t worst_size = layout.size() - 1; for (auto* block : Base::blocks()) { @@ -61,12 +61,10 @@ class WorstFitBlockAllocator worst_size = inner_size; } } - if (worst == nullptr) { - return nullptr; + if (worst != nullptr) { + return BlockType::AllocFirst(std::move(worst), layout); } - auto result = BlockType::AllocFirst(std::move(worst), layout); - PW_ASSERT(result.ok()); - return result.block(); + return BlockResult(nullptr, Status::NotFound()); } };