Android-cuttlefish cvd tool
unique_resource_allocator.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <functional>
20#include <memory>
21#include <mutex>
22#include <optional>
23#include <set>
24#include <type_traits>
25#include <unordered_set>
26#include <utility>
27#include <vector>
28
30
32
33namespace cuttlefish {
34
40template <typename T>
42 template <typename U>
44 typename std::remove_cv_t<typename std::remove_reference_t<U>>;
45
46 public:
47 /*
48 * Returning the inner resource to the pool at destruction time
49 *
50 * The pool must live longer than the resources. Use this like you use
51 * std::unique_ptr.
52 */
55 friend class ReservationSet;
56
57 public:
58 Reservation(const Reservation&) = delete;
61 src.resource_pool_ = nullptr;
62 }
65
66 bool operator==(const Reservation& src) const {
67 return (resource_ == src.resource_ &&
69 }
70
72 if (resource_pool_) {
74 }
75 }
76 const T& Get() const { return *resource_; }
77
78 private:
79 Reservation(UniqueResourceAllocator& resource_pool, const T& resource)
80 : resource_pool_(std::addressof(resource_pool)),
81 resource_(std::addressof(resource)) {}
82 /*
83 * Once this Reservation is std::move-ed out to other object,
84 * resource_pool_ should be invalidated, and resource_ shouldn't
85 * be tried to be returned to the invalid resource_pool_
86 */
88 const T* resource_;
89 };
90
92 std::size_t operator()(const Reservation& resource_wrapper) const {
93 return std::hash<const T*>()(std::addressof(resource_wrapper.Get()));
94 }
95 };
96 using ReservationSet = std::unordered_set<Reservation, ReservationHash>;
97 /*
98 * Creates the singleton object.
99 *
100 * Call this function once during the entire program's life
101 */
102 static UniqueResourceAllocator& Create(const std::vector<T>& pool) {
103 static UniqueResourceAllocator singleton_allocator(pool);
104 return singleton_allocator;
105 }
106
107 static std::unique_ptr<UniqueResourceAllocator> New(
108 const std::vector<T>& pool) {
110 return std::unique_ptr<UniqueResourceAllocator>(new_allocator);
111 }
112
113 // Adds the elements from new pool that did not belong to and have not
114 // belonged to the current pool of the allocator. returns the leftover
115 std::vector<T> ExpandPool(std::vector<T> another_pool) {
116 std::lock_guard lock(mutex_);
117 std::vector<T> not_selected;
118 for (auto& new_item : another_pool) {
119 if (Contains(available_resources_, new_item) ||
120 Contains(allocated_resources_, new_item)) {
121 not_selected.emplace_back(std::move(new_item));
122 continue;
123 }
124 available_resources_.insert(std::move(new_item));
125 }
126 return not_selected;
127 }
128
129 std::vector<T> ExpandPool(T&& t) {
130 std::vector<T> pool_to_add;
131 pool_to_add.emplace_back(std::move(t));
132 return ExpandPool(std::move(pool_to_add));
133 }
134
135 std::vector<T> ExpandPool(const T& t) {
136 std::vector<T> pool_to_add;
137 pool_to_add.emplace_back(t);
138 return ExpandPool(std::move(pool_to_add));
139 }
140
141 std::optional<Reservation> UniqueItem() {
142 std::lock_guard<std::mutex> lock(mutex_);
143 auto itr = available_resources_.begin();
144 if (itr == available_resources_.end()) {
145 return std::nullopt;
146 }
147 Reservation r(*this, *(RemoveFromPool(itr)));
148 return {std::move(r)};
149 }
150
151 // gives n unique integers from the pool, and then remove them from the pool
152 std::optional<ReservationSet> UniqueItems(const int n) {
153 std::lock_guard<std::mutex> lock(mutex_);
154 if (n <= 0 || available_resources_.size() < n) {
155 return std::nullopt;
156 }
157 ReservationSet result;
158 for (int i = 0; i < n; i++) {
159 auto itr = available_resources_.begin();
160 result.insert(Reservation{*this, *(RemoveFromPool(itr))});
161 }
162 return {std::move(result)};
163 }
164
165 template <typename V = T>
166 std::enable_if_t<std::is_integral<V>::value, std::optional<ReservationSet>>
167 UniqueConsecutiveItems(const std::size_t n) {
168 static_assert(std::is_same<T, V>::value);
169 std::lock_guard<std::mutex> lock(mutex_);
170 if (n <= 0 || available_resources_.size() < n) {
171 return std::nullopt;
172 }
173
174 for (const auto& available_resource : available_resources_) {
175 auto start_inclusive = available_resource;
176 auto resources_opt =
177 TakeRangeInternal(start_inclusive, start_inclusive + n);
178 if (!resources_opt) {
179 continue;
180 }
181 return resources_opt;
182 }
183 return std::nullopt;
184 }
185
186 // takes t if available
187 // returns false if not available or not in the pool at all
188 std::optional<Reservation> Take(const T& t) {
189 std::lock_guard<std::mutex> lock(mutex_);
190 auto itr = available_resources_.find(t);
191 if (itr == available_resources_.end()) {
192 return std::nullopt;
193 }
194 Reservation resource{*this, *(RemoveFromPool(itr))};
195 return resource;
196 }
197
198 template <typename Container>
199 std::optional<ReservationSet> TakeAll(const Container& ts) {
200 std::lock_guard<std::mutex> lock(mutex_);
201 for (const auto& t : ts) {
203 return std::nullopt;
204 }
205 }
206 ReservationSet resources;
207 for (const auto& t : ts) {
208 auto itr = available_resources_.find(t);
209 resources.insert(Reservation{*this, *(RemoveFromPool(itr))});
210 }
211 return resources;
212 }
213
214 /*
215 * If the range is available, returns the resources from the pool
216 *
217 * Otherwise, makes no change in the internal data structure but
218 * returns false.
219 */
220 template <typename V = T>
221 std::enable_if_t<std::is_integral<V>::value, std::optional<ReservationSet>>
222 TakeRange(const T& start_inclusive, const T& end_exclusive) {
223 static_assert(std::is_same<T, V>::value);
224 std::lock_guard<std::mutex> lock(mutex_);
225 return TakeRangeInternal(start_inclusive, end_exclusive);
226 }
227
228 private:
229 template <typename Container>
230 UniqueResourceAllocator(const Container& items)
231 : available_resources_{items.cbegin(), items.cend()} {}
232
233 bool operator==(const UniqueResourceAllocator& other) const {
234 return std::addressof(*this) == std::addressof(other);
235 }
236
237 // only called by the destructor of Reservation
238 // harder to use Result as this is called by destructors only
239 void Reclaim(const T& t) {
240 std::lock_guard<std::mutex> lock(mutex_);
241 auto itr = allocated_resources_.find(t);
242 if (itr == allocated_resources_.end()) {
244 LOG(ERROR) << "The resource " << t << " does not belong to this pool";
245 return;
246 }
247 // already reclaimed.
248 return;
249 }
250 T tmp = std::move(*itr);
251 allocated_resources_.erase(itr);
252 available_resources_.insert(std::move(tmp));
253 }
254
255 /*
256 * If the range is available, returns the resources from the pool
257 *
258 * Otherwise, makes no change in the internal data structure but
259 * returns false.
260 */
261 template <typename V = T>
262 std::enable_if_t<std::is_integral<V>::value, std::optional<ReservationSet>>
263 TakeRangeInternal(const T& start_inclusive, const T& end_exclusive) {
264 static_assert(std::is_same<T, V>::value);
265 for (auto cursor = start_inclusive; cursor < end_exclusive; cursor++) {
266 if (!Contains(available_resources_, cursor)) {
267 return std::nullopt;
268 }
269 }
270 ReservationSet resources;
271 for (auto cursor = start_inclusive; cursor < end_exclusive; cursor++) {
272 auto itr = available_resources_.find(cursor);
273 resources.insert(Reservation{*this, *(RemoveFromPool(itr))});
274 }
275 return resources;
276 }
277
278 /*
279 * Moves *itr from available_resources_ to allocated_resources_, and returns
280 * the pointer of the object in the allocated_resources_. The pointer is never
281 * nullptr as it is std::addressof(an object in the unordered_set buffer).
282 *
283 * The itr must belong to available_resources_.
284 */
285 const T* RemoveFromPool(const typename std::set<T>::iterator itr) {
286 T tmp = std::move(*itr);
287 available_resources_.erase(itr);
288 const auto [new_itr, _] = allocated_resources_.insert(std::move(tmp));
289 return std::addressof(*new_itr);
290 }
292 std::unordered_set<T> allocated_resources_;
293 std::mutex mutex_;
294};
295
296} // namespace cuttlefish
Definition: unique_resource_allocator.h:53
Reservation(Reservation &&src)
Definition: unique_resource_allocator.h:59
Reservation & operator=(Reservation &&src)=delete
UniqueResourceAllocator * resource_pool_
Definition: unique_resource_allocator.h:87
const T & Get() const
Definition: unique_resource_allocator.h:76
Reservation & operator=(const Reservation &)=delete
Reservation(UniqueResourceAllocator &resource_pool, const T &resource)
Definition: unique_resource_allocator.h:79
const T * resource_
Definition: unique_resource_allocator.h:88
~Reservation()
Definition: unique_resource_allocator.h:71
bool operator==(const Reservation &src) const
Definition: unique_resource_allocator.h:66
Definition: unique_resource_allocator.h:41
std::enable_if_t< std::is_integral< V >::value, std::optional< ReservationSet > > TakeRange(const T &start_inclusive, const T &end_exclusive)
Definition: unique_resource_allocator.h:222
std::unordered_set< T > allocated_resources_
Definition: unique_resource_allocator.h:292
std::vector< T > ExpandPool(std::vector< T > another_pool)
Definition: unique_resource_allocator.h:115
std::optional< ReservationSet > TakeAll(const Container &ts)
Definition: unique_resource_allocator.h:199
static UniqueResourceAllocator & Create(const std::vector< T > &pool)
Definition: unique_resource_allocator.h:102
const T * RemoveFromPool(const typename std::set< T >::iterator itr)
Definition: unique_resource_allocator.h:285
std::enable_if_t< std::is_integral< V >::value, std::optional< ReservationSet > > TakeRangeInternal(const T &start_inclusive, const T &end_exclusive)
Definition: unique_resource_allocator.h:263
std::set< T > available_resources_
Definition: unique_resource_allocator.h:291
std::optional< Reservation > UniqueItem()
Definition: unique_resource_allocator.h:141
void Reclaim(const T &t)
Definition: unique_resource_allocator.h:239
std::vector< T > ExpandPool(const T &t)
Definition: unique_resource_allocator.h:135
UniqueResourceAllocator(const Container &items)
Definition: unique_resource_allocator.h:230
typename std::remove_cv_t< typename std::remove_reference_t< U > > RemoveCvref
Definition: unique_resource_allocator.h:44
std::optional< Reservation > Take(const T &t)
Definition: unique_resource_allocator.h:188
std::unordered_set< Reservation, ReservationHash > ReservationSet
Definition: unique_resource_allocator.h:96
static std::unique_ptr< UniqueResourceAllocator > New(const std::vector< T > &pool)
Definition: unique_resource_allocator.h:107
std::optional< ReservationSet > UniqueItems(const int n)
Definition: unique_resource_allocator.h:152
bool operator==(const UniqueResourceAllocator &other) const
Definition: unique_resource_allocator.h:233
std::enable_if_t< std::is_integral< V >::value, std::optional< ReservationSet > > UniqueConsecutiveItems(const std::size_t n)
Definition: unique_resource_allocator.h:167
std::mutex mutex_
Definition: unique_resource_allocator.h:293
std::vector< T > ExpandPool(T &&t)
Definition: unique_resource_allocator.h:129
#define LOG(severity)
Definition: logging.h:223
@ ERROR
Definition: logging.h:92
std::multimap< int, std::vector< uint8_t > > pool
Definition: cvd_video_frame_buffer.cpp:34
Definition: alloc_utils.cpp:23
constexpr bool Contains(Container &&container, U &&u)
Definition: contains.h:98
Definition: logging.h:464
Definition: unique_resource_allocator.h:91
std::size_t operator()(const Reservation &resource_wrapper) const
Definition: unique_resource_allocator.h:92