1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include "stdafx.h"
6
7#include "memorypool.h"
8#include "ex.h"
9
10size_t MemoryPool::GetSize()
11{
12 LIMITED_METHOD_CONTRACT;
13 size_t retval=0;
14
15 Block *block = m_blocks;
16 while (block != NULL)
17 {
18 retval+=(BYTE*)block->elementsEnd-(BYTE*)block->elements;
19 block = block->next;
20 }
21 return retval;
22}
23
24#ifndef DACCESS_COMPILE
25
26BOOL MemoryPool::AddBlock(SIZE_T elementCount)
27{
28 CONTRACTL {
29 NOTHROW;
30 GC_NOTRIGGER;
31 INJECT_FAULT(return FALSE;);
32 } CONTRACTL_END;
33
34 //
35 // Check for arithmetic overflow
36 //
37 S_SIZE_T cbBlockSize = S_SIZE_T(elementCount) * S_SIZE_T(m_elementSize);
38 S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(Block)) + cbBlockSize;
39 if (cbBlockSize.IsOverflow() || cbAllocSize.IsOverflow())
40 return FALSE;
41
42 //
43 // Allocate the new block.
44 //
45
46 Block *block = (Block *) new (nothrow) BYTE [cbAllocSize.Value()];
47
48 if (block == NULL)
49 return FALSE;
50
51 //
52 // Chain all elements together for the free list
53 //
54
55 _ASSERTE(m_freeList == NULL);
56 Element **prev = &m_freeList;
57
58 Element *e = block->elements;
59 Element *eEnd = (Element *) ((BYTE*) block->elements + elementCount*m_elementSize);
60 while (e < eEnd)
61 {
62 *prev = e;
63 prev = &e->next;
64#if _DEBUG
65 DeadBeef(e);
66#endif
67 e = (Element*) ((BYTE*)e + m_elementSize);
68 }
69
70 *prev = NULL;
71
72 //
73 // Initialize the other block fields & link the block into the block list
74 //
75
76 block->elementsEnd = e;
77 block->next = m_blocks;
78 m_blocks = block;
79
80 return TRUE;
81}
82
83void MemoryPool::DeadBeef(Element *element)
84{
85#if _DEBUG
86 CONTRACTL {
87 NOTHROW;
88 GC_NOTRIGGER;
89 CANNOT_TAKE_LOCK;
90 } CONTRACTL_END;
91
92 int *i = &element->deadBeef;
93 int *iEnd = (int*) ((BYTE*)element + m_elementSize);
94 while (i < iEnd)
95 *i++ = (int) 0xdeadbeef;
96#else
97 LIMITED_METHOD_CONTRACT;
98#endif
99}
100
101MemoryPool::MemoryPool(SIZE_T elementSize, SIZE_T initGrowth, SIZE_T initCount)
102 : m_elementSize(elementSize),
103 m_growCount(initGrowth),
104 m_blocks(NULL),
105 m_freeList(NULL)
106{
107 CONTRACTL {
108 if (initCount) THROWS; else NOTHROW;
109 GC_NOTRIGGER;
110 } CONTRACTL_END;
111
112 _ASSERTE(elementSize >= sizeof(Element));
113 _ASSERTE((elementSize & ((sizeof(PVOID)-1))) == 0);
114
115 if (initCount > 0)
116 AddBlock(initCount);
117}
118
119MemoryPool::~MemoryPool()
120{
121 LIMITED_METHOD_CONTRACT;
122 Block *block = m_blocks;
123 while (block != NULL)
124 {
125 Block *next = block->next;
126 delete [] (BYTE*) block;
127 block = next;
128 }
129}
130
131BOOL MemoryPool::IsElement(void *element)
132{
133 LIMITED_METHOD_CONTRACT;
134
135 Block *block = m_blocks;
136 while (block != NULL)
137 {
138 if (element >= block->elements &&
139 element < block->elementsEnd )
140 {
141 return ((BYTE *)element - (BYTE*)block->elements) % m_elementSize == 0;
142 }
143 block = block->next;
144 }
145
146 return FALSE;
147}
148
149BOOL MemoryPool::IsAllocatedElement(void *element)
150{
151 CONTRACTL {
152 NOTHROW;
153 GC_NOTRIGGER;
154 CANNOT_TAKE_LOCK;
155 } CONTRACTL_END;
156
157 if (!IsElement(element))
158 return FALSE;
159
160 //
161 // Now, make sure the element isn't
162 // in the free list.
163 //
164
165#if _DEBUG
166 //
167 // In a debug build, all objects on the free list
168 // will be marked with deadbeef. This means that
169 // if the object is not deadbeef, it's not on the
170 // free list.
171 //
172 // This check will give us decent performance in
173 // a debug build for FreeElement, since we
174 // always expect to return TRUE in that case.
175 //
176
177 if (((Element*)element)->deadBeef != (int) 0xdeadBeef)
178 return TRUE;
179#endif
180
181 Element *f = m_freeList;
182 while (f != NULL)
183 {
184 if (f == element)
185 return FALSE;
186 f = f->next;
187 }
188
189#if _DEBUG
190 //
191 // We should never get here in a debug build, because
192 // all free elements should be deadbeefed.
193 //
194 _ASSERTE(!"Unreachable");
195#endif
196
197 return TRUE;
198}
199
200void *MemoryPool::AllocateElement()
201{
202 CONTRACTL {
203 THROWS;
204 GC_NOTRIGGER;
205 } CONTRACTL_END;
206
207 void *element = AllocateElementNoThrow();
208 if (element == NULL)
209 ThrowOutOfMemory();
210
211 return element;
212}
213
214void *MemoryPool::AllocateElementNoThrow()
215{
216 CONTRACTL {
217 NOTHROW;
218 GC_NOTRIGGER;
219 INJECT_FAULT( return FALSE; );
220 } CONTRACTL_END;
221
222 void *element = m_freeList;
223
224 if (element == NULL)
225 {
226 if (!AddBlock(m_growCount))
227 return NULL;
228
229 m_growCount *= 2;
230 element = m_freeList;
231 }
232
233 // if we come there means that addblock succeeded and m_freelist isn't null anymore
234 PREFIX_ASSUME(m_freeList!= NULL);
235 m_freeList = m_freeList->next;
236
237 return element;
238}
239
240void MemoryPool::FreeElement(void *element)
241{
242 CONTRACTL {
243 NOTHROW;
244 GC_NOTRIGGER;
245 CANNOT_TAKE_LOCK;
246 } CONTRACTL_END;
247
248#if _DEBUG // don't want to do this assert in a non-debug build; it is expensive
249 _ASSERTE(IsAllocatedElement(element));
250#endif
251
252 Element *e = (Element *) element;
253
254#if _DEBUG
255 DeadBeef(e);
256#endif
257
258 e->next = m_freeList;
259 m_freeList = e;
260}
261
262void MemoryPool::FreeAllElements()
263{
264 LIMITED_METHOD_CONTRACT;
265
266 Block *block = m_blocks;
267 while (block != NULL)
268 {
269 Block *next = block->next;
270 delete [] block;
271 block = next;
272 }
273
274 m_freeList = NULL;
275 m_blocks = NULL;
276}
277
278
279
280MemoryPool::Iterator::Iterator(MemoryPool *pool)
281{
282 LIMITED_METHOD_CONTRACT;
283
284 //
285 // Warning! This only works if you haven't freed
286 // any elements.
287 //
288
289 m_next = pool->m_blocks;
290 m_e = NULL;
291 m_eEnd = NULL;
292 m_end = (BYTE*) pool->m_freeList;
293 m_size = pool->m_elementSize;
294}
295
296BOOL MemoryPool::Iterator::Next()
297{
298 LIMITED_METHOD_CONTRACT;
299
300 if (m_e == m_eEnd
301 || (m_e == m_end && m_end != NULL))
302 {
303 if (m_next == NULL)
304 return FALSE;
305 m_e = (BYTE*) m_next->elements;
306 m_eEnd = (BYTE*) m_next->elementsEnd;
307 m_next = m_next->next;
308 if (m_e == m_end)
309 return FALSE;
310 }
311
312 m_e += m_size;
313
314 return TRUE;
315}
316
317#endif
318