// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "../Collections/Array.h"
#include "Engine/Platform/CriticalSection.h"
namespace CollectionPoolCacheUtils
{
///
/// Clear callback used to initialize the given collection container type (clear array, etc.). Called when pool item is being reused or initialized.
///
template
using ClearCallback = void(*)(T*);
///
/// Create callback spawns a new entry of the pooled collection
///
template
using CreateCallback = T * (*)();
template
inline void DefaultClearCallback(T* obj)
{
obj->Clear();
}
template
inline T* DefaultCreateCallback()
{
return New();
}
}
///
/// Cache container that holds a list of cached collections to allow reuse and reduced memory allocation amount. Helps with sharing data across code and usages. It's thread-safe.
///
template ClearCallback = CollectionPoolCacheUtils::DefaultClearCallback, CollectionPoolCacheUtils::CreateCallback CreateCallback = CollectionPoolCacheUtils::DefaultCreateCallback>
class CollectionPoolCache
{
public:
///
/// Helper object used to access the pooled collection and return it to the pool after usage (on code scope execution end).
///
struct ScopeCache
{
friend CollectionPoolCache;
private:
CollectionPoolCache* _pool;
FORCE_INLINE ScopeCache(CollectionPoolCache* pool, T* value)
{
_pool = pool;
Value = value;
}
public:
T* Value;
ScopeCache() = delete;
ScopeCache(const ScopeCache& other) = delete;
ScopeCache& operator=(const ScopeCache& other) = delete;
ScopeCache& operator=(ScopeCache&& other) noexcept = delete;
ScopeCache(ScopeCache&& other) noexcept
{
Value = other.Value;
other.Value = nullptr;
}
~ScopeCache()
{
_pool->Put(Value);
}
T* operator->()
{
return Value;
}
const T* operator->() const
{
return Value;
}
T& operator*()
{
return *Value;
}
const T& operator*() const
{
return *Value;
}
};
private:
CriticalSection _locker;
Array> _pool;
public:
///
/// Finalizes an instance of the class.
///
~CollectionPoolCache()
{
_pool.ClearDelete();
}
public:
///
/// Gets the collection instance from the pool. Can reuse the object from the pool or create a new one. Returns collection is always cleared and ready to use.
///
/// The collection (cleared).
FORCE_INLINE ScopeCache Get()
{
return ScopeCache(this, GetUnscoped());
}
///
/// Gets the collection instance from the pool. Can reuse the object from the pool or create a new one. Returns collection is always cleared and ready to use.
///
/// The collection (cleared).
T* GetUnscoped()
{
T* result;
_locker.Lock();
if (_pool.HasItems())
result = _pool.Pop();
else
result = CreateCallback();
_locker.Unlock();
ClearCallback(result);
return result;
}
///
/// Puts the collection value back to the pool.
///
void Put(T* value)
{
_locker.Lock();
_pool.Add(value);
_locker.Unlock();
}
///
/// Releases all the allocated resources (existing in the pool that are not during usage).
///
void Release()
{
_locker.Lock();
_pool.ClearDelete();
_pool.Resize(0);
_locker.Unlock();
}
};