| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: Memory.h
- //-----------------------------------------------------------------------------
- //
- // Westwood Studios Pacific.
- //
- // Confidential Information
- // Copyright (C); 2001 - All Rights Reserved
- //
- //-----------------------------------------------------------------------------
- //
- // Project: RTS3
- //
- // File name: Memory.h
- //
- // Created: Steven Johnson, August 2001
- //
- // Desc: Memory manager
- //
- //-----------------------------------------------------------------------------
- ///////////////////////////////////////////////////////////////////////////////
- #pragma once
- #ifndef _GAME_MEMORY_H_
- #define _GAME_MEMORY_H_
- // Turn off memory pool checkpointing for now.
- #define DISABLE_MEMORYPOOL_CHECKPOINTING 1
- #if (defined(_DEBUG) || defined(_INTERNAL)) && !defined(MEMORYPOOL_DEBUG_CUSTOM_NEW) && !defined(DISABLE_MEMORYPOOL_DEBUG_CUSTOM_NEW)
- #define MEMORYPOOL_DEBUG_CUSTOM_NEW
- #endif
- //#if (defined(_DEBUG) || defined(_INTERNAL)) && !defined(MEMORYPOOL_DEBUG) && !defined(DISABLE_MEMORYPOOL_DEBUG)
- #if (defined(_DEBUG)) && !defined(MEMORYPOOL_DEBUG) && !defined(DISABLE_MEMORYPOOL_DEBUG)
- #define MEMORYPOOL_DEBUG
- #endif
- // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
- #include <new.h>
- #include <stdio.h>
- #ifdef MEMORYPOOL_OVERRIDE_MALLOC
- #include <malloc.h>
- #endif
- // USER INCLUDES //////////////////////////////////////////////////////////////
- #include "Lib/BaseType.h"
- #include "Common/Debug.h"
- #include "Common/Errors.h"
- // MACROS //////////////////////////////////////////////////////////////////
- #ifdef MEMORYPOOL_DEBUG
- // by default, enable free-block-retention for checkpointing in debug mode
- #ifndef DISABLE_MEMORYPOOL_CHECKPOINTING
- #define MEMORYPOOL_CHECKPOINTING
- #endif
- // by default, enable bounding walls in debug mode (unless we have specifically disabled them)
- #ifndef DISABLE_MEMORYPOOL_BOUNDINGWALL
- #define MEMORYPOOL_BOUNDINGWALL
- #endif
- #define DECLARE_LITERALSTRING_ARG1 const char * debugLiteralTagString
- #define PASS_LITERALSTRING_ARG1 debugLiteralTagString
- #define DECLARE_LITERALSTRING_ARG2 , const char * debugLiteralTagString
- #define PASS_LITERALSTRING_ARG2 , debugLiteralTagString
- #define MP_LOC_SUFFIX /*" [" DEBUG_FILENLINE "]"*/
- #define allocateBlock(ARGLITERAL) allocateBlockImplementation(ARGLITERAL MP_LOC_SUFFIX)
- #define allocateBlockDoNotZero(ARGLITERAL) allocateBlockDoNotZeroImplementation(ARGLITERAL MP_LOC_SUFFIX)
- #define allocateBytes(ARGCOUNT,ARGLITERAL) allocateBytesImplementation(ARGCOUNT, ARGLITERAL MP_LOC_SUFFIX)
- #define allocateBytesDoNotZero(ARGCOUNT,ARGLITERAL) allocateBytesDoNotZeroImplementation(ARGCOUNT, ARGLITERAL MP_LOC_SUFFIX)
- #define newInstanceDesc(ARGCLASS,ARGLITERAL) new(ARGCLASS::ARGCLASS##_GLUE_NOT_IMPLEMENTED, ARGLITERAL MP_LOC_SUFFIX) ARGCLASS
- #define newInstance(ARGCLASS) new(ARGCLASS::ARGCLASS##_GLUE_NOT_IMPLEMENTED, __FILE__) ARGCLASS
- #if !defined(MEMORYPOOL_STACKTRACE) && !defined(DISABLE_MEMORYPOOL_STACKTRACE)
- #define MEMORYPOOL_STACKTRACE
- #endif
- // flags for the memory-report options.
- enum
- {
- #ifdef MEMORYPOOL_CHECKPOINTING
- // ------------------------------------------------------
- // you usually won't use the _REPORT bits directly; see below for more convenient combinations.
- // you must set at least one of the 'allocate' bits.
- _REPORT_CP_ALLOCATED_BEFORE = 0x0001,
- _REPORT_CP_ALLOCATED_BETWEEN = 0x0002,
- _REPORT_CP_ALLOCATED_DONTCARE = (_REPORT_CP_ALLOCATED_BEFORE|_REPORT_CP_ALLOCATED_BETWEEN),
- // you must set at least one of the 'freed' bits.
- _REPORT_CP_FREED_BEFORE = 0x0010,
- _REPORT_CP_FREED_BETWEEN = 0x0020,
- _REPORT_CP_FREED_NEVER = 0x0040, // ie, still in existence
- _REPORT_CP_FREED_DONTCARE = (_REPORT_CP_FREED_BEFORE|_REPORT_CP_FREED_BETWEEN|_REPORT_CP_FREED_NEVER),
- // ------------------------------------------------------
- #endif // MEMORYPOOL_CHECKPOINTING
- #ifdef MEMORYPOOL_STACKTRACE
- /** display the stacktrace for allocation location for all blocks found.
- this bit may be mixed-n-matched with any other flag.
- */
- REPORT_CP_STACKTRACE = 0x0100,
- #endif
-
- /** display stats for each pool, in addition to each block.
- (this is useful for finding suitable allocation counts for the pools.)
- this bit may be mixed-n-matched with any other flag.
- */
- REPORT_POOLINFO = 0x0200,
- /** report on the overall memory situation (including all pools and dma's).
- this bit may be mixed-n-matched with any other flag.
- */
- REPORT_FACTORYINFO = 0x0400,
- /** report on pools that have overflowed their initial allocation.
- this bit may be mixed-n-matched with any other flag.
- */
- REPORT_POOL_OVERFLOW = 0x0800,
- /** simple-n-cheap leak checking */
- REPORT_SIMPLE_LEAKS = 0x1000,
- #ifdef MEMORYPOOL_CHECKPOINTING
- /** report on blocks that were allocated between the checkpoints.
- (don't care if they were freed or not.)
- */
- REPORT_CP_ALLOCATES = (_REPORT_CP_ALLOCATED_BETWEEN | _REPORT_CP_FREED_DONTCARE),
- /** report on blocks that were freed between the checkpoints.
- (don't care when they were allocated.)
- */
- REPORT_CP_FREES = (_REPORT_CP_ALLOCATED_DONTCARE | _REPORT_CP_FREED_BETWEEN),
- /** report on blocks that were allocated between the checkpoints, and still exist
- (note that this reports *potential* leaks -- some such blocks may be desired)
- */
- REPORT_CP_LEAKS = (_REPORT_CP_ALLOCATED_BETWEEN | _REPORT_CP_FREED_NEVER),
- /** report on blocks that existed before checkpoint #1 and still exist now.
- */
- REPORT_CP_LONGTERM = (_REPORT_CP_ALLOCATED_BEFORE | _REPORT_CP_FREED_NEVER),
-
- /** report on blocks that were allocated-and-freed between the checkpoints.
- */
- REPORT_CP_TRANSIENT = (_REPORT_CP_ALLOCATED_BETWEEN | _REPORT_CP_FREED_BETWEEN),
- /** report on all blocks that currently exist
- */
- REPORT_CP_EXISTING = (_REPORT_CP_ALLOCATED_BEFORE | _REPORT_CP_ALLOCATED_BETWEEN | _REPORT_CP_FREED_NEVER),
- /** report on all blocks that have ever existed (!) (or at least, since the last call
- to debugResetCheckpoints)
- */
- REPORT_CP_ALL = (_REPORT_CP_ALLOCATED_DONTCARE | _REPORT_CP_FREED_DONTCARE)
- #endif // MEMORYPOOL_CHECKPOINTING
-
- };
- #else
- #define DECLARE_LITERALSTRING_ARG1
- #define PASS_LITERALSTRING_ARG1
- #define DECLARE_LITERALSTRING_ARG2
- #define PASS_LITERALSTRING_ARG2
- #define allocateBlock(ARGLITERAL) allocateBlockImplementation()
- #define allocateBlockDoNotZero(ARGLITERAL) allocateBlockDoNotZeroImplementation()
- #define allocateBytes(ARGCOUNT,ARGLITERAL) allocateBytesImplementation(ARGCOUNT)
- #define allocateBytesDoNotZero(ARGCOUNT,ARGLITERAL) allocateBytesDoNotZeroImplementation(ARGCOUNT)
- #define newInstanceDesc(ARGCLASS,ARGLITERAL) new(ARGCLASS::ARGCLASS##_GLUE_NOT_IMPLEMENTED) ARGCLASS
- #define newInstance(ARGCLASS) new(ARGCLASS::ARGCLASS##_GLUE_NOT_IMPLEMENTED) ARGCLASS
- #endif
- // FORWARD REFERENCES /////////////////////////////////////////////////////////
- class MemoryPoolSingleBlock;
- class MemoryPoolBlob;
- class MemoryPool;
- class MemoryPoolFactory;
- class DynamicMemoryAllocator;
- class BlockCheckpointInfo;
- // TYPE DEFINES ///////////////////////////////////////////////////////////////
- // ----------------------------------------------------------------------------
- /**
- This class is purely a convenience used to pass optional arguments to initMemoryManager(),
- and by extension, to createDynamicMemoryAllocator(). You can specify how many sub-pools you
- want, what size each is, what the allocation counts are to be, etc. Most apps will
- construct an array of these to pass to initMemoryManager() and never use it elsewhere.
- */
- struct PoolInitRec
- {
- const char *poolName; ///< name of the pool; by convention, "dmaPool_XXX" where XXX is allocationSize
- Int allocationSize; ///< size, in bytes, of the pool.
- Int initialAllocationCount; ///< initial number of blocks to allocate.
- Int overflowAllocationCount; ///< when the pool runs out of space, allocate more blocks in this increment
- };
- enum
- {
- MAX_DYNAMICMEMORYALLOCATOR_SUBPOOLS = 8 ///< The max number of subpools allowed in a DynamicMemoryAllocator
- };
- #ifdef MEMORYPOOL_CHECKPOINTING
- // ----------------------------------------------------------------------------
- /**
- This class exists purely for coding convenience, and should never be used by external code.
- It simply allows MemoryPool and DynamicMemoryAllocator to share checkpoint-related
- code in a seamless way.
- */
- class Checkpointable
- {
- private:
- BlockCheckpointInfo *m_firstCheckpointInfo; ///< head of the linked list of checkpoint infos for this pool/dma
- Bool m_cpiEverFailed; ///< flag to detect if we ran out of memory accumulating checkpoint info.
- protected:
- Checkpointable();
- ~Checkpointable();
- /// create a new checkpoint info and add it to the list.
- BlockCheckpointInfo *debugAddCheckpointInfo(
- const char *debugLiteralTagString,
- Int allocCheckpoint,
- Int blockSize
- );
- public:
- /// dump a checkpoint report to logfile
- void debugCheckpointReport(Int flags, Int startCheckpoint, Int endCheckpoint, const char *poolName);
- /// reset all the checkpoints for this pool/dma
- void debugResetCheckpoints();
- };
- #endif
- // ----------------------------------------------------------------------------
- /**
- A MemoryPool provides a way to efficiently allocate objects of the same (or similar)
- size. We allocate large a large chunk of memory (a "blob") and subdivide it into
- even-size chunks, doling these out as needed. If the first blob gets full, we allocate
- additional blobs as necessary. A given pool can allocate blocks of only one size;
- if you need a different size, you should use a different pool.
- */
- class MemoryPool
- #ifdef MEMORYPOOL_CHECKPOINTING
- : public Checkpointable
- #endif
- {
- private:
- MemoryPoolFactory *m_factory; ///< the factory that created us
- MemoryPool *m_nextPoolInFactory; ///< linked list node, managed by factory
- const char *m_poolName; ///< name of this pool. (literal string; must not be freed)
- Int m_allocationSize; ///< size of the blocks allocated by this pool, in bytes
- Int m_initialAllocationCount; ///< number of blocks to be allocated in initial blob
- Int m_overflowAllocationCount; ///< number of blocks to be allocated in any subsequent blob(s)
- Int m_usedBlocksInPool; ///< total number of blocks in use in the pool.
- Int m_totalBlocksInPool; ///< total number of blocks in all blobs of this pool (used or not).
- Int m_peakUsedBlocksInPool; ///< high-water mark of m_usedBlocksInPool
- MemoryPoolBlob *m_firstBlob; ///< head of linked list: first blob for this pool.
- MemoryPoolBlob *m_lastBlob; ///< tail of linked list: last blob for this pool. (needed for efficiency)
- MemoryPoolBlob *m_firstBlobWithFreeBlocks; ///< first blob in this pool that has at least one unallocated block.
- private:
- /// create a new blob with the given number of blocks.
- MemoryPoolBlob* createBlob(Int allocationCount);
- /// destroy a blob.
- Int freeBlob(MemoryPoolBlob *blob);
- public:
- // 'public' funcs that are really only for use by MemoryPoolFactory
- MemoryPool *getNextPoolInList(); ///< return next pool in linked list
- void addToList(MemoryPool **pHead); ///< add this pool to head of the linked list
- void removeFromList(MemoryPool **pHead); ///< remove this pool from the linked list
- #ifdef MEMORYPOOL_DEBUG
- static void debugPoolInfoReport( MemoryPool *pool, FILE *fp = NULL ); ///< dump a report about this pool to the logfile
- const char *debugGetBlockTagString(void *pBlock); ///< return the tagstring for the given block (assumed to belong to this pool)
- void debugMemoryVerifyPool(); ///< perform internal consistency check on this pool.
- Int debugPoolReportLeaks( const char* owner );
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- void debugResetCheckpoints(); ///< throw away all checkpoint information for this pool.
- #endif
- public:
- MemoryPool();
- /// initialize the given memory pool.
- void init(MemoryPoolFactory *factory, const char *poolName, Int allocationSize, Int initialAllocationCount, Int overflowAllocationCount);
- ~MemoryPool();
- /// allocate a block from this pool. (don't call directly; use allocateBlock() macro)
- void *allocateBlockImplementation(DECLARE_LITERALSTRING_ARG1);
- /// same as allocateBlockImplementation, but memory returned is not zeroed
- void *allocateBlockDoNotZeroImplementation(DECLARE_LITERALSTRING_ARG1);
-
- /// free the block. it is OK to pass null.
- void freeBlock(void *pMem);
- /// return the factory that created (and thus owns) this pool.
- MemoryPoolFactory *getOwningFactory();
- /// return the name of this pool. the result is a literal string and must not be freed.
- const char *getPoolName();
- /// return the block allocation size of this pool.
- Int getAllocationSize();
- /// return the number of free (available) blocks in this pool.
- Int getFreeBlockCount();
- /// return the number of blocks in use in this pool.
- Int getUsedBlockCount();
- /// return the total number of blocks in this pool. [ == getFreeBlockCount() + getUsedBlockCount() ]
- Int getTotalBlockCount();
- /// return the high-water mark for getUsedBlockCount()
- Int getPeakBlockCount();
- /// return the initial allocation count for this pool
- Int getInitialBlockCount();
- Int countBlobsInPool();
- /// if this pool has any empty blobs, return them to the system.
- Int releaseEmpties();
- /// destroy all blocks and blobs in this pool.
- void reset();
- #ifdef MEMORYPOOL_DEBUG
- /// return true iff this block was allocated by this pool.
- Bool debugIsBlockInPool(void *pBlock);
- #endif
- };
- // ----------------------------------------------------------------------------
- /**
- The DynamicMemoryAllocator class is used to handle unpredictably-sized
- allocation requests. It basically allocates a number of (private) MemoryPools,
- then routes request to the smallest-size pool that will satisfy the request.
- (Requests too large for any of the pool are routed to the system memory allocator.)
- You should normally use this in place of malloc/free or (global) new/delete.
- */
- class DynamicMemoryAllocator
- #ifdef MEMORYPOOL_CHECKPOINTING
- : public Checkpointable
- #endif
- {
- private:
- MemoryPoolFactory *m_factory; ///< the factory that created us
- DynamicMemoryAllocator *m_nextDmaInFactory; ///< linked list node, managed by factory
- Int m_numPools; ///< number of subpools (up to MAX_DYNAMICMEMORYALLOCATOR_SUBPOOLS)
- Int m_usedBlocksInDma; ///< total number of blocks allocated, from subpools and "raw"
- MemoryPool *m_pools[MAX_DYNAMICMEMORYALLOCATOR_SUBPOOLS]; ///< the subpools
- MemoryPoolSingleBlock *m_rawBlocks; ///< linked list of "raw" blocks allocated directly from system
- /// return the best pool for the given allocSize, or null if none are suitable
- MemoryPool *findPoolForSize(Int allocSize);
- public:
- // 'public' funcs that are really only for use by MemoryPoolFactory
- DynamicMemoryAllocator *getNextDmaInList(); ///< return next dma in linked list
- void addToList(DynamicMemoryAllocator **pHead); ///< add this dma to the list
- void removeFromList(DynamicMemoryAllocator **pHead); ///< remove this dma from the list
- #ifdef MEMORYPOOL_DEBUG
- Int debugCalcRawBlockBytes(Int *numBlocks); ///< calculate the number of bytes in "raw" (non-subpool) blocks
- void debugMemoryVerifyDma(); ///< perform internal consistency check
- const char *debugGetBlockTagString(void *pBlock); ///< return the tagstring for the given block (assumed to belong to this dma)
- void debugDmaInfoReport( FILE *fp = NULL ); ///< dump a report about this pool to the logfile
- Int debugDmaReportLeaks();
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- void debugResetCheckpoints(); ///< toss all checkpoint information
- #endif
- public:
- DynamicMemoryAllocator();
- /// initialize the dma. pass 0/null for numSubPool/parms to get some reasonable default subpools.
- void init(MemoryPoolFactory *factory, Int numSubPools, const PoolInitRec pParms[]);
-
- ~DynamicMemoryAllocator();
- /// allocate bytes from this pool. (don't call directly; use allocateBytes() macro)
- void *allocateBytesImplementation(Int numBytes DECLARE_LITERALSTRING_ARG2);
- /// like allocateBytesImplementation, but zeroes the memory before returning
- void *allocateBytesDoNotZeroImplementation(Int numBytes DECLARE_LITERALSTRING_ARG2);
- #ifdef MEMORYPOOL_DEBUG
- void debugIgnoreLeaksForThisBlock(void* pBlockPtr);
- #endif
- /// free the bytes. (assumes allocated by this dma.)
- void freeBytes(void* pMem);
- /**
- return the actual number of bytes that would be allocated
- if you tried to allocate the given size. (It will generally be slightly
- larger than you request.) This lets you use extra space if you're gonna get it anyway...
- The idea is that you will call this before doing a memory allocation, to see if
- you got any extra "bonus" space.
- */
- Int getActualAllocationSize(Int numBytes);
- /// destroy all allocations performed by this DMA.
- void reset();
- Int getDmaMemoryPoolCount() const { return m_numPools; }
- MemoryPool* getNthDmaMemoryPool(Int i) const { return m_pools[i]; }
- #ifdef MEMORYPOOL_DEBUG
- /// return true iff this block was allocated by this dma
- Bool debugIsBlockInDma(void *pBlock);
- /// return true iff the pool is a subpool of this dma
- Bool debugIsPoolInDma(MemoryPool *pool);
- #endif // MEMORYPOOL_DEBUG
- };
- // ----------------------------------------------------------------------------
- #ifdef MEMORYPOOL_DEBUG
- enum { MAX_SPECIAL_USED = 256 };
- #endif
- // ----------------------------------------------------------------------------
- /**
- The class that manages all the MemoryPools and DynamicMemoryAllocators.
- Usually you will create exactly one of these (TheMemoryPoolFactory)
- and use it for everything.
- */
- class MemoryPoolFactory
- {
- private:
- MemoryPool *m_firstPoolInFactory; ///< linked list of pools
- DynamicMemoryAllocator *m_firstDmaInFactory; ///< linked list of dmas
- #ifdef MEMORYPOOL_CHECKPOINTING
- Int m_curCheckpoint; ///< most recent checkpoint value
- #endif
- #ifdef MEMORYPOOL_DEBUG
- Int m_usedBytes; ///< total bytes in use
- Int m_physBytes; ///< total bytes allocated to all pools (includes unused blocks)
- Int m_peakUsedBytes; ///< high-water mark of m_usedBytes
- Int m_peakPhysBytes; ///< high-water mark of m_physBytes
- Int m_usedBytesSpecial[MAX_SPECIAL_USED];
- Int m_usedBytesSpecialPeak[MAX_SPECIAL_USED];
- Int m_physBytesSpecial[MAX_SPECIAL_USED];
- Int m_physBytesSpecialPeak[MAX_SPECIAL_USED];
- #endif
- public:
- // 'public' funcs that are really only for use by MemoryPool and friends
- #ifdef MEMORYPOOL_DEBUG
- /// adjust the usedBytes and physBytes variables by the given amoun ts.
- void adjustTotals(const char* tagString, Int usedDelta, Int physDelta);
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
- /// return the current checkpoint value.
- Int getCurCheckpoint() { return m_curCheckpoint; }
- #endif
- public:
-
- MemoryPoolFactory();
- void init();
- ~MemoryPoolFactory();
- /// create a new memory pool with the given settings. if a pool with the given name already exists, return it.
- MemoryPool *createMemoryPool(const PoolInitRec *parms);
- /// overloaded version of createMemoryPool with explicit parms.
- MemoryPool *createMemoryPool(const char *poolName, Int allocationSize, Int initialAllocationCount, Int overflowAllocationCount);
-
- /// return the pool with the given name. if no such pool exists, return null.
- MemoryPool *findMemoryPool(const char *poolName);
- /// destroy the given pool.
- void destroyMemoryPool(MemoryPool *pMemoryPool);
- /// create a DynamicMemoryAllocator with subpools with the given parms.
- DynamicMemoryAllocator *createDynamicMemoryAllocator(Int numSubPools, const PoolInitRec pParms[]);
- /// destroy the given DynamicMemoryAllocator.
- void destroyDynamicMemoryAllocator(DynamicMemoryAllocator *dma);
- /// destroy the contents of all pools and dmas. (the pools and dma's are not destroyed, just reset)
- void reset();
- void memoryPoolUsageReport( const char* filename, FILE *appendToFileInstead = NULL );
- #ifdef MEMORYPOOL_DEBUG
- /// perform internal consistency checking
- void debugMemoryVerify();
- /// return true iff the block was allocated by any pool or dma owned by this factory.
- Bool debugIsBlockInAnyPool(void *pBlock);
- /// return the tag string for the block.
- const char *debugGetBlockTagString(void *pBlock);
- /// dump a report with the given options to the logfile.
- void debugMemoryReport(Int flags, Int startCheckpoint, Int endCheckpoint, FILE *fp = NULL );
- void debugSetInitFillerIndex(Int index);
- #endif
- #ifdef MEMORYPOOL_CHECKPOINTING
-
- /// set a new checkpoint.
- Int debugSetCheckpoint();
- /// reset all checkpoint information.
- void debugResetCheckpoints();
- #endif
- };
- // how many bytes are we allowed to 'waste' per pool allocation before the debug code starts yelling at us...
- #define MEMORY_POOL_OBJECT_ALLOCATION_SLOP 16
- // ----------------------------------------------------------------------------
- #define GCMP_FIND(ARGCLASS, ARGPOOLNAME) \
- private: \
- static MemoryPool *getClassMemoryPool() \
- { \
- /* \
- Note that this static variable will be initialized exactly once: the first time \
- control flows over this section of code. This allows us to neatly resolve the \
- order-of-execution problem for static variables, ensuring this is not executed \
- prior to the initialization of TheMemoryPoolFactory. \
- */ \
- DEBUG_ASSERTCRASH(TheMemoryPoolFactory, ("TheMemoryPoolFactory is NULL\n")); \
- static MemoryPool *The##ARGCLASS##Pool = TheMemoryPoolFactory->findMemoryPool(ARGPOOLNAME); \
- DEBUG_ASSERTCRASH(The##ARGCLASS##Pool, ("Pool \"%s\" not found (did you set it up in initMemoryPools?)\n", ARGPOOLNAME)); \
- DEBUG_ASSERTCRASH(The##ARGCLASS##Pool->getAllocationSize() >= sizeof(ARGCLASS), ("Pool \"%s\" is too small for this class (currently %d, need %d)\n", ARGPOOLNAME, The##ARGCLASS##Pool->getAllocationSize(), sizeof(ARGCLASS))); \
- DEBUG_ASSERTCRASH(The##ARGCLASS##Pool->getAllocationSize() <= sizeof(ARGCLASS)+MEMORY_POOL_OBJECT_ALLOCATION_SLOP, ("Pool \"%s\" is too large for this class (currently %d, need %d)\n", ARGPOOLNAME, The##ARGCLASS##Pool->getAllocationSize(), sizeof(ARGCLASS))); \
- return The##ARGCLASS##Pool; \
- }
- // ----------------------------------------------------------------------------
- #define GCMP_CREATE(ARGCLASS, ARGPOOLNAME, ARGINITIAL, ARGOVERFLOW) \
- private: \
- static MemoryPool *getClassMemoryPool() \
- { \
- /* \
- Note that this static variable will be initialized exactly once: the first time \
- control flows over this section of code. This allows us to neatly resolve the \
- order-of-execution problem for static variables, ensuring this is not executed \
- prior to the initialization of TheMemoryPoolFactory. \
- */ \
- DEBUG_ASSERTCRASH(TheMemoryPoolFactory, ("TheMemoryPoolFactory is NULL\n")); \
- static MemoryPool *The##ARGCLASS##Pool = TheMemoryPoolFactory->createMemoryPool(ARGPOOLNAME, sizeof(ARGCLASS), ARGINITIAL, ARGOVERFLOW); \
- DEBUG_ASSERTCRASH(The##ARGCLASS##Pool, ("Pool \"%s\" not found (did you set it up in initMemoryPools?)\n", ARGPOOLNAME)); \
- DEBUG_ASSERTCRASH(The##ARGCLASS##Pool->getAllocationSize() >= sizeof(ARGCLASS), ("Pool \"%s\" is too small for this class (currently %d, need %d)\n", ARGPOOLNAME, The##ARGCLASS##Pool->getAllocationSize(), sizeof(ARGCLASS))); \
- DEBUG_ASSERTCRASH(The##ARGCLASS##Pool->getAllocationSize() <= sizeof(ARGCLASS)+MEMORY_POOL_OBJECT_ALLOCATION_SLOP, ("Pool \"%s\" is too large for this class (currently %d, need %d)\n", ARGPOOLNAME, The##ARGCLASS##Pool->getAllocationSize(), sizeof(ARGCLASS))); \
- return The##ARGCLASS##Pool; \
- }
-
- // ----------------------------------------------------------------------------
- #define MEMORY_POOL_GLUE_WITHOUT_GCMP(ARGCLASS) \
- protected: \
- virtual ~ARGCLASS(); \
- public: \
- enum ARGCLASS##MagicEnum { ARGCLASS##_GLUE_NOT_IMPLEMENTED = 0 }; \
- public: \
- inline void *operator new(size_t s, ARGCLASS##MagicEnum e DECLARE_LITERALSTRING_ARG2) \
- { \
- DEBUG_ASSERTCRASH(s == sizeof(ARGCLASS), ("The wrong operator new is being called; ensure all objects in the hierarchy have MemoryPoolGlue set up correctly")); \
- return ARGCLASS::getClassMemoryPool()->allocateBlockImplementation(PASS_LITERALSTRING_ARG1); \
- } \
- public: \
- /* \
- Note that this delete operator can't be called directly; it is called \
- only if the analogous new operator is called, AND the constructor \
- throws an exception... \
- */ \
- inline void operator delete(void *p, ARGCLASS##MagicEnum e DECLARE_LITERALSTRING_ARG2) \
- { \
- ARGCLASS::getClassMemoryPool()->freeBlock(p); \
- } \
- protected: \
- /* \
- Make normal new and delete protected, so they can't be called by the outside world. \
- Note that delete is funny, in that it can still be called by the class itself; \
- this is safe but not recommended, for consistency purposes. More problematically, \
- it can be called by another class that has declared itself 'friend' to us. \
- In theory, this shouldn't work, since it may not use the right operator-delete, \
- and thus the wrong memory pool; in practice, it seems the right delete IS called \
- in MSVC -- it seems to make operator delete virtual if the destructor is also virtual. \
- At any rate, this is undocumented behavior as far as I can tell, so we put a big old \
- crash into operator delete telling people to do the right thing and call deleteInstance \
- instead -- it'd be nice if we could catch this at compile time, but catching it at \
- runtime seems to be the best we can do... \
- */ \
- inline void *operator new(size_t s) \
- { \
- DEBUG_CRASH(("This operator new should normally never be called... please use new(char*) instead.")); \
- DEBUG_ASSERTCRASH(s == sizeof(ARGCLASS), ("The wrong operator new is being called; ensure all objects in the hierarchy have MemoryPoolGlue set up correctly")); \
- throw ERROR_BUG; \
- return 0; \
- } \
- inline void operator delete(void *p) \
- { \
- DEBUG_CRASH(("Please call deleteInstance instead of delete.")); \
- ARGCLASS::getClassMemoryPool()->freeBlock(p); \
- } \
- private: \
- virtual MemoryPool *getObjectMemoryPool() \
- { \
- return ARGCLASS::getClassMemoryPool(); \
- } \
- public: /* include this line at the end to reset visibility to 'public' */
- // ----------------------------------------------------------------------------
- #define MEMORY_POOL_GLUE(ARGCLASS, ARGPOOLNAME) \
- MEMORY_POOL_GLUE_WITHOUT_GCMP(ARGCLASS) \
- GCMP_FIND(ARGCLASS, ARGPOOLNAME)
- // ----------------------------------------------------------------------------
- #define MEMORY_POOL_GLUE_WITH_EXPLICIT_CREATE(ARGCLASS, ARGPOOLNAME, ARGINITIAL, ARGOVERFLOW) \
- MEMORY_POOL_GLUE_WITHOUT_GCMP(ARGCLASS) \
- GCMP_CREATE(ARGCLASS, ARGPOOLNAME, ARGINITIAL, ARGOVERFLOW)
- // ----------------------------------------------------------------------------
- #define MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ARGCLASS, ARGPOOLNAME) \
- MEMORY_POOL_GLUE_WITHOUT_GCMP(ARGCLASS) \
- GCMP_CREATE(ARGCLASS, ARGPOOLNAME, -1, -1)
- // ----------------------------------------------------------------------------
- // this is the version for an Abstract Base Class, which will never be instantiated...
- #define MEMORY_POOL_GLUE_ABC(ARGCLASS) \
- protected: \
- virtual ~ARGCLASS(); \
- public: \
- enum ARGCLASS##MagicEnum { ARGCLASS##_GLUE_NOT_IMPLEMENTED = 0 }; \
- protected: \
- inline void *operator new(size_t s, ARGCLASS##MagicEnum e DECLARE_LITERALSTRING_ARG2) \
- { \
- DEBUG_CRASH(("this should be impossible to call (abstract base class)")); \
- DEBUG_ASSERTCRASH(s == sizeof(ARGCLASS), ("The wrong operator new is being called; ensure all objects in the hierarchy have MemoryPoolGlue set up correctly")); \
- throw ERROR_BUG; \
- return 0; \
- } \
- protected: \
- inline void operator delete(void *p, ARGCLASS##MagicEnum e DECLARE_LITERALSTRING_ARG2) \
- { \
- DEBUG_CRASH(("this should be impossible to call (abstract base class)")); \
- } \
- protected: \
- inline void *operator new(size_t s) \
- { \
- DEBUG_CRASH(("this should be impossible to call (abstract base class)")); \
- DEBUG_ASSERTCRASH(s == sizeof(ARGCLASS), ("The wrong operator new is being called; ensure all objects in the hierarchy have MemoryPoolGlue set up correctly")); \
- throw ERROR_BUG; \
- return 0; \
- } \
- inline void operator delete(void *p) \
- { \
- DEBUG_CRASH(("this should be impossible to call (abstract base class)")); \
- } \
- private: \
- virtual MemoryPool *getObjectMemoryPool() \
- { \
- throw ERROR_BUG; \
- return 0; \
- } \
- public: /* include this line at the end to reset visibility to 'public' */
- // ----------------------------------------------------------------------------
- /**
- Sometimes you want to make a class's destructor protected so that it can only
- be destroyed under special circumstances. MemoryPoolObject short-circuits this
- by making the destructor always be protected, and the true delete technique
- (namely, deleteInstance) always public by default. You can simulate the behavior
- you really want by including this macro
- */
- #define MEMORY_POOL_DELETEINSTANCE_VISIBILITY(ARGVIS)\
- ARGVIS: void deleteInstance() { MemoryPoolObject::deleteInstance(); } public:
- // ----------------------------------------------------------------------------
- /**
- This class is provided as a simple and safe way to integrate C++ object allocation
- into MemoryPool usage. To use it, you must have your class inherit from
- MemoryPoolObject, then put the macro MEMORY_POOL_GLUE(MyClassName, "MyPoolName")
- at the start of your class definition. (This does not create the pool itself -- you
- must create that manually using MemoryPoolFactory::createMemoryPool)
- */
- class MemoryPoolObject
- {
- protected:
- /** ensure that all destructors are virtual */
- virtual ~MemoryPoolObject() { }
- protected:
- inline void *operator new(size_t s) { DEBUG_CRASH(("This should be impossible")); return 0; }
- inline void operator delete(void *p) { DEBUG_CRASH(("This should be impossible")); }
- protected:
- virtual MemoryPool *getObjectMemoryPool() = 0;
-
- public:
- void deleteInstance()
- {
- if (this)
- {
- MemoryPool *pool = this->getObjectMemoryPool(); // save this, since the dtor will nuke our vtbl
- this->~MemoryPoolObject(); // it's virtual, so the right one will be called.
- pool->freeBlock((void *)this);
- }
- }
- };
- // ----------------------------------------------------------------------------
- /**
- A simple utility class to ensure exception safety; this holds a MemoryPoolObject
- and deletes it in its destructor. Especially useful for iterators!
- */
- class MemoryPoolObjectHolder
- {
- private:
- MemoryPoolObject *m_mpo;
- public:
- MemoryPoolObjectHolder(MemoryPoolObject *mpo = NULL) : m_mpo(mpo) { }
- void hold(MemoryPoolObject *mpo) { DEBUG_ASSERTCRASH(!m_mpo, ("already holding")); m_mpo = mpo; }
- void release() { m_mpo = NULL; }
- ~MemoryPoolObjectHolder() { m_mpo->deleteInstance(); }
- };
- // INLINING ///////////////////////////////////////////////////////////////////
- // ----------------------------------------------------------------------------
- inline MemoryPoolFactory *MemoryPool::getOwningFactory() { return m_factory; }
- inline MemoryPool *MemoryPool::getNextPoolInList() { return m_nextPoolInFactory; }
- inline const char *MemoryPool::getPoolName() { return m_poolName; }
- inline Int MemoryPool::getAllocationSize() { return m_allocationSize; }
- inline Int MemoryPool::getFreeBlockCount() { return getTotalBlockCount() - getUsedBlockCount(); }
- inline Int MemoryPool::getUsedBlockCount() { return m_usedBlocksInPool; }
- inline Int MemoryPool::getTotalBlockCount() { return m_totalBlocksInPool; }
- inline Int MemoryPool::getPeakBlockCount() { return m_peakUsedBlocksInPool; }
- inline Int MemoryPool::getInitialBlockCount() { return m_initialAllocationCount; }
- // ----------------------------------------------------------------------------
- inline DynamicMemoryAllocator *DynamicMemoryAllocator::getNextDmaInList() { return m_nextDmaInFactory; }
- // EXTERNALS //////////////////////////////////////////////////////////////////
- /**
- Initialize the memory manager. Construct a new MemoryPoolFactory and
- DynamicMemoryAllocator and store 'em in the singletons of the relevant
- names.
- */
- extern void initMemoryManager();
- /**
- return true if initMemoryManager() has been called.
- return false if only preMainInitMemoryManager() has been called.
- */
- extern Bool isMemoryManagerOfficiallyInited();
- /**
- similar to initMemoryManager, but this should be used if the memory manager must be initialized
- prior to main() (e.g., from a static constructor). If preMainInitMemoryManager() is called prior
- to initMemoryManager(), then subsequent calls to either are quietly ignored, AS IS any subsequent
- call to shutdownMemoryManager() [since there's no safe way to ensure that shutdownMemoryManager
- will execute after all static destructors].
- (Note: this function is actually not externally visible, but is documented here for clarity.)
- */
- /* extern void preMainInitMemoryManager(); */
- /**
- Shut down the memory manager. Throw away TheMemoryPoolFactory and
- TheDynamicMemoryAllocator.
- */
- extern void shutdownMemoryManager();
- extern MemoryPoolFactory *TheMemoryPoolFactory;
- extern DynamicMemoryAllocator *TheDynamicMemoryAllocator;
- /**
- This function is declared in this header, but is not defined anywhere -- you must provide
- it in your code. It is called by initMemoryManager() or preMainInitMemoryManager() in order
- to get the specifics of the subpool for the dynamic memory allocator. (If you just want
- some defaults, set both return arguments to zero.) The reason for this odd setup is that
- we may need to init the memory manager prior to main() [due to static C++ ctors] and
- this allows us a way to get the necessary parameters.
- */
- extern void userMemoryManagerGetDmaParms(Int *numSubPools, const PoolInitRec **pParms);
- /**
- This function is declared in this header, but is not defined anywhere -- you must provide
- it in your code. It is called by initMemoryManager() or preMainInitMemoryManager() in order
- to initialize the pools to be used. (You can define an empty function if you like.)
- */
- extern void userMemoryManagerInitPools();
- /**
- This function is declared in this header, but is not defined anywhere -- you must provide
- it in your code. It is called by createMemoryPool to adjust the allocation size(s) for a
- given pool. Note that the counts are in-out parms!
- */
- extern void userMemoryAdjustPoolSize(const char *poolName, Int& initialAllocationCount, Int& overflowAllocationCount);
- #ifdef __cplusplus
- #ifndef _OPERATOR_NEW_DEFINED_
- #define _OPERATOR_NEW_DEFINED_
- extern void * __cdecl operator new (size_t size);
- extern void __cdecl operator delete (void *p);
- extern void * __cdecl operator new[] (size_t size);
- extern void __cdecl operator delete[] (void *p);
- // additional overloads to account for VC/MFC funky versions
- extern void* __cdecl operator new(size_t nSize, const char *, int);
- extern void __cdecl operator delete(void *, const char *, int);
- extern void* __cdecl operator new[](size_t nSize, const char *, int);
- extern void __cdecl operator delete[](void *, const char *, int);
- // additional overloads for 'placement new'
- //inline void* __cdecl operator new (size_t s, void *p) { return p; }
- //inline void __cdecl operator delete (void *, void *p) { }
- inline void* __cdecl operator new[] (size_t s, void *p) { return p; }
- inline void __cdecl operator delete[] (void *, void *p) { }
- #endif
- #ifdef MEMORYPOOL_DEBUG_CUSTOM_NEW
- #define MSGNEW(MSG) new(MSG, 0)
- #define NEW new(__FILE__, __LINE__)
- #else
- #define MSGNEW(MSG) new
- #define NEW new
- #endif
- #endif
- class STLSpecialAlloc
- {
- public:
- static void* allocate(size_t __n);
- static void deallocate(void* __p, size_t);
- };
- #define EMPTY_DTOR(CLASS) inline CLASS::~CLASS() { }
- #endif // _GAME_MEMORY_H_
|