libnative-utilities/include/raknet/DS_MemoryPool.hpp
2024-08-15 18:40:30 +08:00

357 lines
9.6 KiB
C++

/*
* Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
/// \file DS_MemoryPool.h
///
#ifndef __MEMORY_POOL_H
#define __MEMORY_POOL_H
#ifndef __APPLE__
// Use stdlib and not malloc for compatibility
#include <stdlib.h>
#endif
#include "RakAssert.hpp"
#include "Export.hpp"
#include "RakMemoryOverride.hpp"
// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1
#define DS_MEMORY_POOL_MAX_FREE_PAGES 4
//#define _DISABLE_MEMORY_POOL
namespace DataStructures
{
/// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors.
/// Contains a list of pages, each of which has an array of the user structures
template <class MemoryBlockType>
class RAK_DLL_EXPORT MemoryPool
{
public:
struct Page;
struct MemoryWithPage
{
MemoryBlockType userMemory;
Page *parentPage;
};
struct Page
{
MemoryWithPage** availableStack;
int availableStackSize;
MemoryWithPage* block;
Page *next, *prev;
};
MemoryPool();
~MemoryPool();
void SetPageSize(int size); // Defaults to 16384 bytes
MemoryBlockType *Allocate(const char *file, unsigned int line);
void Release(MemoryBlockType *m, const char *file, unsigned int line);
void Clear(const char *file, unsigned int line);
int GetAvailablePagesSize(void) const {return availablePagesSize;}
int GetUnavailablePagesSize(void) const {return unavailablePagesSize;}
int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;}
protected:
int BlocksPerPage(void) const;
void AllocateFirst(void);
bool InitPage(Page *page, Page *prev, const char *file, unsigned int line);
// availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list
// unavailablePages are pages which are totally full, and from which we do not return new blocks.
// Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages
Page *availablePages, *unavailablePages;
int availablePagesSize, unavailablePagesSize;
int memoryPoolPageSize;
};
template<class MemoryBlockType>
MemoryPool<MemoryBlockType>::MemoryPool()
{
#ifndef _DISABLE_MEMORY_POOL
//AllocateFirst();
availablePagesSize=0;
unavailablePagesSize=0;
memoryPoolPageSize=16384;
#endif
}
template<class MemoryBlockType>
MemoryPool<MemoryBlockType>::~MemoryPool()
{
#ifndef _DISABLE_MEMORY_POOL
Clear(_FILE_AND_LINE_);
#endif
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::SetPageSize(int size)
{
memoryPoolPageSize=size;
}
template<class MemoryBlockType>
MemoryBlockType* MemoryPool<MemoryBlockType>::Allocate(const char *file, unsigned int line)
{
#ifdef _DISABLE_MEMORY_POOL
return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line);
#else
if (availablePagesSize>0)
{
MemoryBlockType *retVal;
Page *curPage;
curPage=availablePages;
retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)];
if (curPage->availableStackSize==0)
{
--availablePagesSize;
availablePages=curPage->next;
RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0);
curPage->next->prev=curPage->prev;
curPage->prev->next=curPage->next;
if (unavailablePagesSize++==0)
{
unavailablePages=curPage;
curPage->next=curPage;
curPage->prev=curPage;
}
else
{
curPage->next=unavailablePages;
curPage->prev=unavailablePages->prev;
unavailablePages->prev->next=curPage;
unavailablePages->prev=curPage;
}
}
RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0);
return retVal;
}
availablePages = (Page *) rakMalloc_Ex(sizeof(Page), file, line);
if (availablePages==0)
return 0;
availablePagesSize=1;
if (InitPage(availablePages, availablePages, file, line)==false)
return 0;
// If this assert hits, we couldn't allocate even 1 block per page. Increase the page size
RakAssert(availablePages->availableStackSize>1);
return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize];
#endif
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Release(MemoryBlockType *m, const char *file, unsigned int line)
{
#ifdef _DISABLE_MEMORY_POOL
rakFree_Ex(m, file, line);
return;
#else
// Find the page this block is in and return it.
Page *curPage;
MemoryWithPage *memoryWithPage = (MemoryWithPage*)m;
curPage=memoryWithPage->parentPage;
if (curPage->availableStackSize==0)
{
// The page is in the unavailable list so move it to the available list
curPage->availableStack[curPage->availableStackSize++]=memoryWithPage;
unavailablePagesSize--;
// As this page is no longer totally empty, move it to the end of available pages
curPage->next->prev=curPage->prev;
curPage->prev->next=curPage->next;
if (unavailablePagesSize>0 && curPage==unavailablePages)
unavailablePages=unavailablePages->next;
if (availablePagesSize++==0)
{
availablePages=curPage;
curPage->next=curPage;
curPage->prev=curPage;
}
else
{
curPage->next=availablePages;
curPage->prev=availablePages->prev;
availablePages->prev->next=curPage;
availablePages->prev=curPage;
}
}
else
{
curPage->availableStack[curPage->availableStackSize++]=memoryWithPage;
if (curPage->availableStackSize==BlocksPerPage() &&
availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES)
{
// After a certain point, just deallocate empty pages rather than keep them around
if (curPage==availablePages)
{
availablePages=curPage->next;
RakAssert(availablePages->availableStackSize>0);
}
curPage->prev->next=curPage->next;
curPage->next->prev=curPage->prev;
availablePagesSize--;
rakFree_Ex(curPage->availableStack, file, line );
rakFree_Ex(curPage->block, file, line );
rakFree_Ex(curPage, file, line );
}
}
#endif
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Clear(const char *file, unsigned int line)
{
#ifdef _DISABLE_MEMORY_POOL
return;
#else
Page *cur, *freed;
if (availablePagesSize>0)
{
cur = availablePages;
#ifdef _MSC_VER
#pragma warning(disable:4127) // conditional expression is constant
#endif
while (true)
// do
{
rakFree_Ex(cur->availableStack, file, line );
rakFree_Ex(cur->block, file, line );
freed=cur;
cur=cur->next;
if (cur==availablePages)
{
rakFree_Ex(freed, file, line );
break;
}
rakFree_Ex(freed, file, line );
}// while(cur!=availablePages);
}
if (unavailablePagesSize>0)
{
cur = unavailablePages;
while (1)
//do
{
rakFree_Ex(cur->availableStack, file, line );
rakFree_Ex(cur->block, file, line );
freed=cur;
cur=cur->next;
if (cur==unavailablePages)
{
rakFree_Ex(freed, file, line );
break;
}
rakFree_Ex(freed, file, line );
} // while(cur!=unavailablePages);
}
availablePagesSize=0;
unavailablePagesSize=0;
#endif
}
template<class MemoryBlockType>
int MemoryPool<MemoryBlockType>::BlocksPerPage(void) const
{
return memoryPoolPageSize / sizeof(MemoryWithPage);
}
template<class MemoryBlockType>
bool MemoryPool<MemoryBlockType>::InitPage(Page *page, Page *prev, const char *file, unsigned int line)
{
int i=0;
const int bpp = BlocksPerPage();
page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line);
if (page->block==0)
return false;
page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line);
if (page->availableStack==0)
{
rakFree_Ex(page->block, file, line );
return false;
}
MemoryWithPage *curBlock = page->block;
MemoryWithPage **curStack = page->availableStack;
while (i < bpp)
{
curBlock->parentPage=page;
curStack[i]=curBlock++;
i++;
}
page->availableStackSize=bpp;
page->next=availablePages;
page->prev=prev;
return true;
}
}
#endif
/*
#include "DS_MemoryPool.hpp"
#include "DS_List.hpp"
struct TestMemoryPool
{
int allocationId;
};
int main(void)
{
DataStructures::MemoryPool<TestMemoryPool> memoryPool;
DataStructures::List<TestMemoryPool*> returnList;
for (int i=0; i < 100000; i++)
returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
for (int i=0; i < returnList.Size(); i+=2)
{
memoryPool.Release(returnList[i], _FILE_AND_LINE_);
returnList.RemoveAtIndexFast(i);
}
for (int i=0; i < 100000; i++)
returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
while (returnList.Size())
{
memoryPool.Release(returnList[returnList.Size()-1], _FILE_AND_LINE_);
returnList.RemoveAtIndex(returnList.Size()-1);
}
for (int i=0; i < 100000; i++)
returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
while (returnList.Size())
{
memoryPool.Release(returnList[returnList.Size()-1], _FILE_AND_LINE_);
returnList.RemoveAtIndex(returnList.Size()-1);
}
for (int i=0; i < 100000; i++)
returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
for (int i=100000-1; i <= 0; i-=2)
{
memoryPool.Release(returnList[i], _FILE_AND_LINE_);
returnList.RemoveAtIndexFast(i);
}
for (int i=0; i < 100000; i++)
returnList.Push(memoryPool.Allocate(_FILE_AND_LINE_), _FILE_AND_LINE_);
while (returnList.Size())
{
memoryPool.Release(returnList[returnList.Size()-1], _FILE_AND_LINE_);
returnList.RemoveAtIndex(returnList.Size()-1);
}
return 0;
}
*/