Files
FlaxEngine/Source/Engine/Core/Collections/CollectionPoolCache.h
2021-09-21 18:23:31 +02:00

155 lines
3.6 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "../Collections/Array.h"
#include "Engine/Platform/CriticalSection.h"
namespace CollectionPoolCacheUtils
{
/// <summary>
/// Clear callback used to initialize the given collection container type (clear array, etc.). Called when pool item is being reused or initialized.
/// </summary>
template<typename T>
using ClearCallback = void(*)(T*);
/// <summary>
/// Create callback spawns a new entry of the pooled collection
/// </summary>
template<typename T>
using CreateCallback = T * (*)();
template<typename T>
inline void DefaultClearCallback(T* obj)
{
obj->Clear();
}
template<typename T>
inline T* DefaultCreateCallback()
{
return New<T>();
}
}
/// <summary>
/// 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.
/// </summary>
template<typename T, CollectionPoolCacheUtils::ClearCallback<T> ClearCallback = CollectionPoolCacheUtils::DefaultClearCallback<T>, CollectionPoolCacheUtils::CreateCallback<T> CreateCallback = CollectionPoolCacheUtils::DefaultCreateCallback<T>>
class CollectionPoolCache
{
public:
/// <summary>
/// Helper object used to access the pooled collection and return it to the pool after usage (on code scope execution end).
/// </summary>
struct ScopeCache
{
friend CollectionPoolCache;
private:
CollectionPoolCache* _pool;
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->Release(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<T*, InlinedAllocation<64>> _pool;
void Release(T* value)
{
_locker.Lock();
_pool.Add(value);
_locker.Unlock();
}
public:
/// <summary>
/// Finalizes an instance of the <see cref="CollectionPoolCache"/> class.
/// </summary>
~CollectionPoolCache()
{
_pool.ClearDelete();
}
public:
/// <summary>
/// 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.
/// </summary>
/// <returns>The collection (cleared).</returns>
ScopeCache Get()
{
T* result;
_locker.Lock();
if (_pool.HasItems())
result = _pool.Pop();
else
result = CreateCallback();
_locker.Unlock();
ClearCallback(result);
return ScopeCache(this, result);
}
/// <summary>
/// Releases all the allocated resources (existing in the pool that are not during usage).
/// </summary>
void Release()
{
_locker.Lock();
_pool.ClearDelete();
_pool.Resize(0);
_locker.Unlock();
}
};