asynchrony 2.3.1
Add asynchrony to your C++ applications using standard C++20
Loading...
Searching...
No Matches
resource_pool.hpp
1/*
2 asynchrony : Add asynchrony to your apps
3
4 BSD 3-Clause License
5
6 Copyright (c) 2021, Siddiq Software LLC
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11
12 1. Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
14
15 2. Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
18
19 3. Neither the name of the copyright holder nor the names of its
20 contributors may be used to endorse or promote products derived from
21 this software without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#pragma once
36#include <cstdint>
37#ifndef RESOURCE_POOL_HPP
38#define RESOURCE_POOL_HPP
39
40#include <stdexcept>
41#include <mutex>
42#include <shared_mutex>
43#include <deque>
44#include <format>
45
46#include "siddiqsoft/RunOnEnd.hpp"
47
48namespace siddiqsoft
49{
103 template <typename T>
104 requires std::move_constructible<T>
106 {
107 protected:
109 T rsrc {};
110
112 uint64_t debugId {static_cast<uint64_t>(std::rand())};
113
116 std::function<void(T&&)> putbackCallback {};
117
122 bool isValid {false};
123
124 public:
127 resource_wrap() = delete;
128
141 resource_wrap(T&& src, std::function<void(T&&)>&& f = {})
142 : rsrc(std::move(src))
143 , putbackCallback(std::move(f))
144 , isValid(true)
145 {
146#if defined(DEBUG)
147 if constexpr (std::is_pointer_v<T>) {
148 std::cerr << std::format(" - resource_wrap: debugId:{} {:p}\n", debugId, static_cast<void*>(rsrc));
149 }
150 else if constexpr (std::is_integral_v<T>) {
151 std::cerr << std::format(" - resource_wrap: debugId:{} {}\n", debugId, rsrc);
152 }
153 else {
154 std::cerr << std::format(" - resource_wrap: debugId:{}\n", debugId);
155 }
156#endif
157 }
158
161 resource_wrap(const T&) = delete;
162
174 {
175#if defined(DEBUG)
176 std::cerr << std::format(" - resource_wrap: move into debugId:{}\n", debugId);
177#endif
178 rsrc = std::move(src);
179 isValid = true;
180 return *this;
181 };
182
185
197 auto operator*() -> T& { return rsrc; }
198
208 operator T() { return rsrc; }
209
223 {
224#if defined(DEBUG)
225 std::cerr << std::format(" - ~resource_wrap: putback debugId:{} isValid:{}\n", debugId, isValid);
226#endif
227 // Only return resource if it's valid and callback exists
228 // This prevents returning uninitialized or moved-out resources to the pool
229 if (isValid && putbackCallback) {
230 putbackCallback(std::move(rsrc));
231 isValid = false;
232 }
233 }
234
259#if defined(DEBUG)
260 void invalidate() { isValid = false; }
261#endif
262 };
263
337 template <typename T, uint16_t InitCapacity = sizeof(uint8_t)>
338 requires((InitCapacity <= sizeof(uint16_t))) && std::move_constructible<T>
340 {
341 private:
344 std::deque<T> _pool {};
345
348 std::mutex _poolLock {};
349
350 public:
353 resource_pool() = default;
354
358
361 resource_pool(resource_pool&& src) = default;
362
365
368
372
383 void clear()
384 {
385 std::scoped_lock<std::mutex> l(_poolLock);
386 _pool.clear();
387 }
388
402 auto size()
403 {
404 std::scoped_lock<std::mutex> l(_poolLock);
405 return _pool.size();
406 }
407
445 [[nodiscard]] auto checkout() -> resource_wrap<T> /* throw() */
446 {
447 {
448 std::scoped_lock<std::mutex> l(_poolLock);
449
450 if (!_pool.empty()) {
451 RunOnEnd roe([&]() { _pool.pop_front(); });
452
457 auto autoReturnResource = [this](T&& rsrc) {
458 this->checkin(std::move(rsrc));
459 };
460
461 return {std::move(_pool.front()), autoReturnResource};
462 // The pop_front() happens within this scope and
463 // within the lock!
464 }
465 } // scope end
466
467 throw std::runtime_error("Empty pool; add something first!");
468 }
469
508 void checkin(T&& rsrc)
509 {
510 std::scoped_lock<std::mutex> l(_poolLock);
511 _pool.push_back(std::move(rsrc));
512 }
513 };
514} // namespace siddiqsoft
515#endif
auto size()
Get the current size of the pool.
resource_pool(resource_pool &&src)=default
Move constructor (defaulted) Allows moving a pool to a new location.
auto checkout() -> resource_wrap< T >
resource_pool & operator=(resource_pool &)=delete
Copy assignment operator (deleted - pools are not copyable).
resource_pool & operator=(resource_pool &&src)=default
Move assignment operator (defaulted).
resource_pool()=default
Default constructor Creates an empty pool ready to accept resources.
void clear()
Clear all items from the pool.
resource_pool(resource_pool &)=delete
Copy constructor (deleted - pools are not copyable) Each pool manages its own resources independently...
~resource_pool()
Destructor - clears all resources from the pool All remaining resources are destroyed.
uint64_t debugId
Debug identifier for tracking (used in DEBUG builds).
resource_wrap()=delete
Default constructor is deleted Resources must be explicitly constructed with a valid resource.
~resource_wrap()
Destructor - automatically returns resource to pool if valid.
resource_wrap & operator=(T &&src)
Move assignment operator.
bool isValid
Tracks whether the resource is valid and should be returned to pool Prevents returning uninitialized ...
resource_wrap & operator=(const resource_wrap &)=delete
Copy assignment is deleted.
resource_wrap(T &&src, std::function< void(T &&)> &&f={})
Construct a resource_wrap with a resource and optional callback.
resource_wrap(const T &)=delete
Copy constructor is deleted Resources are move-only to maintain clear ownership semantics.
std::function< void(T &&)> putbackCallback
Callback function to return the resource to the pool Called by destructor when resource is valid.
T rsrc
The actual resource being wrapped.