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