ClickHouse/base/common/BorrowedObjectPool.h

157 lines
5.1 KiB
C++
Raw Normal View History

2021-03-04 12:04:25 +00:00
#pragma once
2021-03-03 22:52:31 +00:00
#include <cstdint>
#include <vector>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <common/defines.h>
2021-04-12 20:36:13 +00:00
#include <common/MoveOrCopyIfThrow.h>
2021-03-03 22:52:31 +00:00
2021-03-04 11:58:36 +00:00
/** Pool for limited size objects that cannot be used from different threads simultaneously.
* The main use case is to have fixed size of objects that can be reused in difference threads during their lifetime
* and have to be initialized on demand.
2021-03-05 10:27:24 +00:00
* Two main properties of pool are allocated objects size and borrowed objects size.
2021-03-04 11:58:36 +00:00
* Allocated objects size is size of objects that are currently allocated by the pool.
2021-03-06 18:45:51 +00:00
* Borrowed objects size is size of objects that are borrowed by clients.
2021-03-04 11:58:36 +00:00
* If max_size == 0 then pool has unlimited size and objects will be allocated without limit.
*
* Pool provides following strategy for borrowing object:
* If max_size == 0 then pool has unlimited size and objects will be allocated without limit.
2021-03-06 18:45:51 +00:00
* 1. If pool has objects that can be borrowed increase borrowed objects size and return it.
2021-03-04 11:58:36 +00:00
* 2. If pool allocatedObjectsSize is lower than max objects size or pool has unlimited size
* allocate new object, increase borrowed objects size and return it.
* 3. If pool is full wait on condition variable with or without timeout until some object
* will be returned to the pool.
*/
2021-03-03 22:52:31 +00:00
template <typename T>
class BorrowedObjectPool final
{
public:
explicit BorrowedObjectPool(size_t max_size_) : max_size(max_size_) {}
2021-03-04 11:58:36 +00:00
/// Borrow object from pool. If pull is full and all objects were borrowed
/// then calling thread will wait until some object will be returned into pool.
2021-03-03 22:52:31 +00:00
template <typename FactoryFunc>
2021-03-04 11:58:36 +00:00
void borrowObject(T & dest, FactoryFunc && func)
2021-03-03 22:52:31 +00:00
{
2021-03-04 11:58:36 +00:00
std::unique_lock<std::mutex> lock(objects_mutex);
if (!objects.empty())
{
2021-03-06 18:45:51 +00:00
dest = borrowFromObjects(lock);
2021-03-04 11:58:36 +00:00
return;
}
bool has_unlimited_size = (max_size == 0);
if (unlikely(has_unlimited_size) || allocated_objects_size < max_size)
{
2021-03-06 18:45:51 +00:00
dest = allocateObjectForBorrowing(lock, std::forward<FactoryFunc>(func));
2021-03-04 11:58:36 +00:00
return;
}
condition_variable.wait(lock, [this] { return !objects.empty(); });
2021-03-06 18:45:51 +00:00
dest = borrowFromObjects(lock);
2021-03-03 22:52:31 +00:00
}
2021-03-04 11:58:36 +00:00
/// Same as borrowObject function, but wait with timeout.
/// Returns true if object was borrowed during timeout.
2021-03-03 22:52:31 +00:00
template <typename FactoryFunc>
2021-03-04 11:58:36 +00:00
bool tryBorrowObject(T & dest, FactoryFunc && func, size_t timeout_in_milliseconds = 0)
2021-03-03 22:52:31 +00:00
{
2021-03-04 11:58:36 +00:00
std::unique_lock<std::mutex> lock(objects_mutex);
if (!objects.empty())
{
2021-03-06 18:45:51 +00:00
dest = borrowFromObjects(lock);
2021-03-04 11:58:36 +00:00
return true;
}
bool has_unlimited_size = (max_size == 0);
if (unlikely(has_unlimited_size) || allocated_objects_size < max_size)
{
2021-03-06 18:45:51 +00:00
dest = allocateObjectForBorrowing(lock, std::forward<FactoryFunc>(func));
2021-03-04 11:58:36 +00:00
return true;
}
bool wait_result = condition_variable.wait_for(lock, std::chrono::milliseconds(timeout_in_milliseconds), [this] { return !objects.empty(); });
if (wait_result)
2021-03-06 18:45:51 +00:00
dest = borrowFromObjects(lock);
2021-03-04 11:58:36 +00:00
return wait_result;
2021-03-03 22:52:31 +00:00
}
2021-03-04 11:58:36 +00:00
/// Return object into pool. Client must return same object that was borrowed.
2021-03-06 18:45:51 +00:00
inline void returnObject(T && object_to_return)
2021-03-03 22:52:31 +00:00
{
std::unique_lock<std::mutex> lck(objects_mutex);
objects.emplace_back(std::move(object_to_return));
--borrowed_objects_size;
condition_variable.notify_one();
}
2021-03-04 11:58:36 +00:00
/// Max pool size
2021-03-06 18:45:51 +00:00
inline size_t maxSize() const
2021-03-04 11:58:36 +00:00
{
return max_size;
}
/// Allocated objects size by the pool. If allocatedObjectsSize == maxSize then pool is full.
2021-03-06 18:45:51 +00:00
inline size_t allocatedObjectsSize() const
2021-03-03 22:52:31 +00:00
{
std::unique_lock<std::mutex> lock(objects_mutex);
return allocated_objects_size;
}
2021-03-04 11:58:36 +00:00
/// Returns allocatedObjectsSize == maxSize
2021-03-06 18:45:51 +00:00
inline bool isFull() const
2021-03-03 22:52:31 +00:00
{
std::unique_lock<std::mutex> lock(objects_mutex);
2021-03-04 11:58:36 +00:00
return allocated_objects_size == max_size;
2021-03-03 22:52:31 +00:00
}
2021-03-04 11:58:36 +00:00
/// Borrowed objects size. If borrowedObjectsSize == allocatedObjectsSize and pool is full.
/// Then client will wait during borrowObject function call.
2021-03-06 18:45:51 +00:00
inline size_t borrowedObjectsSize() const
2021-03-03 22:52:31 +00:00
{
std::unique_lock<std::mutex> lock(objects_mutex);
2021-03-04 11:58:36 +00:00
return borrowed_objects_size;
2021-03-03 22:52:31 +00:00
}
2021-03-04 11:58:36 +00:00
private:
2021-03-03 22:52:31 +00:00
template <typename FactoryFunc>
2021-03-06 18:45:51 +00:00
inline T allocateObjectForBorrowing(const std::unique_lock<std::mutex> &, FactoryFunc && func)
2021-03-03 22:52:31 +00:00
{
++allocated_objects_size;
++borrowed_objects_size;
return std::forward<FactoryFunc>(func)();
}
2021-03-06 18:45:51 +00:00
inline T borrowFromObjects(const std::unique_lock<std::mutex> &)
2021-03-03 22:52:31 +00:00
{
T dst;
detail::moveOrCopyIfThrow(std::move(objects.back()), dst);
objects.pop_back();
++borrowed_objects_size;
return dst;
}
size_t max_size;
mutable std::mutex objects_mutex;
std::condition_variable condition_variable;
size_t allocated_objects_size = 0;
size_t borrowed_objects_size = 0;
std::vector<T> objects;
};