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