1// tPool.cpp 
2// 
3// Memory management pools. These are significantly faster than standard memory allocators. All pool implementations 
4// found here must not call new or delete. The reason for this is to allow a user of the pool to overload operator new. 
5// We would get into an infinite loop if we called new from inside the pool implementation. 
6// 
7// Copyright (c) 2004-2006, 2017, 2020 Tristan Grimmer. 
8// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
9// granted, provided that the above copyright notice and this permission notice appear in all copies. 
10// 
11// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
12// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
13// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
14// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
15// PERFORMANCE OF THIS SOFTWARE. 
16 
17#include "Foundation/tPool.h" 
18#include "Foundation/tMemory.h" 
19 
20 
21void tMem::tFastPool::SlotBlock::Init(int slotSizeInBytes, int numSlots) 
22{ 
23 SlotSize = slotSizeInBytes; 
24 NumSlots = numSlots; 
25 
26 tAssert((!(SlotSize % 4)) && (SlotSize >= 8)); 
27 Slots = (uint8*)tMalloc(SlotSize * NumSlots); 
28 
29 // Next we need to initialize all the slots so they all "point" to the next one. 
30 uint8* currSlot = Slots; 
31 for (int i = 0; i < NumSlots; i++) 
32 { 
33 uint8** currPointerPointer = (uint8**)currSlot; 
34 
35 if (i < (NumSlots-1)) 
36 { 
37 uint8* next = currSlot+SlotSize; 
38 *currPointerPointer = next; 
39 } 
40 else 
41 { 
42 *currPointerPointer = 0; 
43 } 
44 
45 currSlot += SlotSize; 
46 } 
47} 
48 
49 
50tMem::tFastPool::tFastPool(int maxItemSizeInBytes, int initialSlotsInBlock, int slotsPerExpansionBlock, bool threadSafe) : 
51 Blocks(tListMode::UserOwns), 
52 FreeSlot(nullptr), 
53 SlotSize(maxItemSizeInBytes), 
54 SlotsInitialBlock(initialSlotsInBlock), 
55 SlotsPerExpansionBlock(slotsPerExpansionBlock), 
56 ThreadSafe(threadSafe), 
57 PoolMutex() 
58{ 
59 // OK, the slot size must be big enough to hold a pointer. We currently support up to 8 byte pointers. Also, the slot 
60 // size must be a multiple of 4 for alignment purposes. 
61 if (SlotSize < 8) 
62 { 
63 SlotSize = 8; 
64 } 
65 else 
66 { 
67 int mod = SlotSize % 4; 
68 if (mod > 0) 
69 SlotSize += 4 - mod; 
70 } 
71 
72 tAssert(!(SlotSize % 4)); 
73 SlotBlock* initialBlock = (SlotBlock*)tMalloc(sizeof(SlotBlock)); 
74 initialBlock->Init(SlotSize, initialSlotsInBlock); 
75 Blocks.Append(initialBlock); 
76 
77 FreeSlot = initialBlock->Slots; 
78} 
79 
80 
81tMem::tFastPool::~tFastPool() 
82{ 
83 // Statically allocated pools can't assume NumAllocations is 0 even if there are no leaks. 
84 if (NumAllocations == 0) 
85 { 
86 while (SlotBlock* block = Blocks.Remove()) 
87 { 
88 block->Shutdown(); 
89 tFree(block); 
90 } 
91 } 
92} 
93 
94 
95void tMem::tFastPool::GrowPool() 
96{ 
97 // You're only allowed to grow if there is currently no free slot. 
98 tAssert(!FreeSlot); 
99 
100 SlotBlock* expansionBlock = (SlotBlock*)tMalloc(sizeof(SlotBlock)); 
101 expansionBlock->Init(SlotSize, SlotsPerExpansionBlock); 
102 
103 Blocks.Append(expansionBlock); 
104 FreeSlot = expansionBlock->Slots; 
105} 
106 
107 
108#ifdef MEMORY_ALLOCATOR_CHECK 
109void tMem::tFastPool::SanityCheckSlotValid(void* slot) 
110{ 
111 uint64 slotValue = uint64(slot); 
112 
113 bool found = false; 
114 SlotBlock* block = Blocks.Head(); 
115 while (block) 
116 { 
117 uint64 memStart = uint64(block->Slots); 
118 uint64 memSize = block->SlotSize * block->NumSlots; 
119 uint64 position = slotValue - memStart; 
120 
121 if ((position >= 0) && (position < memSize)) 
122 { 
123 found = true; 
124 tAssert((position % block->SlotSize) == 0); 
125 break; 
126 } 
127 
128 block = block->Next(); 
129 } 
130 
131 tAssert(found); 
132} 
133 
134 
135void tMem::tFastPool::SanityCheckSlotFreeable(void* slot) 
136{ 
137 SanityCheckSlotValid(slot); 
138 
139 // This check is really slow since it goes through all the allocated slots. We verify that the 
140 // slot is not in the free list. 
141 #ifdef MEMORY_ALLOCATOR__DEEP_CHECK 
142 uint8* freeSlot = FreeSlot; 
143 while (freeSlot) 
144 { 
145 tAssert(freeSlot != (uint8*)slot); 
146 freeSlot = *((uint8**)freeSlot); 
147 } 
148 #endif 
149} 
150#endif 
151