diff options
-rw-r--r-- | src/AllocationPool.h | 109 | ||||
-rw-r--r-- | src/Chunk.cpp | 4 | ||||
-rw-r--r-- | src/Chunk.h | 3 | ||||
-rw-r--r-- | src/ChunkData.cpp | 25 | ||||
-rw-r--r-- | src/ChunkData.h | 54 | ||||
-rw-r--r-- | src/ChunkMap.cpp | 20 | ||||
-rw-r--r-- | src/ChunkMap.h | 27 | ||||
-rw-r--r-- | tests/ChunkData/ArraytoCoord.cpp | 20 | ||||
-rw-r--r-- | tests/ChunkData/Coordinates.cpp | 21 | ||||
-rw-r--r-- | tests/ChunkData/Copies.cpp | 23 | ||||
-rw-r--r-- | tests/ChunkData/CopyBlocks.cpp | 15 | ||||
-rw-r--r-- | tests/ChunkData/creatable.cpp | 15 |
12 files changed, 281 insertions, 55 deletions
diff --git a/src/AllocationPool.h b/src/AllocationPool.h new file mode 100644 index 000000000..5d749a79e --- /dev/null +++ b/src/AllocationPool.h @@ -0,0 +1,109 @@ + +#pragma once + +#include <memory> + +template<class T> +class cAllocationPool +{ +public: + class cStarvationCallbacks + { + public: + virtual ~cStarvationCallbacks() {} + + /** Is called when the reserve buffer starts to be used **/ + virtual void OnStartUsingReserve() = 0; + + /** Is called once the reserve buffer has returned to normal size **/ + virtual void OnEndUsingReserve() = 0; + + /** Is called when the allocation pool is unable to allocate memory. Will be repeatedly + called if it does not free sufficient memory **/ + virtual void OnOutOfReserve() = 0; + }; + + virtual ~cAllocationPool() {} + + /** Allocates a pointer to T **/ + virtual T * Allocate() = 0; + + /** Frees the pointer passed in a_ptr, invalidating it **/ + virtual void Free(T * a_ptr) = 0; +}; + +/** Allocates memory storing unused elements in a linked list. Keeps at least NumElementsInReserve +elements in the list unless malloc fails so that the program has a reserve to handle OOM.**/ +template<class T, size_t NumElementsInReserve> +class cListAllocationPool : public cAllocationPool<T> +{ + public: + + cListAllocationPool(std::auto_ptr<typename cAllocationPool<T>::cStarvationCallbacks> a_Callbacks) : + m_Callbacks(a_Callbacks) + { + for (size_t i = 0; i < NumElementsInReserve; i++) + { + void * space = malloc(sizeof(T)); + if (space == NULL) + { + m_Callbacks->OnStartUsingReserve(); + break; + } + m_FreeList.push_front(space); + } + } + + virtual ~cListAllocationPool() + { + while (!m_FreeList.empty()) + { + free (m_FreeList.front()); + m_FreeList.pop_front(); + } + } + + virtual T * Allocate() override + { + if (m_FreeList.size() <= NumElementsInReserve) + { + void * space = malloc(sizeof(T)); + if (space != NULL) + { + return new(space) T; + } + else if (m_FreeList.size() == NumElementsInReserve) + { + m_Callbacks->OnStartUsingReserve(); + } + else if (m_FreeList.empty()) + { + m_Callbacks->OnOutOfReserve(); + // Try again until the memory is avalable + return Allocate(); + } + } + // placement new, used to initalize the object + T * ret = new (m_FreeList.front()) T; + m_FreeList.pop_front(); + return ret; + } + virtual void Free(T * a_ptr) override + { + if (a_ptr == NULL) + { + return; + } + // placement destruct. + a_ptr->~T(); + m_FreeList.push_front(a_ptr); + if (m_FreeList.size() == NumElementsInReserve) + { + m_Callbacks->OnEndUsingReserve(); + } + } + + private: + std::list<void *> m_FreeList; + std::auto_ptr<typename cAllocationPool<T>::cStarvationCallbacks> m_Callbacks; +}; diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 44fcefbe1..4703e4536 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -64,7 +64,8 @@ sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_Bloc cChunk::cChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World, - cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, + cAllocationPool<cChunkData::sChunkSection> & a_Pool ) : m_IsValid(false), m_IsLightValid(false), @@ -77,6 +78,7 @@ cChunk::cChunk( m_PosZ(a_ChunkZ), m_World(a_World), m_ChunkMap(a_ChunkMap), + m_ChunkData(a_Pool), m_BlockTickX(0), m_BlockTickY(0), m_BlockTickZ(0), diff --git a/src/Chunk.h b/src/Chunk.h index dfdabea04..7664a7afd 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -65,7 +65,8 @@ public: cChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects - cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP // Neighbor chunks + cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, // Neighbor chunks + cAllocationPool<cChunkData::sChunkSection> & a_Pool ); cChunk(cChunk & other); ~cChunk(); diff --git a/src/ChunkData.cpp b/src/ChunkData.cpp index f2d220bd2..03b0224a6 100644 --- a/src/ChunkData.cpp +++ b/src/ChunkData.cpp @@ -27,11 +27,12 @@ template <typename T> inline bool IsAllValue(const T * a_Array, size_t a_NumElem -cChunkData::cChunkData(void) +cChunkData::cChunkData(cAllocationPool<cChunkData::sChunkSection> & a_Pool) : #if __cplusplus < 201103L // auto_ptr style interface for memory management - : m_IsOwner(true) + m_IsOwner(true), #endif + m_Pool(a_Pool) { for (size_t i = 0; i < NumSections; i++) { @@ -66,7 +67,8 @@ cChunkData::~cChunkData() #if __cplusplus < 201103L // auto_ptr style interface for memory management cChunkData::cChunkData(const cChunkData & a_Other) : - m_IsOwner(true) + m_IsOwner(true), + m_Pool(a_Other.m_Pool) { // Move contents and ownership from a_Other to this, pointer-wise: for (size_t i = 0; i < NumSections; i++) @@ -97,7 +99,7 @@ cChunkData::~cChunkData() m_Sections[i] = NULL; } } - + // Move contents and ownership from a_Other to this, pointer-wise: m_IsOwner = true; for (size_t i = 0; i < NumSections; i++) @@ -105,13 +107,15 @@ cChunkData::~cChunkData() m_Sections[i] = a_Other.m_Sections[i]; } a_Other.m_IsOwner = false; + ASSERT(&m_Pool == &a_Other.m_Pool); return *this; } #else // unique_ptr style interface for memory management - cChunkData::cChunkData(cChunkData && other) + cChunkData::cChunkData(cChunkData && other) : + m_Pool(other.m_Pool) { for (size_t i = 0; i < NumSections; i++) { @@ -128,6 +132,7 @@ cChunkData::~cChunkData() { if (&other != this) { + ASSERT(&m_Pool == &other.m_Pool); for (size_t i = 0; i < NumSections; i++) { Free(m_Sections[i]); @@ -317,12 +322,12 @@ NIBBLETYPE cChunkData::GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) const cChunkData cChunkData::Copy(void) const { - cChunkData copy; + cChunkData copy(m_Pool); for (size_t i = 0; i < NumSections; i++) { if (m_Sections[i] != NULL) { - copy.m_Sections[i] = Allocate(); + copy.m_Sections[i] = copy.Allocate(); *copy.m_Sections[i] = *m_Sections[i]; } } @@ -561,8 +566,7 @@ void cChunkData::SetSkyLight(const NIBBLETYPE * a_Src) cChunkData::sChunkSection * cChunkData::Allocate(void) { - // TODO: Use an allocation pool - return new cChunkData::sChunkSection; + return m_Pool.Allocate(); } @@ -571,8 +575,7 @@ cChunkData::sChunkSection * cChunkData::Allocate(void) void cChunkData::Free(cChunkData::sChunkSection * a_Section) { - // TODO: Use an allocation pool - delete a_Section; + m_Pool.Free(a_Section); } diff --git a/src/ChunkData.h b/src/ChunkData.h index fef31b5ad..fe8b068a2 100644 --- a/src/ChunkData.h +++ b/src/ChunkData.h @@ -15,6 +15,7 @@ #include "ChunkDef.h" +#include "AllocationPool.h" @@ -26,9 +27,17 @@ class cChunkData { +private: + + static const size_t SectionHeight = 16; + static const size_t NumSections = (cChunkDef::Height / SectionHeight); + static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width; + public: - cChunkData(void); + struct sChunkSection; + + cChunkData(cAllocationPool<cChunkData::sChunkSection> & a_Pool); ~cChunkData(); #if __cplusplus < 201103L @@ -53,17 +62,17 @@ public: /** Creates a (deep) copy of self. */ cChunkData Copy(void) const; - + /** Copies the blocktype data into the specified flat array. Optionally, only a part of the data is copied, as specified by the a_Idx and a_Length parameters. */ void CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx = 0, size_t a_Length = cChunkDef::NumBlocks) const; - + /** Copies the metadata into the specified flat array. */ void CopyMetas(NIBBLETYPE * a_Dest) const; - + /** Copies the block light data into the specified flat array. */ void CopyBlockLight(NIBBLETYPE * a_Dest) const; - + /** Copies the skylight data into the specified flat array. */ void CopySkyLight (NIBBLETYPE * a_Dest) const; @@ -71,12 +80,12 @@ public: Allocates sections that are needed for the operation. Requires that a_Src is a valid pointer. */ void SetBlockTypes(const BLOCKTYPE * a_Src); - + /** Copies the metadata from the specified flat array into the internal representation. Allocates sectios that are needed for the operation. Requires that a_Src is a valid pointer. */ void SetMetas(const NIBBLETYPE * a_Src); - + /** Copies the blocklight data from the specified flat array into the internal representation. Allocates sectios that are needed for the operation. Allows a_Src to be NULL, in which case it doesn't do anything. */ @@ -86,36 +95,35 @@ public: Allocates sectios that are needed for the operation. Allows a_Src to be NULL, in which case it doesn't do anything. */ void SetSkyLight(const NIBBLETYPE * a_Src); + + struct sChunkSection + { + BLOCKTYPE m_BlockTypes [SectionHeight * 16 * 16] ; + NIBBLETYPE m_BlockMetas [SectionHeight * 16 * 16 / 2]; + NIBBLETYPE m_BlockLight [SectionHeight * 16 * 16 / 2]; + NIBBLETYPE m_BlockSkyLight[SectionHeight * 16 * 16 / 2]; + }; private: - - static const size_t SectionHeight = 16; - static const size_t NumSections = (cChunkDef::Height / SectionHeight); - static const size_t SectionBlockCount = SectionHeight * cChunkDef::Width * cChunkDef::Width; - #if __cplusplus < 201103L // auto_ptr style interface for memory management mutable bool m_IsOwner; #endif - - struct sChunkSection { - BLOCKTYPE m_BlockTypes [SectionBlockCount]; - NIBBLETYPE m_BlockMetas [SectionBlockCount / 2]; - NIBBLETYPE m_BlockLight [SectionBlockCount / 2]; - NIBBLETYPE m_BlockSkyLight[SectionBlockCount / 2]; - }; - + sChunkSection * m_Sections[NumSections]; + + cAllocationPool<cChunkData::sChunkSection> & m_Pool; /** Allocates a new section. Entry-point to custom allocators. */ - static sChunkSection * Allocate(void); - + sChunkSection * Allocate(void); + /** Frees the specified section, previously allocated using Allocate(). Note that a_Section may be NULL. */ - static void Free(sChunkSection * a_Section); + void Free(sChunkSection * a_Section); /** Sets the data in the specified section to their default values. */ void ZeroSection(sChunkSection * a_Section) const; + }; diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index dba6f3f41..d2ccca94e 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -34,8 +34,15 @@ // cChunkMap: cChunkMap::cChunkMap(cWorld * a_World ) - : m_World( a_World ) + : m_World( a_World ), + m_Pool( + new cListAllocationPool<cChunkData::sChunkSection, 1600>( + std::auto_ptr<cAllocationPool<cChunkData::sChunkSection>::cStarvationCallbacks>( + new cStarvationCallbacks()) + ) + ) { + } @@ -78,7 +85,7 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) } // Not found, create new: - cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); + cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this, *m_Pool); if (Layer == NULL) { LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); @@ -2670,11 +2677,16 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) //////////////////////////////////////////////////////////////////////////////// // cChunkMap::cChunkLayer: -cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) +cChunkMap::cChunkLayer::cChunkLayer( + int a_LayerX, int a_LayerZ, + cChunkMap * a_Parent, + cAllocationPool<cChunkData::sChunkSection> & a_Pool +) : m_LayerX( a_LayerX ) , m_LayerZ( a_LayerZ ) , m_Parent( a_Parent ) , m_NumChunksLoaded( 0 ) + , m_Pool(a_Pool) { memset(m_Chunks, 0, sizeof(m_Chunks)); } @@ -2716,7 +2728,7 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ); cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ - 1); cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX , a_ChunkZ + 1); - m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp); + m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp, m_Pool); } return m_Chunks[Index]; } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 7e85bb6f1..5aad0dd2a 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -351,7 +351,11 @@ private: class cChunkLayer { public: - cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); + cChunkLayer( + int a_LayerX, int a_LayerZ, + cChunkMap * a_Parent, + cAllocationPool<cChunkData::sChunkSection> & a_Pool + ); ~cChunkLayer(); /** Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check */ @@ -395,6 +399,25 @@ private: int m_LayerZ; cChunkMap * m_Parent; int m_NumChunksLoaded; + + cAllocationPool<cChunkData::sChunkSection> & m_Pool; + }; + + class cStarvationCallbacks + : public cAllocationPool<cChunkData::sChunkSection>::cStarvationCallbacks + { + virtual void OnStartUsingReserve() override + { + LOG("Using backup memory buffer"); + } + virtual void OnEndUsingReserve() override + { + LOG("Stoped using backup memory buffer"); + } + virtual void OnOutOfReserve() override + { + LOG("Out of Memory"); + } }; typedef std::list<cChunkLayer *> cChunkLayerList; @@ -427,6 +450,8 @@ private: /** The cChunkStay descendants that are currently enabled in this chunkmap */ cChunkStays m_ChunkStays; + std::auto_ptr<cAllocationPool<cChunkData::sChunkSection>> m_Pool; + cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate diff --git a/tests/ChunkData/ArraytoCoord.cpp b/tests/ChunkData/ArraytoCoord.cpp index 0daf38f7b..9d0ca6c8c 100644 --- a/tests/ChunkData/ArraytoCoord.cpp +++ b/tests/ChunkData/ArraytoCoord.cpp @@ -6,9 +6,23 @@ int main(int argc, char** argv) { + class cMockAllocationPool + : public cAllocationPool<cChunkData::sChunkSection> + { + virtual cChunkData::sChunkSection * Allocate() + { + return new cChunkData::sChunkSection(); + } + + virtual void Free(cChunkData::sChunkSection * a_Ptr) + { + delete a_Ptr; + } + } Pool; { + // Test first segment - cChunkData buffer; + cChunkData buffer(Pool); BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, sizeof(SrcBlockBuffer)); @@ -35,7 +49,7 @@ int main(int argc, char** argv) { // test following segment - cChunkData buffer; + cChunkData buffer(Pool); BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, sizeof(SrcBlockBuffer)); @@ -62,7 +76,7 @@ int main(int argc, char** argv) { // test zeros - cChunkData buffer; + cChunkData buffer(Pool); BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, sizeof(SrcBlockBuffer)); diff --git a/tests/ChunkData/Coordinates.cpp b/tests/ChunkData/Coordinates.cpp index 48d731c7e..b3c66dde5 100644 --- a/tests/ChunkData/Coordinates.cpp +++ b/tests/ChunkData/Coordinates.cpp @@ -6,8 +6,21 @@ int main(int argc, char** argv) { + class cMockAllocationPool + : public cAllocationPool<cChunkData::sChunkSection> + { + virtual cChunkData::sChunkSection * Allocate() + { + return new cChunkData::sChunkSection(); + } + + virtual void Free(cChunkData::sChunkSection * a_Ptr) + { + delete a_Ptr; + } + } Pool; { - cChunkData buffer; + cChunkData buffer(Pool); // Empty chunks buffer.SetBlock(0, 0, 0, 0xAB); @@ -105,7 +118,7 @@ int main(int argc, char** argv) } { - cChunkData buffer; + cChunkData buffer(Pool); // Zero's buffer.SetBlock(0, 0, 0, 0x0); @@ -122,9 +135,9 @@ int main(int argc, char** argv) { // Operator = - cChunkData buffer; + cChunkData buffer(Pool); buffer.SetBlock(0, 0, 0, 0x42); - cChunkData copy; + cChunkData copy(Pool); #if __cplusplus < 201103L copy = buffer; #else diff --git a/tests/ChunkData/Copies.cpp b/tests/ChunkData/Copies.cpp index 312441eee..440819e91 100644 --- a/tests/ChunkData/Copies.cpp +++ b/tests/ChunkData/Copies.cpp @@ -6,8 +6,21 @@ int main(int argc, char** argv) { + class cMockAllocationPool + : public cAllocationPool<cChunkData::sChunkSection> + { + virtual cChunkData::sChunkSection * Allocate() + { + return new cChunkData::sChunkSection(); + } + + virtual void Free(cChunkData::sChunkSection * a_Ptr) + { + delete a_Ptr; + } + } Pool; { - cChunkData buffer; + cChunkData buffer(Pool); buffer.SetBlock(3, 1, 4, 0xDE); buffer.SetMeta(3, 1, 4, 0xA); @@ -37,7 +50,7 @@ int main(int argc, char** argv) } { - cChunkData buffer; + cChunkData buffer(Pool); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; for (int i = 0; i < 16 * 16 * 256 / 2; i += 4) @@ -60,7 +73,7 @@ int main(int argc, char** argv) } { - cChunkData buffer; + cChunkData buffer(Pool); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; for (int i = 0; i < 16 * 16 * 256 / 2; i += 4) @@ -83,7 +96,7 @@ int main(int argc, char** argv) } { - cChunkData buffer; + cChunkData buffer(Pool); NIBBLETYPE SrcNibbleBuffer[16 * 16 * 256 / 2]; for (int i = 0; i < 16 * 16 * 256 / 2; i += 4) @@ -106,7 +119,7 @@ int main(int argc, char** argv) } { - cChunkData buffer; + cChunkData buffer(Pool); BLOCKTYPE SrcBlockBuffer[16 * 16 * 256]; memset(SrcBlockBuffer, 0x00, 16 * 16 * 256); diff --git a/tests/ChunkData/CopyBlocks.cpp b/tests/ChunkData/CopyBlocks.cpp index be8cab234..ec9451099 100644 --- a/tests/ChunkData/CopyBlocks.cpp +++ b/tests/ChunkData/CopyBlocks.cpp @@ -17,7 +17,20 @@ int main(int argc, char ** argv) { // Set up a cChunkData with known contents - all blocks 0x01, all metas 0x02: - cChunkData Data; + class cMockAllocationPool + : public cAllocationPool<cChunkData::sChunkSection> + { + virtual cChunkData::sChunkSection * Allocate() + { + return new cChunkData::sChunkSection(); + } + + virtual void Free(cChunkData::sChunkSection * a_Ptr) + { + delete a_Ptr; + } + } Pool; + cChunkData Data(Pool); cChunkDef::BlockTypes BlockTypes; cChunkDef::BlockNibbles BlockMetas; memset(BlockTypes, 0x01, sizeof(BlockTypes)); diff --git a/tests/ChunkData/creatable.cpp b/tests/ChunkData/creatable.cpp index 1321bf49b..fc786f688 100644 --- a/tests/ChunkData/creatable.cpp +++ b/tests/ChunkData/creatable.cpp @@ -4,6 +4,19 @@ int main(int argc, char** argv) { - cChunkData buffer; + class cMockAllocationPool + : public cAllocationPool<cChunkData::sChunkSection> + { + virtual cChunkData::sChunkSection * Allocate() + { + return new cChunkData::sChunkSection(); + } + + virtual void Free(cChunkData::sChunkSection * a_Ptr) + { + delete a_Ptr; + } + } Pool; + cChunkData buffer(Pool); return 0; } |