| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- /*
- * 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.h"
- #include "Export.h"
- #include "RakMemoryOverride.h"
- // 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.h"
- #include "DS_List.h"
- 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;
- }
- */
|