11#include <condition_variable>
21template <
class Resource>
class ResourcePool;
22template <
class Resource>
class ResourceLease;
28template <
class Resource>
class ResourceWaiter {
30 using notify_type = void (*)(ResourceWaiter&, ResourceLease<Resource>)
noexcept;
32 explicit ResourceWaiter(notify_type notify) noexcept : notify_(notify) {
CHECK(notify_ !=
nullptr); }
33 ResourceWaiter(ResourceWaiter
const&) =
delete;
34 ResourceWaiter& operator=(ResourceWaiter
const&) =
delete;
37 void notify(ResourceLease<Resource> lease)
noexcept { notify_(*
this, std::move(lease)); }
40 ResourceWaiter* next_ =
nullptr;
43 friend class ResourcePool<Resource>;
58 : pool_(
std::exchange(other.pool_,
nullptr)), slot_(
std::exchange(other.slot_, 0))
66 pool_ = std::exchange(other.pool_,
nullptr);
67 slot_ = std::exchange(other.slot_, 0);
75 [[nodiscard]]
explicit operator bool() const noexcept {
return pool_ !=
nullptr; }
78 [[nodiscard]] Resource&
get()
const;
87 std::size_t slot_ = 0;
104 : resources_(
std::move(resources)), idle_(resources_.
size(), true), idle_count_(resources_.
size())
106 CHECK(!resources_.empty(),
"resource pool requires at least one slot");
116 std::lock_guard lock(mutex_);
117 CHECK(waiter_head_ ==
nullptr,
"destroying resource pool with queued waiters");
118 CHECK(idle_count_ == resources_.size(),
"destroying resource pool with active leases", idle_count_,
125 std::lock_guard lock(mutex_);
126 auto const slot = this->take_idle_slot_locked();
127 if (!slot)
return std::nullopt;
134 std::unique_lock lock(mutex_);
135 available_.wait(lock, [
this] {
return idle_count_ != 0; });
136 auto const slot = this->take_idle_slot_locked();
137 CHECK(slot.has_value());
142 [[nodiscard]] std::size_t
size() const noexcept {
return resources_.size(); }
147 std::lock_guard lock(mutex_);
154 std::lock_guard lock(mutex_);
155 CHECK(!waiter.queued_);
156 if (
auto const slot = this->take_idle_slot_locked())
161 waiter.queued_ =
true;
162 waiter.next_ =
nullptr;
163 if (waiter_tail_ ==
nullptr)
165 waiter_head_ = &waiter;
169 waiter_tail_->next_ = &waiter;
171 waiter_tail_ = &waiter;
176 [[nodiscard]] std::optional<std::size_t> take_idle_slot_locked() noexcept
178 if (idle_count_ == 0)
return std::nullopt;
179 for (std::size_t slot = 0; slot < idle_.size(); ++slot)
188 PANIC(
"resource pool idle count is inconsistent", idle_count_, idle_.size());
191 [[nodiscard]] Resource& resource(std::size_t slot)
193 CHECK(slot < resources_.size(), slot, resources_.size());
194 return resources_[slot];
197 void release(std::size_t slot)
noexcept
201 std::lock_guard lock(mutex_);
202 CHECK(slot < resources_.size(), slot, resources_.size());
203 CHECK(!idle_[slot],
"resource pool slot was released twice", slot);
205 if (waiter_head_ ==
nullptr)
209 available_.notify_one();
213 waiter = waiter_head_;
214 waiter_head_ = waiter->next_;
215 if (waiter_head_ ==
nullptr) waiter_tail_ =
nullptr;
216 waiter->next_ =
nullptr;
217 waiter->queued_ =
false;
223 mutable std::mutex mutex_;
224 std::condition_variable available_;
225 std::vector<Resource> resources_;
226 std::vector<bool> idle_;
227 std::size_t idle_count_;
236 CHECK(pool_ !=
nullptr,
"cannot access an empty resource lease");
237 return pool_->resource(slot_);
242 if (pool_ ==
nullptr)
return;
243 auto* pool = std::exchange(pool_,
nullptr);
244 auto const slot = std::exchange(slot_, 0);
Move-only exclusive lease of one provider-resource pool slot.
Definition resource_pool.hpp:51
ResourceLease & operator=(ResourceLease &&other) noexcept
Definition resource_pool.hpp:61
void release() noexcept
Return the resource to its pool before this object's destruction.
Definition resource_pool.hpp:240
Resource & get() const
Access the exclusively leased provider resource.
Definition resource_pool.hpp:234
~ResourceLease()
Definition resource_pool.hpp:72
ResourceLease() noexcept=default
Fixed-capacity pool of preconstructed provider resources.
Definition resource_pool.hpp:96
ResourcePool & operator=(ResourcePool &&)=delete
std::optional< lease_type > try_acquire()
Try to acquire one idle resource without waiting.
Definition resource_pool.hpp:123
ResourceLease< Resource > lease_type
Definition resource_pool.hpp:99
~ResourcePool()
Definition resource_pool.hpp:114
ResourcePool(ResourcePool const &)=delete
ResourcePool(std::vector< Resource > resources)
Take ownership of the resources that form this pool's slots.
Definition resource_pool.hpp:103
ResourcePool & operator=(ResourcePool const &)=delete
Resource resource_type
Definition resource_pool.hpp:98
detail::ResourceWaiter< Resource > waiter_type
Definition resource_pool.hpp:100
ResourcePool(ResourcePool &&)=delete
std::optional< lease_type > acquire_or_enqueue(waiter_type &waiter) noexcept
Register an internal awaiter unless a resource is immediately available.
Definition resource_pool.hpp:152
std::size_t idle_count() const noexcept
Return the number of resources currently available for acquisition.
Definition resource_pool.hpp:145
lease_type acquire()
Block until one resource can be leased.
Definition resource_pool.hpp:132
std::size_t size() const noexcept
Return the fixed number of resource slots.
Definition resource_pool.hpp:142
Definition cublas/execution.hpp:18
#define CHECK(cond,...)
Definition trace.hpp:299
#define PANIC(...)
Definition trace.hpp:382