obs-streamFX/source/util/util-pool.hpp

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