-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pw_multibuf: Add SingleChunkRegionTracker
The SingleChunkRegionTracker tracks a single chunk on a given span of contigous memory. Test: compiles in CMake, Soong, and GN. Change-Id: I800af3e85c137d23c5886435efc32c9480603a86 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/189074 Reviewed-by: Taylor Cramer <cramertj@google.com> Commit-Queue: Carlos Chinchilla <cachinchilla@google.com>
- Loading branch information
1 parent
e0cdf4e
commit 14f8112
Showing
8 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
pw_multibuf/public/pw_multibuf/single_chunk_region_tracker.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright 2024 The Pigweed Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
// use this file except in compliance with the License. You may obtain a copy of | ||
// the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations under | ||
// the License. | ||
#pragma once | ||
|
||
#include <atomic> | ||
#include <cstring> | ||
#include <optional> | ||
|
||
#include "pw_assert/assert.h" | ||
#include "pw_bytes/span.h" | ||
#include "pw_multibuf/chunk.h" | ||
|
||
namespace pw::multibuf { | ||
|
||
/// A `ChunkRegionTracker` that uses inline memory to create a single `Chunk` | ||
/// with the only caveat that the provided `Chunk` cannot be split. All attempts | ||
/// will result in `std::nullopt`. | ||
class SingleChunkRegionTracker : public ChunkRegionTracker { | ||
public: | ||
/// Constructs a region tracker with a single `Chunk` that maps to `region`, | ||
/// which must outlive this tracker and any `OwnedChunk` it creates. | ||
explicit SingleChunkRegionTracker(ByteSpan region) : region_(region) {} | ||
~SingleChunkRegionTracker() override { Destroy(); } | ||
|
||
/// Gets a `Chunk` of a given size, which must be less than or equal to the | ||
/// provided region. | ||
/// | ||
/// Returns: | ||
/// An `OwnedChunk` if the `Chunk` is free, otherwise `std::nullopt`, in | ||
/// which case `GetChunk()` can be called again. | ||
std::optional<OwnedChunk> GetChunk(size_t size) { | ||
PW_DASSERT(size <= region_.size()); | ||
// Since this is a single `Chunk` region, re-create the first `Chunk` is | ||
// allowed if freed. | ||
std::optional<OwnedChunk> chunk = Chunk::CreateFirstForRegion(*this); | ||
if (chunk.has_value() && size < region_.size()) { | ||
(*chunk)->Truncate(size); | ||
} | ||
return chunk; | ||
} | ||
|
||
void Destroy() final { | ||
// Nothing to release here. | ||
PW_ASSERT(!chunk_in_use_); | ||
} | ||
|
||
ByteSpan Region() const final { return region_; } | ||
|
||
void* AllocateChunkClass() final { | ||
bool in_use = false; | ||
if (!chunk_in_use_.compare_exchange_strong(in_use, true)) { | ||
return nullptr; | ||
} | ||
return &chunk_storage_; | ||
} | ||
|
||
void DeallocateChunkClass(void* chunk) final { | ||
PW_ASSERT(chunk == chunk_storage_.data()); | ||
// Mark the `Chunk` as not in use and zero-out the region and chunk storage. | ||
std::memset(chunk_storage_.data(), 0, chunk_storage_.size()); | ||
std::memset(region_.data(), 0, region_.size()); | ||
chunk_in_use_ = false; | ||
} | ||
|
||
private: | ||
ByteSpan region_; | ||
std::atomic<bool> chunk_in_use_ = false; | ||
alignas(Chunk) std::array<std::byte, sizeof(Chunk)> chunk_storage_; | ||
}; | ||
|
||
} // namespace pw::multibuf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2023 The Pigweed Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not | ||
// use this file except in compliance with the License. You may obtain a copy of | ||
// the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
// License for the specific language governing permissions and limitations under | ||
// the License. | ||
|
||
#include "pw_multibuf/single_chunk_region_tracker.h" | ||
|
||
#include <cstddef> | ||
#include <optional> | ||
|
||
#include "pw_multibuf/chunk.h" | ||
#include "pw_unit_test/framework.h" | ||
|
||
namespace pw::multibuf { | ||
namespace { | ||
|
||
const size_t kArbitraryRegionSize = 1024; | ||
const size_t kArbitraryChunkSize = 32; | ||
|
||
TEST(SingleChunkRegionTrackerTest, GetChunkSmallerThanRegionSuccess) { | ||
std::array<std::byte, kArbitraryRegionSize> chunk_storage{}; | ||
SingleChunkRegionTracker chunk_tracker(chunk_storage); | ||
std::optional<OwnedChunk> chunk = chunk_tracker.GetChunk(kArbitraryChunkSize); | ||
EXPECT_TRUE(chunk.has_value()); | ||
EXPECT_EQ(chunk->size(), kArbitraryChunkSize); | ||
} | ||
|
||
TEST(SingleChunkRegionTrackerTest, GetChunkSameSizeAsRegionSuccess) { | ||
std::array<std::byte, kArbitraryRegionSize> chunk_storage{}; | ||
SingleChunkRegionTracker chunk_tracker(chunk_storage); | ||
std::optional<OwnedChunk> chunk = | ||
chunk_tracker.GetChunk(kArbitraryRegionSize); | ||
EXPECT_TRUE(chunk.has_value()); | ||
EXPECT_EQ(chunk->size(), kArbitraryRegionSize); | ||
} | ||
|
||
TEST(SingleChunkRegionTrackerTest, GetChunkFailChunkInUse) { | ||
std::array<std::byte, kArbitraryRegionSize> chunk_storage{}; | ||
SingleChunkRegionTracker chunk_tracker(chunk_storage); | ||
std::optional<OwnedChunk> chunk1 = | ||
chunk_tracker.GetChunk(kArbitraryChunkSize); | ||
ASSERT_TRUE(chunk1.has_value()); | ||
|
||
std::optional<OwnedChunk> chunk2 = | ||
chunk_tracker.GetChunk(kArbitraryChunkSize); | ||
EXPECT_FALSE(chunk2.has_value()); | ||
} | ||
|
||
TEST(SingleChunkRegionTrackerTest, GetChunkAfterReleasedChunkSuccess) { | ||
std::array<std::byte, kArbitraryRegionSize> chunk_storage{}; | ||
SingleChunkRegionTracker chunk_tracker(chunk_storage); | ||
std::optional<OwnedChunk> chunk1 = | ||
chunk_tracker.GetChunk(kArbitraryChunkSize); | ||
ASSERT_TRUE(chunk1.has_value()); | ||
|
||
std::optional<OwnedChunk> chunk2 = | ||
chunk_tracker.GetChunk(kArbitraryChunkSize); | ||
ASSERT_FALSE(chunk2.has_value()); | ||
|
||
chunk1->Release(); | ||
|
||
std::optional<OwnedChunk> chunk3 = | ||
chunk_tracker.GetChunk(kArbitraryChunkSize); | ||
EXPECT_TRUE(chunk3.has_value()); | ||
} | ||
|
||
} // namespace | ||
} // namespace pw::multibuf |