forked from TheSuperHackers/GeneralsGameCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGameMemory.h
More file actions
912 lines (743 loc) · 36.3 KB
/
GameMemory.h
File metadata and controls
912 lines (743 loc) · 36.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
/*
** Command & Conquer Generals Zero Hour(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
// Turn off memory pool checkpointing for now.
#ifndef DISABLE_MEMORYPOOL_CHECKPOINTING
#define DISABLE_MEMORYPOOL_CHECKPOINTING 1
#endif
#if defined(RTS_DEBUG) && !defined(MEMORYPOOL_DEBUG_CUSTOM_NEW) && !defined(DISABLE_MEMORYPOOL_DEBUG_CUSTOM_NEW)
#define MEMORYPOOL_DEBUG_CUSTOM_NEW
#endif
//#if defined(RTS_DEBUG) && !defined(MEMORYPOOL_DEBUG) && !defined(DISABLE_MEMORYPOOL_DEBUG)
#if defined(RTS_DEBUG) && !defined(MEMORYPOOL_DEBUG) && !defined(DISABLE_MEMORYPOOL_DEBUG)
#define MEMORYPOOL_DEBUG
#endif
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <new.h>
#include <Utility/stdio_adapter.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
#if !defined(DISABLE_MEMORYPOOL_CHECKPOINTING) || DISABLE_MEMORYPOOL_CHECKPOINTING == 0
#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
#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_CHECKPOINTING
/** 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
};
#endif // MEMORYPOOL_DEBUG
// TheSuperHackers @build xezon 30/03/2025 Define DISABLE_GAMEMEMORY to use a null implementations for Game Memory.
// Useful for address sanitizer checks and other investigations.
// Is included below the macros so that memory pool debug code can still be used.
#ifdef DISABLE_GAMEMEMORY
#include "GameMemoryNull.h"
#else
#ifdef MEMORYPOOL_DEBUG
#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
#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 = nullptr ); ///< 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 = nullptr ); ///< 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 = nullptr );
#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 = nullptr );
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")); \
static MemoryPool *The##ARGCLASS##Pool = TheMemoryPoolFactory->findMemoryPool(ARGPOOLNAME); \
DEBUG_ASSERTCRASH(The##ARGCLASS##Pool, ("Pool \"%s\" not found (did you set it up in initMemoryPools?)", ARGPOOLNAME)); \
DEBUG_ASSERTCRASH(The##ARGCLASS##Pool->getAllocationSize() >= sizeof(ARGCLASS), ("Pool \"%s\" is too small for this class (currently %d, need %d)", 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)", 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")); \
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?)", ARGPOOLNAME)); \
DEBUG_ASSERTCRASH(The##ARGCLASS##Pool->getAllocationSize() >= sizeof(ARGCLASS), ("Pool \"%s\" is too small for this class (currently %d, need %d)", 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)", ARGPOOLNAME, The##ARGCLASS##Pool->getAllocationSize(), sizeof(ARGCLASS))); \
return The##ARGCLASS##Pool; \
}
// ----------------------------------------------------------------------------
#define MEMORY_POOL_GLUE_WITHOUT_GCMP(ARGCLASS) \
protected: \
virtual ~ARGCLASS() override; \
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() override \
{ \
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() override; \
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() override \
{ \
throw ERROR_BUG; \
return 0; \
} \
public: /* include this line at the end to reset visibility to '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:
void *operator new(size_t s) { DEBUG_CRASH(("This should be impossible")); return 0; }
void operator delete(void *p) { DEBUG_CRASH(("This should be impossible")); }
protected:
virtual MemoryPool *getObjectMemoryPool() = 0;
public:
static void deleteInstanceInternal(MemoryPoolObject* mpo)
{
if (mpo)
{
MemoryPool *pool = mpo->getObjectMemoryPool(); // save this, since the dtor will nuke our vtbl
mpo->~MemoryPoolObject(); // it's virtual, so the right one will be called.
pool->freeBlock((void *)mpo);
}
}
};
inline void deleteInstance(MemoryPoolObject* mpo)
{
MemoryPoolObject::deleteInstanceInternal(mpo);
}
// 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);
#if defined(_MSC_VER) && _MSC_VER < 1300
// 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
#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);
};
#endif // DISABLE_GAMEMEMORY
/**
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 = nullptr) : m_mpo(mpo) { }
void hold(MemoryPoolObject *mpo) { DEBUG_ASSERTCRASH(!m_mpo, ("already holding")); m_mpo = mpo; }
void release() { m_mpo = nullptr; }
~MemoryPoolObjectHolder() { deleteInstance(m_mpo); }
};
#define EMPTY_DTOR(CLASS) inline CLASS::~CLASS() { }