xref: /aosp_15_r20/external/pigweed/pw_multibuf/simple_allocator.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker 
15*61c4878aSAndroid Build Coastguard Worker #include "pw_multibuf/simple_allocator.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <algorithm>
18*61c4878aSAndroid Build Coastguard Worker #include <mutex>
19*61c4878aSAndroid Build Coastguard Worker 
20*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
21*61c4878aSAndroid Build Coastguard Worker 
22*61c4878aSAndroid Build Coastguard Worker namespace pw::multibuf {
23*61c4878aSAndroid Build Coastguard Worker namespace internal {
24*61c4878aSAndroid Build Coastguard Worker 
~LinkedRegionTracker()25*61c4878aSAndroid Build Coastguard Worker LinkedRegionTracker::~LinkedRegionTracker() {
26*61c4878aSAndroid Build Coastguard Worker   // The ``LinkedRegionTracker`` *must* be removed from the parent allocator's
27*61c4878aSAndroid Build Coastguard Worker   // region list prior to being destroyed, as doing so requires holding a lock.
28*61c4878aSAndroid Build Coastguard Worker   //
29*61c4878aSAndroid Build Coastguard Worker   // The destructor is only called via ``Destroy()``'s invocation of
30*61c4878aSAndroid Build Coastguard Worker   // ``metadata_alloc_ref.Delete(this);``
31*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(unlisted());
32*61c4878aSAndroid Build Coastguard Worker }
33*61c4878aSAndroid Build Coastguard Worker 
Destroy()34*61c4878aSAndroid Build Coastguard Worker void LinkedRegionTracker::Destroy() {
35*61c4878aSAndroid Build Coastguard Worker   SimpleAllocator::AvailableMemorySize available;
36*61c4878aSAndroid Build Coastguard Worker   {
37*61c4878aSAndroid Build Coastguard Worker     // N.B.: this lock *must* go out of scope before the call to
38*61c4878aSAndroid Build Coastguard Worker     // ``Delete(this)`` below in order to prevent referencing the ``parent_``
39*61c4878aSAndroid Build Coastguard Worker     // field after this tracker has been destroyed.
40*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(parent_.lock_);
41*61c4878aSAndroid Build Coastguard Worker     unlist();
42*61c4878aSAndroid Build Coastguard Worker     available = parent_.GetAvailableMemorySize();
43*61c4878aSAndroid Build Coastguard Worker   }
44*61c4878aSAndroid Build Coastguard Worker   parent_.MoreMemoryAvailable(available.total, available.contiguous);
45*61c4878aSAndroid Build Coastguard Worker   parent_.metadata_alloc_.Delete(this);
46*61c4878aSAndroid Build Coastguard Worker }
47*61c4878aSAndroid Build Coastguard Worker 
AllocateChunkClass()48*61c4878aSAndroid Build Coastguard Worker void* LinkedRegionTracker::AllocateChunkClass() {
49*61c4878aSAndroid Build Coastguard Worker   return parent_.metadata_alloc_.Allocate(allocator::Layout::Of<Chunk>());
50*61c4878aSAndroid Build Coastguard Worker }
51*61c4878aSAndroid Build Coastguard Worker 
DeallocateChunkClass(void * ptr)52*61c4878aSAndroid Build Coastguard Worker void LinkedRegionTracker::DeallocateChunkClass(void* ptr) {
53*61c4878aSAndroid Build Coastguard Worker   return parent_.metadata_alloc_.Deallocate(ptr);
54*61c4878aSAndroid Build Coastguard Worker }
55*61c4878aSAndroid Build Coastguard Worker 
56*61c4878aSAndroid Build Coastguard Worker }  // namespace internal
57*61c4878aSAndroid Build Coastguard Worker 
DoAllocate(size_t min_size,size_t desired_size,ContiguityRequirement contiguity_requirement)58*61c4878aSAndroid Build Coastguard Worker pw::Result<MultiBuf> SimpleAllocator::DoAllocate(
59*61c4878aSAndroid Build Coastguard Worker     size_t min_size,
60*61c4878aSAndroid Build Coastguard Worker     size_t desired_size,
61*61c4878aSAndroid Build Coastguard Worker     ContiguityRequirement contiguity_requirement) {
62*61c4878aSAndroid Build Coastguard Worker   if (min_size > data_area_.size()) {
63*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
64*61c4878aSAndroid Build Coastguard Worker   }
65*61c4878aSAndroid Build Coastguard Worker   // NB: std::lock_guard is not used here in order to release the lock
66*61c4878aSAndroid Build Coastguard Worker   // prior to destroying ``buf`` below.
67*61c4878aSAndroid Build Coastguard Worker   lock_.lock();
68*61c4878aSAndroid Build Coastguard Worker   auto available_memory_size = GetAvailableMemorySize();
69*61c4878aSAndroid Build Coastguard Worker   size_t available = (contiguity_requirement == kNeedsContiguous)
70*61c4878aSAndroid Build Coastguard Worker                          ? available_memory_size.contiguous
71*61c4878aSAndroid Build Coastguard Worker                          : available_memory_size.total;
72*61c4878aSAndroid Build Coastguard Worker   if (available < min_size) {
73*61c4878aSAndroid Build Coastguard Worker     lock_.unlock();
74*61c4878aSAndroid Build Coastguard Worker     return Status::ResourceExhausted();
75*61c4878aSAndroid Build Coastguard Worker   }
76*61c4878aSAndroid Build Coastguard Worker   size_t goal_size = std::min(desired_size, available);
77*61c4878aSAndroid Build Coastguard Worker   if (contiguity_requirement == kNeedsContiguous) {
78*61c4878aSAndroid Build Coastguard Worker     auto out = InternalAllocateContiguous(goal_size);
79*61c4878aSAndroid Build Coastguard Worker     lock_.unlock();
80*61c4878aSAndroid Build Coastguard Worker     return out;
81*61c4878aSAndroid Build Coastguard Worker   }
82*61c4878aSAndroid Build Coastguard Worker 
83*61c4878aSAndroid Build Coastguard Worker   MultiBuf buf;
84*61c4878aSAndroid Build Coastguard Worker   Status status;
85*61c4878aSAndroid Build Coastguard Worker   size_t remaining_goal = goal_size;
86*61c4878aSAndroid Build Coastguard Worker   ForEachFreeBlock(
87*61c4878aSAndroid Build Coastguard Worker       [this, &buf, &status, remaining_goal](const FreeBlock& block)
88*61c4878aSAndroid Build Coastguard Worker           PW_EXCLUSIVE_LOCKS_REQUIRED(lock_) mutable {
89*61c4878aSAndroid Build Coastguard Worker             if (remaining_goal == 0) {
90*61c4878aSAndroid Build Coastguard Worker               return ControlFlow::Break;
91*61c4878aSAndroid Build Coastguard Worker             }
92*61c4878aSAndroid Build Coastguard Worker             size_t chunk_size = std::min(block.span.size(), remaining_goal);
93*61c4878aSAndroid Build Coastguard Worker             pw::Result<OwnedChunk> chunk = InsertRegion(
94*61c4878aSAndroid Build Coastguard Worker                 {block.iter, ByteSpan(block.span.data(), chunk_size)});
95*61c4878aSAndroid Build Coastguard Worker             if (!chunk.ok()) {
96*61c4878aSAndroid Build Coastguard Worker               status = chunk.status();
97*61c4878aSAndroid Build Coastguard Worker               return ControlFlow::Break;
98*61c4878aSAndroid Build Coastguard Worker             }
99*61c4878aSAndroid Build Coastguard Worker             remaining_goal -= chunk->size();
100*61c4878aSAndroid Build Coastguard Worker             buf.PushFrontChunk(std::move(*chunk));
101*61c4878aSAndroid Build Coastguard Worker             return ControlFlow::Continue;
102*61c4878aSAndroid Build Coastguard Worker           });
103*61c4878aSAndroid Build Coastguard Worker   // Lock must be released prior to possibly free'ing the `buf` in the case
104*61c4878aSAndroid Build Coastguard Worker   // where `!status.ok()`. This is necessary so that the destructing chunks
105*61c4878aSAndroid Build Coastguard Worker   // can free their regions.
106*61c4878aSAndroid Build Coastguard Worker   lock_.unlock();
107*61c4878aSAndroid Build Coastguard Worker   if (!status.ok()) {
108*61c4878aSAndroid Build Coastguard Worker     return status;
109*61c4878aSAndroid Build Coastguard Worker   }
110*61c4878aSAndroid Build Coastguard Worker   return buf;
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker 
InternalAllocateContiguous(size_t size)113*61c4878aSAndroid Build Coastguard Worker pw::Result<MultiBuf> SimpleAllocator::InternalAllocateContiguous(size_t size) {
114*61c4878aSAndroid Build Coastguard Worker   pw::Result<MultiBuf> buf = Status::ResourceExhausted();
115*61c4878aSAndroid Build Coastguard Worker   ForEachFreeBlock([this, &buf, size](const FreeBlock& block)
116*61c4878aSAndroid Build Coastguard Worker                        PW_EXCLUSIVE_LOCKS_REQUIRED(lock_) {
117*61c4878aSAndroid Build Coastguard Worker                          if (block.span.size() >= size) {
118*61c4878aSAndroid Build Coastguard Worker                            ByteSpan buf_span(block.span.data(), size);
119*61c4878aSAndroid Build Coastguard Worker                            buf = InsertRegion({block.iter, buf_span})
120*61c4878aSAndroid Build Coastguard Worker                                      .transform(MultiBuf::FromChunk);
121*61c4878aSAndroid Build Coastguard Worker                            return ControlFlow::Break;
122*61c4878aSAndroid Build Coastguard Worker                          }
123*61c4878aSAndroid Build Coastguard Worker                          return ControlFlow::Continue;
124*61c4878aSAndroid Build Coastguard Worker                        });
125*61c4878aSAndroid Build Coastguard Worker   return buf;
126*61c4878aSAndroid Build Coastguard Worker }
127*61c4878aSAndroid Build Coastguard Worker 
InsertRegion(const FreeBlock & block)128*61c4878aSAndroid Build Coastguard Worker pw::Result<OwnedChunk> SimpleAllocator::InsertRegion(const FreeBlock& block) {
129*61c4878aSAndroid Build Coastguard Worker   internal::LinkedRegionTracker* new_region =
130*61c4878aSAndroid Build Coastguard Worker       metadata_alloc_.New<internal::LinkedRegionTracker>(*this, block.span);
131*61c4878aSAndroid Build Coastguard Worker   if (new_region == nullptr) {
132*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
133*61c4878aSAndroid Build Coastguard Worker   }
134*61c4878aSAndroid Build Coastguard Worker   std::optional<OwnedChunk> chunk = new_region->CreateFirstChunk();
135*61c4878aSAndroid Build Coastguard Worker   if (!chunk.has_value()) {
136*61c4878aSAndroid Build Coastguard Worker     metadata_alloc_.Delete(new_region);
137*61c4878aSAndroid Build Coastguard Worker     return Status::OutOfRange();
138*61c4878aSAndroid Build Coastguard Worker   }
139*61c4878aSAndroid Build Coastguard Worker   regions_.insert_after(block.iter, *new_region);
140*61c4878aSAndroid Build Coastguard Worker   return std::move(*chunk);
141*61c4878aSAndroid Build Coastguard Worker }
142*61c4878aSAndroid Build Coastguard Worker 
GetAvailableMemorySize()143*61c4878aSAndroid Build Coastguard Worker SimpleAllocator::AvailableMemorySize SimpleAllocator::GetAvailableMemorySize() {
144*61c4878aSAndroid Build Coastguard Worker   size_t total = 0;
145*61c4878aSAndroid Build Coastguard Worker   size_t max_contiguous = 0;
146*61c4878aSAndroid Build Coastguard Worker   ForEachFreeBlock([&total, &max_contiguous](const FreeBlock& block) {
147*61c4878aSAndroid Build Coastguard Worker     total += block.span.size();
148*61c4878aSAndroid Build Coastguard Worker     if (block.span.size() > max_contiguous) {
149*61c4878aSAndroid Build Coastguard Worker       max_contiguous = block.span.size();
150*61c4878aSAndroid Build Coastguard Worker     }
151*61c4878aSAndroid Build Coastguard Worker     return ControlFlow::Continue;
152*61c4878aSAndroid Build Coastguard Worker   });
153*61c4878aSAndroid Build Coastguard Worker 
154*61c4878aSAndroid Build Coastguard Worker   AvailableMemorySize available;
155*61c4878aSAndroid Build Coastguard Worker   available.total = total;
156*61c4878aSAndroid Build Coastguard Worker   available.contiguous = max_contiguous;
157*61c4878aSAndroid Build Coastguard Worker   return available;
158*61c4878aSAndroid Build Coastguard Worker }
159*61c4878aSAndroid Build Coastguard Worker 
160*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::multibuf
161