xref: /aosp_15_r20/external/pigweed/pw_multibuf/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/allocator.h"
16*61c4878aSAndroid Build Coastguard Worker 
17*61c4878aSAndroid Build Coastguard Worker #include <mutex>
18*61c4878aSAndroid Build Coastguard Worker 
19*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
20*61c4878aSAndroid Build Coastguard Worker 
21*61c4878aSAndroid Build Coastguard Worker namespace pw::multibuf {
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Context;
24*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Poll;
25*61c4878aSAndroid Build Coastguard Worker using ::pw::async2::Waker;
26*61c4878aSAndroid Build Coastguard Worker 
Allocate(size_t size)27*61c4878aSAndroid Build Coastguard Worker std::optional<MultiBuf> MultiBufAllocator::Allocate(size_t size) {
28*61c4878aSAndroid Build Coastguard Worker   return Allocate(size, size);
29*61c4878aSAndroid Build Coastguard Worker }
30*61c4878aSAndroid Build Coastguard Worker 
Allocate(size_t min_size,size_t desired_size)31*61c4878aSAndroid Build Coastguard Worker std::optional<MultiBuf> MultiBufAllocator::Allocate(size_t min_size,
32*61c4878aSAndroid Build Coastguard Worker                                                     size_t desired_size) {
33*61c4878aSAndroid Build Coastguard Worker   pw::Result<MultiBuf> result =
34*61c4878aSAndroid Build Coastguard Worker       DoAllocate(min_size, desired_size, kAllowDiscontiguous);
35*61c4878aSAndroid Build Coastguard Worker   if (result.ok()) {
36*61c4878aSAndroid Build Coastguard Worker     return std::move(*result);
37*61c4878aSAndroid Build Coastguard Worker   }
38*61c4878aSAndroid Build Coastguard Worker   return std::nullopt;
39*61c4878aSAndroid Build Coastguard Worker }
40*61c4878aSAndroid Build Coastguard Worker 
AllocateContiguous(size_t size)41*61c4878aSAndroid Build Coastguard Worker std::optional<MultiBuf> MultiBufAllocator::AllocateContiguous(size_t size) {
42*61c4878aSAndroid Build Coastguard Worker   return AllocateContiguous(size, size);
43*61c4878aSAndroid Build Coastguard Worker }
44*61c4878aSAndroid Build Coastguard Worker 
AllocateContiguous(size_t min_size,size_t desired_size)45*61c4878aSAndroid Build Coastguard Worker std::optional<MultiBuf> MultiBufAllocator::AllocateContiguous(
46*61c4878aSAndroid Build Coastguard Worker     size_t min_size, size_t desired_size) {
47*61c4878aSAndroid Build Coastguard Worker   pw::Result<MultiBuf> result =
48*61c4878aSAndroid Build Coastguard Worker       DoAllocate(min_size, desired_size, kNeedsContiguous);
49*61c4878aSAndroid Build Coastguard Worker   if (result.ok()) {
50*61c4878aSAndroid Build Coastguard Worker     return std::move(*result);
51*61c4878aSAndroid Build Coastguard Worker   }
52*61c4878aSAndroid Build Coastguard Worker   return std::nullopt;
53*61c4878aSAndroid Build Coastguard Worker }
54*61c4878aSAndroid Build Coastguard Worker 
AllocateAsync(size_t size)55*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture MultiBufAllocator::AllocateAsync(size_t size) {
56*61c4878aSAndroid Build Coastguard Worker   return MultiBufAllocationFuture(*this, size, size, kAllowDiscontiguous);
57*61c4878aSAndroid Build Coastguard Worker }
AllocateAsync(size_t min_size,size_t desired_size)58*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture MultiBufAllocator::AllocateAsync(size_t min_size,
59*61c4878aSAndroid Build Coastguard Worker                                                           size_t desired_size) {
60*61c4878aSAndroid Build Coastguard Worker   return MultiBufAllocationFuture(
61*61c4878aSAndroid Build Coastguard Worker       *this, min_size, desired_size, kAllowDiscontiguous);
62*61c4878aSAndroid Build Coastguard Worker }
AllocateContiguousAsync(size_t size)63*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture MultiBufAllocator::AllocateContiguousAsync(
64*61c4878aSAndroid Build Coastguard Worker     size_t size) {
65*61c4878aSAndroid Build Coastguard Worker   return MultiBufAllocationFuture(*this, size, size, kNeedsContiguous);
66*61c4878aSAndroid Build Coastguard Worker }
AllocateContiguousAsync(size_t min_size,size_t desired_size)67*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture MultiBufAllocator::AllocateContiguousAsync(
68*61c4878aSAndroid Build Coastguard Worker     size_t min_size, size_t desired_size) {
69*61c4878aSAndroid Build Coastguard Worker   return MultiBufAllocationFuture(
70*61c4878aSAndroid Build Coastguard Worker       *this, min_size, desired_size, kNeedsContiguous);
71*61c4878aSAndroid Build Coastguard Worker }
72*61c4878aSAndroid Build Coastguard Worker 
MoreMemoryAvailable(size_t size_available,size_t contiguous_size_available)73*61c4878aSAndroid Build Coastguard Worker void MultiBufAllocator::MoreMemoryAvailable(size_t size_available,
74*61c4878aSAndroid Build Coastguard Worker                                             size_t contiguous_size_available)
75*61c4878aSAndroid Build Coastguard Worker     // Disable lock safety analysis: the access to `next_` requires locking
76*61c4878aSAndroid Build Coastguard Worker     // `waiter->allocator_->lock_`, but that's the same as `lock_` which we
77*61c4878aSAndroid Build Coastguard Worker     // already hold.
78*61c4878aSAndroid Build Coastguard Worker     PW_NO_LOCK_SAFETY_ANALYSIS {
79*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(lock_);
80*61c4878aSAndroid Build Coastguard Worker   waiting_futures_.remove_if([this, size_available, contiguous_size_available](
81*61c4878aSAndroid Build Coastguard Worker                                  const MultiBufAllocationFuture& future) {
82*61c4878aSAndroid Build Coastguard Worker     PW_CHECK_PTR_EQ(future.allocator_, this);
83*61c4878aSAndroid Build Coastguard Worker     bool should_wake_and_remove =
84*61c4878aSAndroid Build Coastguard Worker         ((future.min_size_ <= contiguous_size_available) ||
85*61c4878aSAndroid Build Coastguard Worker          (future.contiguity_requirement_ == kAllowDiscontiguous &&
86*61c4878aSAndroid Build Coastguard Worker           future.min_size_ <= size_available));
87*61c4878aSAndroid Build Coastguard Worker     if (should_wake_and_remove) {
88*61c4878aSAndroid Build Coastguard Worker       std::move(const_cast<Waker&>(future.waker_)).Wake();
89*61c4878aSAndroid Build Coastguard Worker     }
90*61c4878aSAndroid Build Coastguard Worker     return should_wake_and_remove;
91*61c4878aSAndroid Build Coastguard Worker   });
92*61c4878aSAndroid Build Coastguard Worker }
93*61c4878aSAndroid Build Coastguard Worker 
MultiBufAllocationFuture(MultiBufAllocationFuture && other)94*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture::MultiBufAllocationFuture(
95*61c4878aSAndroid Build Coastguard Worker     MultiBufAllocationFuture&& other)
96*61c4878aSAndroid Build Coastguard Worker     : allocator_(other.allocator_),
97*61c4878aSAndroid Build Coastguard Worker       waker_(),
98*61c4878aSAndroid Build Coastguard Worker       min_size_(other.min_size_),
99*61c4878aSAndroid Build Coastguard Worker       desired_size_(other.desired_size_),
100*61c4878aSAndroid Build Coastguard Worker       contiguity_requirement_(other.contiguity_requirement_) {
101*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(allocator_->lock_);
102*61c4878aSAndroid Build Coastguard Worker   if (!other.unlisted()) {
103*61c4878aSAndroid Build Coastguard Worker     allocator_->waiting_futures_.remove(other);
104*61c4878aSAndroid Build Coastguard Worker     allocator_->waiting_futures_.push_front(*this);
105*61c4878aSAndroid Build Coastguard Worker     // We must move the waker under the lock in order to ensure that there is no
106*61c4878aSAndroid Build Coastguard Worker     // race between swapping ``MultiBufAllocationFuture``s and the waker being
107*61c4878aSAndroid Build Coastguard Worker     // awoken by the allocator.
108*61c4878aSAndroid Build Coastguard Worker     waker_ = std::move(other.waker_);
109*61c4878aSAndroid Build Coastguard Worker   }
110*61c4878aSAndroid Build Coastguard Worker }
111*61c4878aSAndroid Build Coastguard Worker 
operator =(MultiBufAllocationFuture && other)112*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture& MultiBufAllocationFuture::operator=(
113*61c4878aSAndroid Build Coastguard Worker     MultiBufAllocationFuture&& other) {
114*61c4878aSAndroid Build Coastguard Worker   {
115*61c4878aSAndroid Build Coastguard Worker     std::lock_guard lock(allocator_->lock_);
116*61c4878aSAndroid Build Coastguard Worker     if (!this->unlisted()) {
117*61c4878aSAndroid Build Coastguard Worker       allocator_->waiting_futures_.remove(*this);
118*61c4878aSAndroid Build Coastguard Worker     }
119*61c4878aSAndroid Build Coastguard Worker   }
120*61c4878aSAndroid Build Coastguard Worker 
121*61c4878aSAndroid Build Coastguard Worker   allocator_ = other.allocator_;
122*61c4878aSAndroid Build Coastguard Worker   min_size_ = other.min_size_;
123*61c4878aSAndroid Build Coastguard Worker   desired_size_ = other.desired_size_;
124*61c4878aSAndroid Build Coastguard Worker   contiguity_requirement_ = other.contiguity_requirement_;
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(allocator_->lock_);
127*61c4878aSAndroid Build Coastguard Worker   if (!other.unlisted()) {
128*61c4878aSAndroid Build Coastguard Worker     allocator_->waiting_futures_.remove(other);
129*61c4878aSAndroid Build Coastguard Worker     allocator_->waiting_futures_.push_front(*this);
130*61c4878aSAndroid Build Coastguard Worker     // We must move the waker under the lock in order to ensure that there is no
131*61c4878aSAndroid Build Coastguard Worker     // race between swapping ``MultiBufAllocationFuture``s and the waker being
132*61c4878aSAndroid Build Coastguard Worker     // awoken by the allocator.
133*61c4878aSAndroid Build Coastguard Worker     waker_ = std::move(other.waker_);
134*61c4878aSAndroid Build Coastguard Worker   }
135*61c4878aSAndroid Build Coastguard Worker 
136*61c4878aSAndroid Build Coastguard Worker   return *this;
137*61c4878aSAndroid Build Coastguard Worker }
138*61c4878aSAndroid Build Coastguard Worker 
~MultiBufAllocationFuture()139*61c4878aSAndroid Build Coastguard Worker MultiBufAllocationFuture::~MultiBufAllocationFuture() {
140*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(allocator_->lock_);
141*61c4878aSAndroid Build Coastguard Worker   if (!this->unlisted()) {
142*61c4878aSAndroid Build Coastguard Worker     allocator_->waiting_futures_.remove(*this);
143*61c4878aSAndroid Build Coastguard Worker   }
144*61c4878aSAndroid Build Coastguard Worker }
145*61c4878aSAndroid Build Coastguard Worker 
SetDesiredSizes(size_t new_min_size,size_t new_desired_size,ContiguityRequirement new_contiguity_requirement)146*61c4878aSAndroid Build Coastguard Worker void MultiBufAllocationFuture::SetDesiredSizes(
147*61c4878aSAndroid Build Coastguard Worker     size_t new_min_size,
148*61c4878aSAndroid Build Coastguard Worker     size_t new_desired_size,
149*61c4878aSAndroid Build Coastguard Worker     ContiguityRequirement new_contiguity_requirement) {
150*61c4878aSAndroid Build Coastguard Worker   // No-op if the sizes are unchanged.
151*61c4878aSAndroid Build Coastguard Worker   if (new_min_size == min_size_ && new_desired_size == desired_size_ &&
152*61c4878aSAndroid Build Coastguard Worker       new_contiguity_requirement == contiguity_requirement_) {
153*61c4878aSAndroid Build Coastguard Worker     return;
154*61c4878aSAndroid Build Coastguard Worker   }
155*61c4878aSAndroid Build Coastguard Worker   // Acquire the lock so the allocator doesn't touch the sizes while we're
156*61c4878aSAndroid Build Coastguard Worker   // modifying them.
157*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(allocator_->lock_);
158*61c4878aSAndroid Build Coastguard Worker 
159*61c4878aSAndroid Build Coastguard Worker   // If our needs decreased, try allocating again next time rather than
160*61c4878aSAndroid Build Coastguard Worker   // waiting for a wake.
161*61c4878aSAndroid Build Coastguard Worker   if (new_min_size < min_size_ ||
162*61c4878aSAndroid Build Coastguard Worker       ((new_contiguity_requirement == kAllowDiscontiguous) &&
163*61c4878aSAndroid Build Coastguard Worker        (contiguity_requirement_ == kNeedsContiguous))) {
164*61c4878aSAndroid Build Coastguard Worker     if (!this->unlisted()) {
165*61c4878aSAndroid Build Coastguard Worker       allocator_->waiting_futures_.remove(*this);
166*61c4878aSAndroid Build Coastguard Worker     }
167*61c4878aSAndroid Build Coastguard Worker   }
168*61c4878aSAndroid Build Coastguard Worker   min_size_ = new_min_size;
169*61c4878aSAndroid Build Coastguard Worker   desired_size_ = new_desired_size;
170*61c4878aSAndroid Build Coastguard Worker   contiguity_requirement_ = new_contiguity_requirement;
171*61c4878aSAndroid Build Coastguard Worker }
172*61c4878aSAndroid Build Coastguard Worker 
Pend(Context & cx)173*61c4878aSAndroid Build Coastguard Worker Poll<std::optional<MultiBuf>> MultiBufAllocationFuture::Pend(Context& cx) {
174*61c4878aSAndroid Build Coastguard Worker   std::lock_guard lock(allocator_->lock_);
175*61c4878aSAndroid Build Coastguard Worker   // If we're still listed waiting for a wakeup, don't bother to try again.
176*61c4878aSAndroid Build Coastguard Worker   if (this->unlisted()) {
177*61c4878aSAndroid Build Coastguard Worker     auto result = TryAllocate();
178*61c4878aSAndroid Build Coastguard Worker     if (result.IsReady()) {
179*61c4878aSAndroid Build Coastguard Worker       return result;
180*61c4878aSAndroid Build Coastguard Worker     }
181*61c4878aSAndroid Build Coastguard Worker     allocator_->waiting_futures_.push_front(*this);
182*61c4878aSAndroid Build Coastguard Worker   }
183*61c4878aSAndroid Build Coastguard Worker   // We set the waker while still holding the lock to ensure there is no gap
184*61c4878aSAndroid Build Coastguard Worker   // between us checking TryAllocate above and the waker being reset here.
185*61c4878aSAndroid Build Coastguard Worker   PW_ASYNC_STORE_WAKER(
186*61c4878aSAndroid Build Coastguard Worker       cx,
187*61c4878aSAndroid Build Coastguard Worker       waker_,
188*61c4878aSAndroid Build Coastguard Worker       "MultiBufAllocationFuture is waiting for memory to become available");
189*61c4878aSAndroid Build Coastguard Worker   return async2::Pending();
190*61c4878aSAndroid Build Coastguard Worker }
191*61c4878aSAndroid Build Coastguard Worker 
TryAllocate()192*61c4878aSAndroid Build Coastguard Worker Poll<std::optional<MultiBuf>> MultiBufAllocationFuture::TryAllocate() {
193*61c4878aSAndroid Build Coastguard Worker   Result<MultiBuf> buf_opt =
194*61c4878aSAndroid Build Coastguard Worker       allocator_->DoAllocate(min_size_, desired_size_, contiguity_requirement_);
195*61c4878aSAndroid Build Coastguard Worker   if (buf_opt.ok()) {
196*61c4878aSAndroid Build Coastguard Worker     return async2::Ready<std::optional<MultiBuf>>(std::move(*buf_opt));
197*61c4878aSAndroid Build Coastguard Worker   }
198*61c4878aSAndroid Build Coastguard Worker   if (buf_opt.status().IsOutOfRange()) {
199*61c4878aSAndroid Build Coastguard Worker     return async2::Ready<std::optional<MultiBuf>>(std::nullopt);
200*61c4878aSAndroid Build Coastguard Worker   }
201*61c4878aSAndroid Build Coastguard Worker   return async2::Pending();
202*61c4878aSAndroid Build Coastguard Worker }
203*61c4878aSAndroid Build Coastguard Worker 
204*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::multibuf
205