C/C++ API Reference
Loading...
Searching...
No Matches
guarded_allocator.h
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <cstddef>
17
18#include "pw_allocator/allocator.h"
19#include "pw_allocator/layout.h"
20#include "pw_allocator/synchronized_allocator.h"
21#include "pw_result/result.h"
22#include "pw_sync/borrow.h"
23
24namespace pw::allocator {
25namespace internal {
26
54 protected:
55 constexpr explicit GenericGuardedAllocator(const Capabilities& capabilities)
56 : Allocator(capabilities) {}
57
62 static Layout AdjustLayout(Layout layout);
63
68 static size_t AdjustSize(void* ptr, size_t inner_size);
69
72 static void* GetOriginal(void* ptr);
73
76 static void* AddPrefix(void* ptr, size_t alignment);
77
79 static void AddSuffix(void* ptr, size_t size);
80
83 static bool CheckPrefixAndSuffix(void* ptr, size_t size);
84};
85
86} // namespace internal
87
89
116template <typename BlockAllocatorType, typename LockType = NoSync>
118 private:
120
121 public:
122 using BlockType = typename BlockAllocatorType::BlockType;
123
124 constexpr explicit GuardedAllocator(BlockAllocatorType& allocator)
125 : Base(allocator.capabilities()), borrowable_(allocator, lock_) {
126 block_ = *(allocator.blocks().end());
127 }
128
137 void* ValidateOne();
138
147 void* ValidateAll();
148
149 private:
151 void* DoAllocate(Layout layout) override;
152
154 void DoDeallocate(void* ptr) override;
155
157 void DoDeallocate(void* ptr, Layout) override { DoDeallocate(ptr); }
158
160 bool DoResize(void* ptr, size_t new_size) override;
161
163 Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) const override;
164
165 LockType lock_;
167
168 // Ideally, this would be annotated as being guarded `lock_`. However, the
169 // lock is always acquired via `borrowable_`, which does not support thread
170 // safety analysis.
171 BlockType* block_;
172};
173
175
176// Template method implementations.
177
178template <typename BlockAllocatorType, typename LockType>
180 auto allocator = borrowable_.acquire();
181
182 // Find the bounds of the block range.
183 auto range = allocator->blocks();
184 BlockType* begin = *(range.begin());
185 BlockType* end = *(range.end());
186
187 // Ensure there is at least one block.
188 if (begin == end) {
189 return nullptr;
190 }
191
192 // Ensure we are starting from a block.
193 if (block_ == nullptr || block_ == end) {
194 block_ = begin;
195 }
196
197 // Find the next used block.
198 BlockType* prev = block_;
199 while (!block_->Used()) {
200 BlockType* next = block_->Next();
201 if (next == end) {
202 // Loop around.
203 next = begin;
204 }
205 if (next == prev) {
206 // All blocks are free.
207 return nullptr;
208 }
209 block_ = next;
210 }
211
212 // Validate the block.
213 void* ptr = block_->UsableSpace();
214 size_t size = block_->InnerSize();
215 return Base::CheckPrefixAndSuffix(ptr, size) ? nullptr : ptr;
216}
217
218template <typename BlockAllocatorType, typename LockType>
220 auto allocator = borrowable_.acquire();
221 for (BlockType* block : allocator->blocks()) {
222 if (!block->Used()) {
223 continue;
224 }
225 void* ptr = block->UsableSpace();
226 size_t size = block->InnerSize();
227 if (!Base::CheckPrefixAndSuffix(ptr, size)) {
228 return ptr;
229 }
230 }
231 return nullptr;
232}
233
234template <typename BlockAllocatorType, typename LockType>
236 Layout layout) {
237 layout = Base::AdjustLayout(layout);
238 auto allocator = borrowable_.acquire();
239 void* ptr = allocator->Allocate(layout);
240 if (ptr == nullptr) {
241 return nullptr;
242 }
243 auto* block = BlockType::FromUsableSpace(ptr);
244
245 // Bytes may be shifted to the previous block.
246 BlockType* prev = block->Prev();
247 if (prev != nullptr && prev->Used()) {
248 Base::AddSuffix(prev->UsableSpace(), prev->InnerSize());
249 }
250
251 Base::AddSuffix(block->UsableSpace(), block->InnerSize());
252 return Base::AddPrefix(ptr, layout.alignment());
253}
254
255template <typename BlockAllocatorType, typename LockType>
257 ptr = Base::GetOriginal(ptr);
258
259 // This block and or the next one may be merged on deallocation.
260 auto allocator = borrowable_.acquire();
261 auto* block = BlockType::FromUsableSpace(ptr);
262 BlockType* prev = block->Prev();
263 if (block_ == block || block_ == block->Next()) {
264 block_ = prev;
265 }
266 allocator->Deallocate(ptr);
267
268 // Bytes may have been shifted from the previous block.
269 if (prev != nullptr && prev->Used()) {
270 Base::AddSuffix(prev->UsableSpace(), prev->InnerSize());
271 }
272}
273
274template <typename BlockAllocatorType, typename LockType>
276 size_t new_size) {
277 ptr = Base::GetOriginal(ptr);
278 new_size = Base::AdjustSize(ptr, new_size);
279
280 // The next block may be recreated on resizing.
281 auto allocator = borrowable_.acquire();
282 auto* block = BlockType::FromUsableSpace(ptr);
283 if (block_ == block->Next()) {
284 block_ = block;
285 }
286 if (!allocator->Resize(ptr, new_size)) {
287 return false;
288 }
289 Base::AddSuffix(block->UsableSpace(), block->InnerSize());
290 return true;
291}
292
293template <typename BlockAllocatorType, typename LockType>
295 InfoType info_type, const void* ptr) const {
296 ptr = const_cast<const void*>(Base::GetOriginal(const_cast<void*>(ptr)));
297 auto allocator = borrowable_.acquire();
298 return BlockAllocatorType::GetInfo(*allocator, info_type, ptr);
299}
300
301} // namespace pw::allocator
Definition: allocator.h:36
constexpr Allocator()=default
TODO(b/326509341): Remove when downstream consumers migrate.
Definition: poll.h:25
Definition: capability.h:64
Definition: guarded_allocator.h:117
void DoDeallocate(void *ptr) override
Definition: guarded_allocator.h:256
void * ValidateAll()
Definition: guarded_allocator.h:219
bool DoResize(void *ptr, size_t new_size) override
Definition: guarded_allocator.h:275
void * DoAllocate(Layout layout) override
Definition: guarded_allocator.h:235
void DoDeallocate(void *ptr, Layout) override
Definition: guarded_allocator.h:157
Result< Layout > DoGetInfo(InfoType info_type, const void *ptr) const override
Definition: guarded_allocator.h:294
void * ValidateOne()
Definition: guarded_allocator.h:179
Definition: layout.h:58
Definition: guarded_allocator.h:53
static size_t AdjustSize(void *ptr, size_t inner_size)
static void AddSuffix(void *ptr, size_t size)
Adds a suffix a the end of the allocation given by ptr and size.
static void * AddPrefix(void *ptr, size_t alignment)
static bool CheckPrefixAndSuffix(void *ptr, size_t size)
Definition: borrow.h:164