xref: /aosp_15_r20/external/pigweed/pw_multibuf/public/pw_multibuf/allocator.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 <optional>
17 
18 #include "pw_async2/dispatcher.h"
19 #include "pw_containers/intrusive_forward_list.h"
20 #include "pw_multibuf/multibuf.h"
21 #include "pw_result/result.h"
22 #include "pw_sync/interrupt_spin_lock.h"
23 
24 namespace pw::multibuf {
25 
26 class MultiBufAllocationFuture;
27 
28 enum class ContiguityRequirement {
29   kAllowDiscontiguous,
30   kNeedsContiguous,
31 };
32 
33 inline constexpr ContiguityRequirement kAllowDiscontiguous =
34     ContiguityRequirement::kAllowDiscontiguous;
35 inline constexpr ContiguityRequirement kNeedsContiguous =
36     ContiguityRequirement::kNeedsContiguous;
37 
38 /// Interface for allocating ``MultiBuf`` objects.
39 ///
40 /// A ``MultiBufAllocator`` differs from a regular ``pw::allocator::Allocator``
41 /// in that they may provide support for:
42 /// - Asynchronous allocation.
43 /// - Non-contiguous buffer allocation.
44 /// - Internal header/footer reservation.
45 /// - Size-range allocation.
46 ///
47 /// In order to accomplish this, they return ``MultiBuf`` objects rather than
48 /// arbitrary pieces of memory.
49 ///
50 /// Additionally, ``MultiBufAllocator`` implementations may choose to store
51 /// their allocation metadata separately from the data itself. This allows for
52 /// things like allocation headers to be kept out of restricted DMA-capable or
53 /// shared-memory regions.
54 ///
55 /// NOTE: ``MultiBufAllocator``s *must* outlive any futures created from them.
56 class MultiBufAllocator {
57  public:
58   MultiBufAllocator() = default;
59 
60   /// ```MultiBufAllocator`` is not copyable or movable.
61   MultiBufAllocator(MultiBufAllocator&) = delete;
62   MultiBufAllocator& operator=(MultiBufAllocator&) = delete;
63   MultiBufAllocator(MultiBufAllocator&&) = delete;
64   MultiBufAllocator& operator=(MultiBufAllocator&&) = delete;
65 
~MultiBufAllocator()66   virtual ~MultiBufAllocator() {}
67 
68   ////////////////
69   // -- Sync -- //
70   ////////////////
71 
72   /// Attempts to allocate a ``MultiBuf`` of exactly ``size`` bytes.
73   ///
74   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
75   /// any alignment requirments, preferring instead to allow the allocator
76   /// maximum flexibility for placing regions (especially discontiguous
77   /// regions).
78   ///
79   /// @retval ``MultiBuf`` if the allocation was successful.
80   /// @retval ``nullopt_t`` if the memory is not currently available.
81   std::optional<MultiBuf> Allocate(size_t size);
82 
83   /// Attempts to allocate a ``MultiBuf`` of at least ``min_size`` bytes and at
84   /// most ``desired_size`` bytes.
85   ///
86   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
87   /// any alignment requirments, preferring instead to allow the allocator
88   /// maximum flexibility for placing regions (especially discontiguous
89   /// regions).
90   ///
91   /// @retval ``MultiBuf`` if the allocation was successful.
92   /// @retval ``nullopt_t`` if the memory is not currently available.
93   std::optional<MultiBuf> Allocate(size_t min_size, size_t desired_size);
94 
95   /// Attempts to allocate a contiguous ``MultiBuf`` of exactly ``size``
96   /// bytes.
97   ///
98   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
99   /// any alignment requirments, preferring instead to allow the allocator
100   /// maximum flexibility for placing regions (especially discontiguous
101   /// regions).
102   ///
103   /// @retval ``MultiBuf`` with a single ``Chunk`` if the allocation was
104   /// successful.
105   /// @retval ``nullopt_t`` if the memory is not currently available.
106   std::optional<MultiBuf> AllocateContiguous(size_t size);
107 
108   /// Attempts to allocate a contiguous ``MultiBuf`` of at least ``min_size``
109   /// bytes and at most ``desired_size`` bytes.
110   ///
111   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
112   /// any alignment requirments, preferring instead to allow the allocator
113   /// maximum flexibility for placing regions (especially discontiguous
114   /// regions).
115   ///
116   /// @retval ``MultiBuf`` with a single ``Chunk`` if the allocation was
117   /// successful.
118   /// @retval ``nullopt_t`` if the memory is not currently available.
119   std::optional<MultiBuf> AllocateContiguous(size_t min_size,
120                                              size_t desired_size);
121 
122   /////////////////
123   // -- Async -- //
124   /////////////////
125 
126   /// Asynchronously allocates a ``MultiBuf`` of exactly ``size`` bytes.
127   ///
128   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
129   /// any alignment requirments, preferring instead to allow the allocator
130   /// maximum flexibility for placing regions (especially discontiguous
131   /// regions).
132   ///
133   /// @retval A ``MultiBufAllocationFuture`` which will yield a ``MultiBuf``
134   /// when one is available.
135   MultiBufAllocationFuture AllocateAsync(size_t size);
136 
137   /// Asynchronously allocates a ``MultiBuf`` of at least
138   /// ``min_size`` bytes and at most ``desired_size` bytes.
139   ///
140   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
141   /// any alignment requirments, preferring instead to allow the allocator
142   /// maximum flexibility for placing regions (especially discontiguous
143   /// regions).
144   ///
145   /// @retval A ``MultiBufAllocationFuture`` which will yield a ``MultiBuf``
146   /// when one is available.
147   MultiBufAllocationFuture AllocateAsync(size_t min_size, size_t desired_size);
148 
149   /// Asynchronously allocates a contiguous ``MultiBuf`` of exactly ``size``
150   /// bytes.
151   ///
152   /// Memory allocated by an arbitrary ``MultiBufAllocator`` does not provide
153   /// any alignment requirments, preferring instead to allow the allocator
154   /// maximum flexibility for placing regions (especially discontiguous
155   /// regions).
156   ///
157   /// @retval A ``MultiBufAllocationFuture`` which will yield an ``MultiBuf``
158   /// consisting of a single ``Chunk`` when one is available.
159   MultiBufAllocationFuture AllocateContiguousAsync(size_t size);
160 
161   /// Asynchronously allocates an ``OwnedChunk`` of at least
162   /// ``min_size`` bytes and at most ``desired_size`` bytes.
163   ///
164   /// @retval A ``MultiBufAllocationFuture`` which will yield an ``MultiBuf``
165   /// consisting of a single ``Chunk`` when one is available.
166   MultiBufAllocationFuture AllocateContiguousAsync(size_t min_size,
167                                                    size_t desired_size);
168 
169  protected:
170   /// Awakens callers asynchronously waiting for allocations of at most
171   /// ``size_available`` bytes or at most ``contiguous_size_available``
172   /// contiguous bytes.
173   ///
174   /// This function should be invoked by implementations of
175   /// ``MultiBufAllocator`` when more memory becomes available to allocate.
176   void MoreMemoryAvailable(size_t size_available,
177                            size_t contiguous_size_available);
178 
179  private:
180   friend class MultiBufAllocationFuture;
181 
182   /// Attempts to allocate a ``MultiBuf`` of at least ``min_size`` bytes and at
183   /// most ``desired_size`` bytes.
184   ///
185   /// @returns @rst
186   ///
187   /// .. pw-status-codes::
188   ///
189   ///    OK: Returns the buffer if the allocation was successful.
190   ///
191   ///    RESOURCE_EXHAUSTED: Insufficient memory is available currently.
192   ///
193   ///    OUT_OF_RANGE: This amount of memory will not become possible to
194   ///    allocate in the future, or this allocator is unable to signal via
195   ///    ``MoreMemoryAvailable`` (this will result in asynchronous allocations
196   ///    failing immediately on OOM).
197   ///
198   /// @endrst
199   virtual pw::Result<MultiBuf> DoAllocate(
200       size_t min_size,
201       size_t desired_size,
202       ContiguityRequirement contiguity_requirement) = 0;
203 
204   sync::InterruptSpinLock lock_;
205   IntrusiveForwardList<MultiBufAllocationFuture> waiting_futures_
206       PW_GUARDED_BY(lock_);
207 };
208 
209 /// An object that asynchronously yields a ``MultiBuf`` when ``Pend``ed.
210 ///
211 /// See ``pw::async2`` for details on ``Pend`` and how it is used to build
212 /// asynchronous tasks.
213 class MultiBufAllocationFuture
214     : public IntrusiveForwardList<MultiBufAllocationFuture>::Item {
215  public:
MultiBufAllocationFuture(MultiBufAllocator & allocator)216   constexpr explicit MultiBufAllocationFuture(MultiBufAllocator& allocator)
217       : allocator_(&allocator),
218         min_size_(0),
219         desired_size_(0),
220         contiguity_requirement_(kAllowDiscontiguous) {}
MultiBufAllocationFuture(MultiBufAllocator & allocator,size_t min_size,size_t desired_size,ContiguityRequirement contiguity_requirement)221   MultiBufAllocationFuture(MultiBufAllocator& allocator,
222                            size_t min_size,
223                            size_t desired_size,
224                            ContiguityRequirement contiguity_requirement)
225       : allocator_(&allocator),
226         min_size_(min_size),
227         desired_size_(desired_size),
228         contiguity_requirement_(contiguity_requirement) {}
229 
230   MultiBufAllocationFuture(MultiBufAllocationFuture&&);
231   MultiBufAllocationFuture& operator=(MultiBufAllocationFuture&&);
232   ~MultiBufAllocationFuture();
233 
SetDesiredSize(size_t min_size)234   void SetDesiredSize(size_t min_size) {
235     SetDesiredSizes(min_size, min_size, kAllowDiscontiguous);
236   }
237   void SetDesiredSizes(size_t min_size,
238                        size_t desired_size,
239                        ContiguityRequirement contiguity_requirement);
240   async2::Poll<std::optional<MultiBuf>> Pend(async2::Context& cx);
241 
242   /// Returns the ``allocator`` associated with this future.
allocator()243   MultiBufAllocator& allocator() { return *allocator_; }
min_size()244   size_t min_size() const { return min_size_; }
desired_size()245   size_t desired_size() const { return min_size_; }
needs_contiguous()246   bool needs_contiguous() const {
247     return contiguity_requirement_ == kNeedsContiguous;
248   }
249 
250  private:
251   friend class MultiBufAllocator;
252 
253   /// Attempts to allocate with the stored parameters.
254   async2::Poll<std::optional<MultiBuf>> TryAllocate();
255 
256   // The allocator this future is tied to.
257   MultiBufAllocator* allocator_;
258 
259   // The waker to wake when a suitably-sized allocation becomes available.
260   async2::Waker waker_;
261 
262   // The properties of the kind of allocation being waited for.
263   //
264   // These properties can only be mutated by the owner of the
265   // MultiBufAllocationFuture while holding the allocator's lock,
266   // however the MultiBufAllocationFuture owner can freely read these values
267   // without needing to acquire the lock.
268   //
269   // The allocator may read these values so long as this value is listed and
270   // the allocator holds the lock.
271   size_t min_size_;
272   size_t desired_size_;
273   ContiguityRequirement contiguity_requirement_;
274 };
275 
276 }  // namespace pw::multibuf
277