209 lines
5.7 KiB
C++
209 lines
5.7 KiB
C++
// AUTOGENERATED COPYRIGHT HEADER START
|
|
// Copyright (C) 2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
|
|
|
#pragma once
|
|
#include "util/util-singleton.hpp"
|
|
|
|
#include "warning-disable.hpp"
|
|
#include <functional>
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include "warning-enable.hpp"
|
|
|
|
namespace streamfx::util {
|
|
class poolbase {
|
|
public:
|
|
virtual ~poolbase(){};
|
|
|
|
protected:
|
|
virtual void reset(void* ptr){};
|
|
};
|
|
|
|
/** Simple pool for objects of a single type.
|
|
*
|
|
*/
|
|
template<class _self, class _type, uint64_t _lifetime>
|
|
class pool : public poolbase, public streamfx::util::singleton<_self> {
|
|
typedef std::chrono::high_resolution_clock::time_point _time;
|
|
typedef _type* _ptr;
|
|
typedef std::shared_ptr<_type> _tracked;
|
|
|
|
friend _tracked;
|
|
friend streamfx::util::singleton<_self>;
|
|
|
|
std::list<std::pair<_time, _ptr>> _pool;
|
|
std::mutex _lock;
|
|
|
|
public:
|
|
~pool()
|
|
{
|
|
std::unique_lock<decltype(_lock)> lock(_lock);
|
|
for (auto kv : _pool) {
|
|
delete kv.second;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
pool(void) {}
|
|
|
|
void release(_ptr ptr)
|
|
{
|
|
std::unique_lock<decltype(_lock)> lock(_lock);
|
|
|
|
_time now = std::chrono::high_resolution_clock::now();
|
|
|
|
// Insert the released object into the pool.
|
|
_pool.emplace_front(std::pair<_time, _ptr>{now, ptr});
|
|
reset(ptr);
|
|
|
|
// Clean up any stragglers that exceeded their time limit.
|
|
for (auto pit = _pool.begin(); pit != _pool.end();) {
|
|
if ((pit->first - now) >= std::chrono::milliseconds(_lifetime)) {
|
|
delete pit->second; // Internal object is untracked.
|
|
pit = _pool.erase(pit);
|
|
} else {
|
|
pit++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<class... _valuetypes>
|
|
_tracked acquire(_valuetypes&&... _values)
|
|
{
|
|
std::unique_lock<decltype(_lock)> lock(_lock);
|
|
|
|
// Try and find a free object.
|
|
_ptr ptr = nullptr;
|
|
if (_pool.size() > 0) {
|
|
ptr = _pool.front().second;
|
|
_pool.pop_front();
|
|
}
|
|
|
|
// If there was no usable object, create a new one.
|
|
if (!ptr) {
|
|
ptr = new _type(std::forward<_valuetypes>(_values)...);
|
|
}
|
|
|
|
// Generate a shared pointer which automatically releases the object back to the pool after the end of use.
|
|
auto self = this->shared_from_this();
|
|
return std::shared_ptr<_type>(ptr, [self](_type* v) { self->release(v); });
|
|
}
|
|
};
|
|
|
|
/** Generic template for resource pooling with lifetimes.
|
|
*
|
|
* Declaration / Definition:
|
|
* class example {
|
|
* ...
|
|
* public:
|
|
* class pool;
|
|
* typedef example_type_t _pool_key_t;
|
|
* typedef streamfx::util::pool<example::pool, example, 1000, _pool_key_t> _pool_t;
|
|
* class pool : public _pool_t {
|
|
* friend streamfx::util::singleton<example::pool>;
|
|
* protected:
|
|
* pool() : _pool_t() {}
|
|
*
|
|
* static _pool_key_t as_key(example*) {...}
|
|
* static _pool_key_t as_key(example_type_t) {...}
|
|
* };
|
|
* };
|
|
*
|
|
* Usage:
|
|
* auto inst = example::pool::instance()->acquire(...);
|
|
*
|
|
* The returned pointer will keep a reference to the pool, and automatically release back into it when needed.
|
|
*/
|
|
template<class _self, class _type, uint64_t _lifetime, typename _key>
|
|
class multipool : public poolbase, public streamfx::util::singleton<_self> {
|
|
typedef std::chrono::high_resolution_clock::time_point _time;
|
|
typedef _type* _ptr;
|
|
typedef std::shared_ptr<_type> _tracked;
|
|
|
|
friend _tracked;
|
|
friend streamfx::util::singleton<_self>;
|
|
|
|
std::map<_key, std::list<std::pair<_time, _ptr>>> _pool;
|
|
std::mutex _lock;
|
|
|
|
public:
|
|
~multipool()
|
|
{
|
|
std::unique_lock<decltype(_lock)> lock(_lock);
|
|
for (auto kv : _pool) {
|
|
for (auto lv : kv.second) {
|
|
delete lv.second;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
multipool(void) : _pool(), _lock() {}
|
|
|
|
void release(_ptr ptr)
|
|
{
|
|
std::unique_lock<decltype(_lock)> lock(_lock);
|
|
|
|
_time now = std::chrono::high_resolution_clock::now();
|
|
|
|
// Re-insert the released object into the pool(s).
|
|
auto hash = _self::as_key(ptr);
|
|
if (auto kv = _pool.find(hash); kv != _pool.end()) {
|
|
kv->second.emplace_front(now, ptr);
|
|
} else {
|
|
_pool.emplace(hash, std::list<std::pair<_time, _ptr>>{{now, ptr}});
|
|
}
|
|
reset(ptr);
|
|
|
|
// Clean up any stragglers that exceeded their time limit.
|
|
for (auto pit = _pool.begin(); pit != _pool.end();) {
|
|
for (auto lit = pit->second.begin(); lit != pit->second.end();) {
|
|
if ((lit->first - now) >= std::chrono::milliseconds(_lifetime)) {
|
|
delete lit->second; // Internal object is untracked.
|
|
lit = pit->second.erase(lit);
|
|
} else {
|
|
lit++;
|
|
}
|
|
}
|
|
|
|
// Delete any empty lists from memory.
|
|
if (pit->second.empty()) {
|
|
pit = _pool.erase(pit);
|
|
} else {
|
|
pit++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
template<class... _valuetypes>
|
|
_tracked acquire(_valuetypes&&... _values)
|
|
{
|
|
std::unique_lock<decltype(_lock)> lock(_lock);
|
|
|
|
// Try and find an existing pool, and a free object.
|
|
_ptr ptr = nullptr;
|
|
auto hash = _self::as_key(std::forward<_valuetypes>(_values)...);
|
|
if (auto kv = _pool.find(hash); kv != _pool.cend()) {
|
|
if (kv->second.size() > 0) {
|
|
ptr = kv->second.front().second;
|
|
kv->second.pop_front();
|
|
}
|
|
}
|
|
|
|
// If there was no usable object, create a new one.
|
|
if (!ptr) {
|
|
ptr = new _type(std::forward<_valuetypes>(_values)...);
|
|
}
|
|
|
|
// Generate a shared pointer which automatically releases the object back to the pool after the end of use.
|
|
auto self = this->shared_from_this();
|
|
return std::shared_ptr<_type>(ptr, [self](_type* v) { self->release(v); });
|
|
}
|
|
};
|
|
} // namespace streamfx::util
|