{"id":329,"date":"2013-09-09T22:31:05","date_gmt":"2013-09-10T03:31:05","guid":{"rendered":"http:\/\/www.warsam.com\/?page_id=329"},"modified":"2013-10-16T20:23:50","modified_gmt":"2013-10-17T01:23:50","slug":"test","status":"publish","type":"page","link":"https:\/\/www.warsam.com\/?page_id=329","title":{"rendered":"Thread-Safe Memory Manager"},"content":{"rendered":"<p>I developed a simple memory manager further outside of class into a thread-safe memory manager that I now use in my multithreaded engine which supports <a title=\"Quake III BSP Loader\" href=\"https:\/\/www.warsam.com\/?page_id=361\">Quake III BSP loading<\/a> and a custom <a title=\"Character Animation System\" href=\"https:\/\/www.warsam.com\/?page_id=348\"> Character Animation System<\/a>. <\/p>\n<p\/>The primary motivation for the memory manager is to control the allocation of memory to increase <span class=\"bh\"> locality of references<\/span>. The memory manager allocates a large block of memory which is then used for all the engine&#8217;s memory allocations for <span class=\"bh\">increased spatial locality<\/span>. The engine features recycled object pools within various components to <span class=\"bh\"> increase temporal locality<\/span>. Separate threads are used for resource buffering and <a title=\"Concurrent Renderer\" href=\"https:\/\/www.warsam.com\/?page_id=438\">rendering<\/a> which perform all heap allocations through the memory manager. In addition, <span class=\"bh\">memory usage statistics<\/span> are tracked and <span class=\"bh\"> leaks are reported<\/span> to Visual Studio&#8217;s output window.<\/p>\n<h2>Source Code<\/h2>\n<div id=\"squelch-taas-tab-group-0\" class=\"squelch-taas-tab-group squelch-taas-override\" data-title=\"\" data-disabled=\"false\" data-collapsible=\"true\" data-active=\"0\" data-event=\"click\"><ul><li class=\"squelch-taas-tab\"><a href=\"#squelch-taas-tab-content-0-0\">MemoryPool.h<\/a><\/li><li class=\"squelch-taas-tab\"><a href=\"#squelch-taas-tab-content-0-1\">MemoryPool.cpp<\/a><\/li><li class=\"squelch-taas-tab\"><a href=\"#squelch-taas-tab-content-0-2\">MallocAllocator.h<\/a><\/li><\/ul><div id=\"squelch-taas-tab-content-0-0\" class=\"\">[crayon lang=&#8221;cpp&#8221;]\n\/*<br \/>\nfile    MemoryPool.h<br \/>\ndate    11\/20\/2012<br \/>\nauthor  Warsam Osman<br \/>\nbrief   Memory Pool &#8211; Thread-Safe Memory Manager &#8211; overrides global new\/delete<br \/>\n        Tracks memory use statistics and leaks in separate memory.<br \/>\n*\/<br \/>\n#pragma once<\/p>\n<p>#ifndef _MEMORY_POOL_<br \/>\n#define _MEMORY_POOL_<\/p>\n<p>#include <cassert><br \/>\n#include &#8220;Mutex.h&#8221;<\/p>\n<p>namespace core<br \/>\n{<br \/>\n    class MemoryPool;<\/p>\n<p>    \/* Templated Thread Safe MemoryPool<br \/>\n    Uses Lock_Type Mutex to make the Pool_Type shared Memory pool thread safe *\/<br \/>\n    template< class Pool_Type, class Lock_Type ><br \/>\n    class MTMemoryPool<br \/>\n    {<br \/>\n    public:<br \/>\n        \/\/ Passes filename and linenumber to sharedPool for allocation tracking and statistics<br \/>\n        inline void* MTPoolNew( size_t sizeInBytes, char* fileName,  int const lineNumber );<br \/>\n        inline void MTPoolDelete( void* memAddress );<\/p>\n<p>    private:<br \/>\n        Pool_Type sharedPool;<br \/>\n        Lock_Type lock;<br \/>\n    };<\/p>\n<p>    \/\/ Template implementation<br \/>\n    template< class Pool_Type, class Lock_Type ><br \/>\n    inline void* MTMemoryPool< Pool_Type, Lock_Type >::MTPoolNew( size_t sizeInBytes, char* fileName,  int const lineNumber  )<br \/>\n    {<br \/>\n        void* mem = nullptr;<br \/>\n        lock.Lock();<br \/>\n        mem = sharedPool.PoolNew( sizeInBytes, fileName, lineNumber );<br \/>\n        lock.Unlock();<br \/>\n        return mem;<br \/>\n    }<\/p>\n<p>    template< class Pool_Type, class Lock_Type ><br \/>\n    inline void MTMemoryPool< Pool_Type, Lock_Type >::MTPoolDelete( void* memAddress )<br \/>\n    {<br \/>\n        lock.Lock();<br \/>\n        sharedPool.PoolDelete(memAddress);<br \/>\n        lock.Unlock();<br \/>\n    }<\/p>\n<p>    typedef MTMemoryPool< MemoryPool, Mutex > ThreadSafePool;<\/p>\n<p>    extern ThreadSafePool* const GetMemoryPool();<\/p>\n<p>    typedef unsigned char Byte;<br \/>\n    typedef size_t BoundaryTag;<\/p>\n<p>\/\/ tag size and double tag size<br \/>\n#define TAGSIZE     4<br \/>\n#define DTAGSIZE    8<\/p>\n<p>    \/\/ Memory pool managed with boundary tags &#038; tracks stats \/ reports leaks<br \/>\n    class MemoryPool<br \/>\n    {<br \/>\n    public:<br \/>\n        MemoryPool();<br \/>\n        ~MemoryPool();  <\/p>\n<p>        \/\/ Initialize total memory pool by allocating a large chunk using calloc to 0 out the memory.<br \/>\n        void            Init( size_t sizeBytes );<\/p>\n<p>        \/* Called by overridden new to request allocation of sizeInBytes bytes<br \/>\n        In addition to passing the filename and linenumber when available.<br \/>\n        Otherwise fileName is marked as &#8220;unknown&#8221; *\/<br \/>\n        void*           PoolNew( const size_t sizeInBytes, char* fileName,  int const lineNumber  );<\/p>\n<p>        \/* Called by overridden delete to deallocate the memory at specified pMemAddress<br \/>\n        The address is checked to verify it is in the pool, otherwise an exception occurs. *\/<br \/>\n        void            PoolDelete( void* pMemAddress );<\/p>\n<p>        \/\/ Returns total space available in bytes<br \/>\n        size_t          SpaceAvailable();<\/p>\n<p>    private:<br \/>\n        \/\/ Inline private helpers <\/p>\n<p>        \/* Performs allocation of heap memory by allocation requested size and padding with alignment + overhead.<br \/>\n        Returns nullptr on failure. *\/<br \/>\n        void*           Alloc( size_t requestSizeBytes );<\/p>\n<p>        \/\/ Deallocates allocated memory from passed address and coalesces freed memory using boundary tags.<br \/>\n        void            Dealloc( void* someElement );<\/p>\n<p>        \/\/ Uses next fit algorithm to return the first available memory chunk that is big enough.<br \/>\n        void*           FindFit( size_t sizeBytes );<\/p>\n<p>        \/\/ Splits the block found by FindFit if bigger than threshold (16 bytes) and updates boundary tags accordingly.<br \/>\n        void            PlaceBlock( void* block, size_t sizeBytes );<\/p>\n<p>        \/\/ Coalesces blocks of memory using boundary tags.<br \/>\n        void*           Coalesce( void* block );<\/p>\n<p>        \/\/ Sets the last bit on the BoundaryTag to indicate whether or not the block is allocated.<br \/>\n        BoundaryTag     SetTagAlloc( size_t size, size_t alloc)     { return size | alloc; }   <\/p>\n<p>        \/\/ Gets the BoundaryTag from a passed in address.<br \/>\n        BoundaryTag&#038;    GetTag( Byte* address )                     { return *(BoundaryTag *)(address); }<\/p>\n<p>        \/\/ Sets the passed in BoundaryTag to the specified address.<br \/>\n        void            PutTag( Byte* address, BoundaryTag tag)     { *(BoundaryTag *)(address) = tag; }   <\/p>\n<p>        \/\/ Retrieves the block size in bytes from the boundary tag by checking all but the last 3 bits.<br \/>\n        size_t          GetBlockSize( Byte* address )               { return GetTag(address) &#038; ~0x7; } <\/p>\n<p>        \/\/ Checks the allocated bit of BoundaryTag to indicate whether or not the block is allocated.<br \/>\n        size_t          BlockAllocated( Byte* address )             { return GetTag(address) &#038; 0x1; }   <\/p>\n<p>        \/\/ Get the header BoundaryTag from a passed in block address.<br \/>\n        Byte*           GetHeader( void* block )    { return (Byte*)(block) &#8211; TAGSIZE; }   <\/p>\n<p>        \/\/ Get the footer BoundaryTag from a passed in block address.<br \/>\n        Byte*           GetFooter( void* block )    { return (Byte*)(block) + GetBlockSize(GetHeader(block)) &#8211; DTAGSIZE; }      <\/p>\n<p>        \/\/ Use the BoundaryTag to get the next memory block<br \/>\n        Byte*           NextBlock( void* block )    { return (Byte*)(block) + GetBlockSize(((Byte*)(block) &#8211; TAGSIZE)); } <\/p>\n<p>        \/\/ Use the BoundaryTag to get the previous memory block<br \/>\n        Byte*           PrevBlock( void* block )    { return (Byte*)(block) &#8211; GetBlockSize(((Byte*)(block) &#8211; DTAGSIZE)); } <\/p>\n<p>        void            ReportStatistics();<\/p>\n<p>        \/\/ Memory pool<br \/>\n        Byte*           mem;<br \/>\n        Byte*           current;<br \/>\n        size_t          poolSize;<br \/>\n        size_t          bytesAlreadyAllocated;<\/p>\n<p>        \/\/ Stats<br \/>\n        size_t          totalMemoryPoolSizeInBytes;<br \/>\n        size_t          totalAllocationCount;<br \/>\n        size_t          totalBytesAllocated;<br \/>\n        size_t          largestAllocationBytes;<br \/>\n        size_t          numAllocations;<\/p>\n<p>        \/\/ Copy protection<br \/>\n        MemoryPool( MemoryPool&#038; );<br \/>\n        void operator=( MemoryPool&#038; );<br \/>\n    };<br \/>\n}<\/p>\n<p>extern core::ThreadSafePool* g_pMemPool;<\/p>\n<p>\/\/ Overridden global new and delete variants<br \/>\nvoid*   operator new( size_t sizeInBytes, char* fileName, int lineNumber ) override;<br \/>\nvoid    operator delete( void* memAddress ) throw() override;<br \/>\nvoid    operator delete( void* memAddress, char* fileName, int lineNumber  ) throw() override;<br \/>\nvoid*   operator new( size_t sizeInBytes ) override;<br \/>\nvoid*   operator new[]( size_t sizeInBytes ) override;<br \/>\nvoid    operator delete[]( void* memAddress ) throw() override;<br \/>\nvoid*   operator new[]( size_t sizeInBytes, char* fileName, int lineNumber ) override;<br \/>\nvoid    operator delete[]( void* memAddress, char* fileName, int lineNumber  ) throw() override;<\/p>\n<p>#endif \/\/_MEMORY_POOL_[\/crayon]<\/div><div id=\"squelch-taas-tab-content-0-1\" class=\"\">[crayon lang=&#8221;cpp&#8221;]\n#include &#8220;MemoryPool.h&#8221;<br \/>\n#include &#8220;MallocAllocator.h&#8221;<br \/>\n#include <cstdlib><br \/>\n#include <stdio.h><br \/>\n#include <malloc.h><br \/>\n#include <stdarg.h><br \/>\n#include <new><br \/>\n#include <windows.h><br \/>\n#include <\/p>\n<map>\n<p>core::ThreadSafePool* g_pMemPool = nullptr;<\/p>\n<p>namespace core<br \/>\n{<br \/>\n    void OutputDebugFormatStr( const char* str, &#8230; );<\/p>\n<p>    static void _DestroyMemoryPool()<br \/>\n    {<br \/>\n        g_pMemPool->~ThreadSafePool();<br \/>\n        free(g_pMemPool);<br \/>\n        g_pMemPool = nullptr;<br \/>\n    }<\/p>\n<p>    ThreadSafePool* const GetMemoryPool()<br \/>\n    {<br \/>\n        if ( g_pMemPool == nullptr )<br \/>\n        {<br \/>\n            std::atexit(_DestroyMemoryPool);<\/p>\n<p>            g_pMemPool = reinterpret_cast<ThreadSafePool*>( malloc( sizeof(ThreadSafePool) ) );<br \/>\n            if( g_pMemPool == nullptr )<br \/>\n                throw(&#8220;Unable to allocate g_pMemPool for MemoryPool&#8221;);<\/p>\n<p>            g_pMemPool = new (g_pMemPool) ThreadSafePool();<br \/>\n        }<\/p>\n<p>        return g_pMemPool;<br \/>\n    }<\/p>\n<p>    struct CompareAddress<br \/>\n    {<br \/>\n        bool operator() (const void* lhs, const void* rhs) const<br \/>\n        {<br \/>\n            return lhs<rhs;\n        }\n    };\n\n    struct FileLine\n    {\n        FileLine( char* fileName, const int lineNumber )\n            :m_file( fileName )\n            ,m_line( lineNumber )\n        {}\n\n        char* m_file;\n        int m_line;\n    };\n\n    \/\/Map that uses malloc for allocations - too keep stats and leak detection separate from managed memory\n    typedef std::map<void*,FileLine,CompareAddress, MallocAllocator< std::pair<void*,FileLine> > > AllocationsMap;<br \/>\n    AllocationsMap* g_allocMap = nullptr;<\/p>\n<p>    \/\/MEMORY POOL \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<br \/>\n    \/\/<br \/>\n    MemoryPool::MemoryPool()<br \/>\n        :totalMemoryPoolSizeInBytes( 0 )<br \/>\n        ,totalAllocationCount( 0 )<br \/>\n        ,totalBytesAllocated( 0 )<br \/>\n        ,largestAllocationBytes( 0 )<br \/>\n        ,numAllocations( 0 )<br \/>\n    {<br \/>\n        if( g_allocMap == nullptr )<br \/>\n        {<br \/>\n            g_allocMap = reinterpret_cast< AllocationsMap *>( malloc(sizeof(AllocationsMap)) );<br \/>\n            if( g_allocMap == nullptr )<br \/>\n                throw(&#8220;Unable to allocate g_allocMap for MemoryPool&#8221;);<br \/>\n            g_allocMap = new (g_allocMap) AllocationsMap();<br \/>\n        }<\/p>\n<p>        Init( MEMORY_POOL_SIZE_BYTES );<br \/>\n    }<\/p>\n<p>    void MemoryPool::Init( size_t sizeBytes )<br \/>\n    {<br \/>\n        poolSize = sizeBytes;<\/p>\n<p>        size_t numWords = poolSize \/ TAGSIZE;<br \/>\n        size_t adjustedSize = ( numWords%2 ) ? ( numWords+1 ) * TAGSIZE : numWords * TAGSIZE;<\/p>\n<p>        mem = reinterpret_cast< Byte* >( calloc( ( adjustedSize + (3*TAGSIZE)), sizeof(Byte) ) );<\/p>\n<p>        if( mem == nullptr )<br \/>\n            throw(&#8220;Failed to allocate memory for MemoryPool&#8221;);<\/p>\n<p>        PutTag(mem, 0);                                         \/\/Alignment padding<br \/>\n        PutTag(mem + (1*TAGSIZE), SetTagAlloc(DTAGSIZE, 1));    \/\/Prologue header<br \/>\n        PutTag(mem + (2*TAGSIZE), SetTagAlloc(DTAGSIZE, 1));    \/\/Prologue footer<br \/>\n        PutTag(mem + (3*TAGSIZE), SetTagAlloc(0, 1));           \/\/Epilogue header<br \/>\n        mem += (2*TAGSIZE);                     <\/p>\n<p>        current = mem;<\/p>\n<p>        Byte* block = mem;<br \/>\n        PutTag( GetHeader(block), SetTagAlloc( adjustedSize, 0 ));  \/\/Free block header<br \/>\n        PutTag( GetFooter(block), SetTagAlloc( adjustedSize, 0 ));  \/\/Free block footer<br \/>\n        PutTag( GetHeader( NextBlock(block) ), SetTagAlloc(0,1) );  \/\/New epilogue header<br \/>\n    }<\/p>\n<p>    MemoryPool::~MemoryPool()<br \/>\n    {<br \/>\n        \/\/Report any leaks<br \/>\n        if( numAllocations > 0 )<br \/>\n        {<br \/>\n            OutputDebugFormatStr(&#8220;\\nMEMORY LEAKS DETECTED! \\n&#8221;);<br \/>\n            for ( auto it = g_allocMap->begin() ; it != g_allocMap->end(); ++it )<br \/>\n                OutputDebugFormatStr( &#8220;%s(%d) not deleted!\\n&#8221;, it->second.m_file, it->second.m_line );<br \/>\n        }<\/p>\n<p>        ReportStatistics();<\/p>\n<p>        g_allocMap->~AllocationsMap();<br \/>\n        free(g_allocMap);<br \/>\n    }<\/p>\n<p>    void* MemoryPool::PoolNew( const size_t sizeInBytes, char* fileName,  int const lineNumber )<br \/>\n    {<br \/>\n        assert( g_allocMap );<\/p>\n<p>        void* returnMemAddr = Alloc(sizeInBytes);<br \/>\n        assert( returnMemAddr );<\/p>\n<p>        \/\/Stats<br \/>\n        totalBytesAllocated += sizeInBytes;<br \/>\n        if( sizeInBytes > largestAllocationBytes )<br \/>\n            largestAllocationBytes = sizeInBytes;<br \/>\n        ++totalAllocationCount;<br \/>\n        ++numAllocations;<br \/>\n        g_allocMap->insert( AllocationsMap::value_type(returnMemAddr,FileLine(fileName,lineNumber)) );<\/p>\n<p>        return returnMemAddr;<br \/>\n    }<\/p>\n<p>    void MemoryPool::PoolDelete( void* pMemAddress )<br \/>\n    {<br \/>\n        assert( g_allocMap );<br \/>\n        assert( pMemAddress );<br \/>\n        auto addrIter = g_allocMap->find( pMemAddress );<br \/>\n        if( addrIter != g_allocMap->end() )<br \/>\n        {<br \/>\n            g_allocMap->erase( addrIter );<br \/>\n            &#8211;numAllocations;<br \/>\n        }<br \/>\n        else<br \/>\n        {<br \/>\n            throw(&#8220;MemoryPool::PoolDelete: address not found!&#8221;);<br \/>\n            return;<br \/>\n        }<\/p>\n<p>        Dealloc(pMemAddress);<br \/>\n    }<\/p>\n<p>    void* MemoryPool::Alloc(size_t requestSizeBytes)<br \/>\n    {<br \/>\n        assert( mem );<br \/>\n        void* foundBlock = nullptr;<\/p>\n<p>        if( requestSizeBytes == 0 ) return nullptr;<\/p>\n<p>        size_t alignedByteSize = ( requestSizeBytes <= DTAGSIZE ) ? 2 * DTAGSIZE : \n            DTAGSIZE * ( (requestSizeBytes + (DTAGSIZE) + (DTAGSIZE-1) ) \/ DTAGSIZE); \/\/alignment + overhead\n\n        foundBlock = FindFit(alignedByteSize);\n        if( foundBlock )\n        {\n            PlaceBlock( foundBlock, alignedByteSize );\n            bytesAlreadyAllocated += alignedByteSize;\n            return foundBlock;\n        }\n\n        return nullptr;\n    }\n\n    void MemoryPool::Dealloc(void *doomed) \n    {\n        if( doomed == nullptr ) return;\n\n        size_t sizeInBytes = GetBlockSize( GetHeader(doomed) );\n\n        assert( mem );\n\n        PutTag( GetHeader(doomed), SetTagAlloc(sizeInBytes,0) );\n        PutTag( GetFooter(doomed), SetTagAlloc(sizeInBytes,0) );\n\n        Coalesce( doomed );\n    }\n\n    void* MemoryPool::FindFit( size_t sizeBytes )\n    {\n        void* oldCurrent = current;\n\n        \/\/next fit - start at current and search to the end of the list\n        for(; GetBlockSize( GetHeader(current) ) > 0; current = NextBlock(current))<br \/>\n        {<br \/>\n            Byte* currentHeader = GetHeader(current);<br \/>\n            assert( currentHeader );<br \/>\n            const size_t currentSizeInHeader = GetBlockSize( currentHeader );<br \/>\n            if( !BlockAllocated( currentHeader ) &#038;&#038; (sizeBytes <= currentSizeInHeader ) )\n                return current;\n        }\n\n        \/\/search from start of list\n        for( current = mem; current < oldCurrent; current = NextBlock(current) )\n        {\n            Byte* currentHeader = GetHeader(current);\n            assert( currentHeader );\n            const size_t currentSizeInHeader = GetBlockSize( currentHeader );\n            if( !BlockAllocated( currentHeader ) &#038;&#038; (sizeBytes <= currentSizeInHeader ) )\n                return current;\n        }\n\n        return nullptr;\n    }\n\n    void MemoryPool::PlaceBlock( void* block, size_t sizeInBytes )\n    {\n        const size_t blockSize = GetBlockSize( GetHeader(block) );\n\n        \/\/Split if enough remaining space\n        const size_t remainder = blockSize - sizeInBytes;\n        if( remainder >= (2*DTAGSIZE) )<br \/>\n        {<br \/>\n            PutTag( GetHeader(block), SetTagAlloc(sizeInBytes,1) );<br \/>\n            PutTag( GetFooter(block), SetTagAlloc(sizeInBytes,1) );<br \/>\n            block = NextBlock( block );<br \/>\n            PutTag( GetHeader(block), SetTagAlloc(remainder, 0) );<br \/>\n            PutTag( GetFooter(block), SetTagAlloc(remainder, 0) );<br \/>\n        }<br \/>\n        else<br \/>\n        {<br \/>\n            PutTag( GetHeader(block), SetTagAlloc(blockSize,1) );<br \/>\n            PutTag( GetFooter(block), SetTagAlloc(blockSize,1) );<br \/>\n        }<br \/>\n    }<\/p>\n<p>    void* MemoryPool::Coalesce( void* block )<br \/>\n    {<br \/>\n        size_t prevAllocated = BlockAllocated( GetFooter( PrevBlock(block) ) );<br \/>\n        size_t nextAllocated = BlockAllocated( GetHeader( NextBlock(block) ) );<br \/>\n        size_t blockSize = GetBlockSize( GetHeader(block) );<\/p>\n<p>        if( prevAllocated &#038;&#038; nextAllocated )<br \/>\n        {<br \/>\n            return block;<br \/>\n        }<br \/>\n        else if( prevAllocated &#038;&#038; !nextAllocated )<br \/>\n        {<br \/>\n            blockSize += GetBlockSize( GetHeader( NextBlock(block) ) );<br \/>\n            PutTag( GetHeader(block), SetTagAlloc(blockSize, 0) );<br \/>\n            PutTag( GetFooter(block), SetTagAlloc(blockSize, 0) );<br \/>\n        }<br \/>\n        else if( !prevAllocated &#038;&#038; nextAllocated )<br \/>\n        {<br \/>\n            blockSize += GetBlockSize( GetHeader( PrevBlock(block) ) );<br \/>\n            PutTag( GetFooter(block), SetTagAlloc(blockSize, 0) );<br \/>\n            PutTag( GetHeader( PrevBlock(block) ), SetTagAlloc(blockSize, 0) );<br \/>\n            block = PrevBlock(block);<br \/>\n        }<br \/>\n        else<br \/>\n        {<br \/>\n            blockSize += GetBlockSize( GetHeader( PrevBlock(block) ) )  +<br \/>\n                GetBlockSize( GetFooter( NextBlock(block) ) );<br \/>\n            PutTag( GetHeader( PrevBlock(block) ), SetTagAlloc(blockSize, 0) );<br \/>\n            PutTag( GetFooter( NextBlock(block) ), SetTagAlloc(blockSize, 0) );<br \/>\n            block = PrevBlock(block);<br \/>\n        }<\/p>\n<p>        if( (current > (Byte*)block) &#038;&#038; (current < NextBlock(block) ) )\n            current = (Byte*)block;\n\n        return block;\n    }\n\n    void MemoryPool::ReportStatistics()\n    {\n        OutputDebugFormatStr(\"\\n======= MemoryPool Stats (Bytes) ======= \\n\");\n        OutputDebugFormatStr(\"Total Allocations: %d \\n\", totalAllocationCount );\n        OutputDebugFormatStr(\"Total Bytes Allocated: %d \\n\", totalBytesAllocated );\n        OutputDebugFormatStr(\"Largest Allocation Size Bytes Allocated: %d \\n\", largestAllocationBytes );\n        if( totalAllocationCount > 0 )<br \/>\n            OutputDebugFormatStr(&#8220;Average Allocation Size Bytes Allocated: %g \\n&#8221;, static_cast< float >(totalBytesAllocated \/ totalAllocationCount ) );<br \/>\n        OutputDebugFormatStr(&#8220;======= End of Stats ======= \\n\\n&#8221;);<br \/>\n    }<\/p>\n<p>    inline void OutputDebugFormatStr( const char* str, &#8230; )<br \/>\n    {<br \/>\n        int len = 0;<br \/>\n        char* buffer = nullptr;<br \/>\n        va_list args;<br \/>\n        va_start(args,str);<br \/>\n        len = _vscprintf( str, args ) + 1; \/\/ + &#8216;\\0&#8217;<br \/>\n        if( len > 0 ) buffer = static_cast< char* >( alloca( len* sizeof(char) ) );<br \/>\n        vsprintf_s( buffer, len, str, args );<br \/>\n        OutputDebugStringA( buffer );<br \/>\n    }<br \/>\n}<\/p>\n<p>void* operator new( size_t sizeInBytes, char* fileName, int lineNumber )<br \/>\n{<br \/>\n    core::ThreadSafePool* pMemPool = core::GetMemoryPool();<br \/>\n    if( pMemPool == nullptr )<br \/>\n    {<br \/>\n        throw std::bad_alloc();<br \/>\n    }<\/p>\n<p>    return pMemPool->MTPoolNew( sizeInBytes, fileName, lineNumber );<br \/>\n}<\/p>\n<p>void operator delete( void* memAddress ) throw()<br \/>\n{<br \/>\n    \/\/c++ standard &#8211; safe delete null<br \/>\n    if( memAddress == nullptr )<br \/>\n        return;<\/p>\n<p>    core::ThreadSafePool* pMemPool = core::GetMemoryPool();<\/p>\n<p>    pMemPool->MTPoolDelete( memAddress );<br \/>\n}<\/p>\n<p>void operator delete( void* memAddress, char*, int ) throw()<br \/>\n{<br \/>\n    delete memAddress;<br \/>\n}<\/p>\n<p>void* operator new( size_t sizeInBytes )<br \/>\n{<br \/>\n    return operator new(sizeInBytes,&#8221;unknown&#8221;,-1);<br \/>\n}<\/p>\n<p>void* operator new[]( size_t sizeInBytes )<br \/>\n{<br \/>\n    return operator new(sizeInBytes,&#8221;unknown&#8221;,-1);<br \/>\n}<\/p>\n<p>void operator delete[]( void* memAddress ) throw()<br \/>\n{<br \/>\n    delete memAddress;<br \/>\n}<\/p>\n<p>void* operator new[]( size_t sizeInBytes, char* fileName, int lineNumber )<br \/>\n{<br \/>\n    return operator new(sizeInBytes, fileName, lineNumber);<br \/>\n}<\/p>\n<p>void operator delete[]( void* memAddress, char*, int ) throw()<br \/>\n{<br \/>\n    delete memAddress;<br \/>\n}<\/p>\n<p>[\/crayon]<\/div><div id=\"squelch-taas-tab-content-0-2\" class=\"\">[crayon lang=&#8221;cpp&#8221;]\n\/*<br \/>\nfile    MallocAllocator.h<br \/>\ndate    12\/18\/2012<br \/>\nauthor  Warsam Osman (based on Stroustrup&#8217;s The C++ Programming Language source)<br \/>\nbrief   Allocator that uses malloc\/free instead of new\/delete<br \/>\n        useful for containers that we explicitly want to avoid using memory pool<br \/>\n*\/<br \/>\n#pragma  once<\/p>\n<p>#ifndef _MALLOC_ALLOCATOR_<br \/>\n#define _MALLOC_ALLOCATOR_<\/p>\n<p>#include <cstdlib><\/p>\n<p>namespace core<br \/>\n{<br \/>\n    template < class T ><br \/>\n    class MallocAllocator<br \/>\n    {<br \/>\n    public:<br \/>\n        typedef T value_type;<br \/>\n        typedef size_t size_type;<br \/>\n        typedef ptrdiff_t difference_type;<\/p>\n<p>        typedef T* pointer;<br \/>\n        typedef const T* const_pointer;<\/p>\n<p>        typedef T&#038; reference;<br \/>\n        typedef const T&#038; const_reference;<\/p>\n<p>        pointer address(reference r) const { return &r; }<br \/>\n        const_pointer address(const_reference r) const { return &r; }<\/p>\n<p>        MallocAllocator() throw() {}<br \/>\n        template<class U><br \/>\n        MallocAllocator( const MallocAllocator<U>&#038; ) throw()  {}<br \/>\n        ~MallocAllocator() throw() {}<\/p>\n<p>        pointer allocate( size_type n, const_pointer hint = 0 ) \/\/space for n Ts<br \/>\n        {<br \/>\n            if (n <= 0)\n                n = 0;\n            else if ((static_cast<size_t>(-1) \/ n) < sizeof(T))\n                throw std::bad_alloc();\n\n            \/\/ allocate storage for n elements of type T\n            return ((T*)::malloc(n * sizeof (T))); \n        }\n\n        void deallocate( pointer p, size_type n ) \/\/deallocate n Ts, don't destroy\n        {\n            free(p);\n        }\n\n        void construct( pointer p, const T&#038; val )   \/\/init *p by val in place\n        { \n            new(p) T(val); \n        } \n        \n        void destroy( pointer p )   \/\/destroy *p but don't deallocate\n        { \n            p->~T();<br \/>\n        }   <\/p>\n<p>        size_type max_size() const throw() \/\/ estimate maximum array size<br \/>\n        {<br \/>\n            size_t count = (size_t)(-1) \/ sizeof(T);<br \/>\n            return (0 < count ? count : 1 );\n        }\n\n        template<class U><br \/>\n        struct rebind { typedef MallocAllocator<U>other; }; \/\/in effect: typedef malloc_alloc<U> other<br \/>\n    };<\/p>\n<p>    template<class T><br \/>\n    inline  bool operator==(const MallocAllocator<T>&#038;, const MallocAllocator<T>&#038;) throw()<br \/>\n    {   \/\/ test for malloc_alloc equality (always true)<br \/>\n        return (true);<br \/>\n    }<\/p>\n<p>    template<class T><br \/>\n    inline  bool operator!=(const MallocAllocator<T>&#038;, const MallocAllocator<T>&#038;) throw()<br \/>\n    {   \/\/ test for malloc_alloc inequality (always false)<br \/>\n        return (false);<br \/>\n    }<br \/>\n}<\/p>\n<p>#endif \/\/_MALLOC_ALLOCATOR_[\/crayon]<\/div><\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>I developed a simple memory manager further outside of class into a thread-safe memory manager that I now use in my multithreaded engine which supports Quake III BSP loading and a custom Character Animation System. The primary motivation for the<span class=\"ellipsis\">&hellip;<\/span><\/p>\n<div class=\"read-more\"><a href=\"https:\/\/www.warsam.com\/?page_id=329\">Read more &#8250;<\/a><\/div>\n<p><!-- end of .read-more --><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"full-width-page.php","meta":[],"_links":{"self":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages\/329"}],"collection":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.warsam.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=329"}],"version-history":[{"count":55,"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages\/329\/revisions"}],"predecessor-version":[{"id":706,"href":"https:\/\/www.warsam.com\/index.php?rest_route=\/wp\/v2\/pages\/329\/revisions\/706"}],"wp:attachment":[{"href":"https:\/\/www.warsam.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=329"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}