// AUTOGENERATED COPYRIGHT HEADER START // Copyright (C) 2023 Michael Fabian 'Xaymar' Dirks // AUTOGENERATED COPYRIGHT HEADER END #pragma once #include "util/util-singleton.hpp" #include "warning-disable.hpp" #include #include #include #include #include #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 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> _pool; std::mutex _lock; public: ~pool() { std::unique_lock lock(_lock); for (auto kv : _pool) { delete kv.second; } } protected: pool(void) {} void release(_ptr ptr) { std::unique_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 _tracked acquire(_valuetypes&&... _values) { std::unique_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 _pool_t; * class pool : public _pool_t { * friend streamfx::util::singleton; * 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 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>> _pool; std::mutex _lock; public: ~multipool() { std::unique_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 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>{{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 _tracked acquire(_valuetypes&&... _values) { std::unique_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