IOCP에서 Hardclose되는 경우 발생하는 error
IOCP나 Overlapped IO같은 경우 OVERLAPPED structure가 비동기 작업이 완료될 때 까지 객체가 유지되야 함
또한 너무 많은 Object가 동적으로 할당, 삭제를 반복할 경우 memory 유실이나 fragmentation의 문제가 발생할 수 있음
이를 방지하기 위해 사용이 끝난 memory를 재사용하는 기법
template<typename T>
class TObjectPool
{
public:
enum
{
POOL_MAX_SIZE = 4096, // 2^n
POOL_SIZE_MASK = POOL_MAX_SIZE - 1,
};
private:
static void* m_mPool[POOL_MAX_SIZE];
static long long m_HeadPos;
static long long m_TailPos;
static void Allocation()
{
for (size_t i = 0; i < POOL_MAX_SIZE; i++)
{
// m_mPool[i] % MEMORY_ALLOCATION_ALIGNMENT = 0
m_mPool[i] = _aligned_malloc(sizeof(T), MEMORY_ALLOCATION_ALIGNMENT);
}
InterlockedAdd64(&m_TailPos, POOL_MAX_SIZE);
}
static void Release()
{
for (size_t i = 0; i < POOL_MAX_SIZE; i++)
{
// _aligned_malloc을 통해 할당된 메모리는 _aligned_free를 통해 해제해야 함
_aligned_free(m_mPool[i]);
}
}
// POOL_MAX_SIZE가 2^n이 되야하는 이유
// POOL_SIZE_MASK는 입력값을 적절하게 순환하게 만들어주는 역할
//POOL_SIZE_MASK = 0111
//m_HeadPos = 0, 0000 & 0111 = 0
//m_HeadPos = 1, 0001 & 0111 = 1
//m_HeadPos = 2, 0010 & 0111 = 2
//m_HeadPos = 3, 0011 & 0111 = 3
//m_HeadPos = 4, 0100 & 0111 = 4
//m_HeadPos = 5, 0101 & 0111 = 5
//m_HeadPos = 6, 0110 & 0111 = 6
//m_HeadPos = 7, 0111 & 0111 = 7
//m_HeadPos = 8, 1000 & 0111 = 0
//m_HeadPos = 9, 1001 & 0111 = 1
//m_HeadPos = 10, 1010 & 0111 = 2
// ...
static void* operator new (size_t size)
{
size_t pos = InterlockedIncrement64(&m_HeadPos)-1;
size_t realpos = pos & POOL_SIZE_MASK;
// 값 교환 atomic 함수
void* ret = InterlockedExchangePointer(&m_mPool[realpos], nullptr);
if (ret != nullptr)
{
return ret;
}
return _aligned_malloc(sizeof(T), MEMORY_ALLOCATION_ALIGNMENT);
}
static void operator delete (void* obj)
{
size_t pos = InterlockedIncrement64(&m_TailPos) - 1;
size_t realpos = pos & POOL_SIZE_MASK;
void* ret = InterlockedExchangePointer(&m_mPool[realpos], obj);
if (ret != nullptr)
{
_aligned_free(ret);
}
}
};
메모리 할당 시 주소값이 지정된 크기로 나누어 떨어지도록 작업
Load/Store가 더 빠름
직접 개발 시
virtual function을 갖는 class들은 vfptr을 추가로 가지므로 Object Pool과 같이 배열로 메모리 공간을 재사용 할 수 없음
여기서는 Queue를 사용하여 구현
template<typename T>
class TClassPool
{
public:
TClassPool(size_t chunkSize = DefalultChunkSize);
shared_ptr<T> NewChunk();
void DeleteChunk(shared_ptr<T> obj);
protected:
size_t m_ChunkSize;
static const size_t DefalultChunkSize = 10;
std::queue<shared_ptr<T>> m_List;
};
template<typename T>
shared_ptr<T> TClassPool<T>::NewChunk()
{
if (m_List.empty())
{
m_List.push(std::make_shared<T>());
m_ChunkSize++;
}
auto chunk = m_List.front();
m_List.pop();
return chunk;
}
template<typename T>
void TClassPool<T>::DeleteChunk(shared_ptr<T> obj)
{
m_List.push(obj);
}