1 /*-------------------------------------------------------------------------
2 * drawElements Memory Pool Library
3 * --------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Memory pool management.
22 *//*--------------------------------------------------------------------*/
23
24 #include "deMemPool.h"
25 #include "deMemory.h"
26 #include "deInt32.h"
27
28 #if defined(DE_SUPPORT_FAILING_POOL_ALLOC)
29 #include "deRandom.h"
30 #endif
31
32 #include <stdlib.h>
33 #include <string.h>
34
35 enum
36 {
37 INITIAL_PAGE_SIZE = 128, /*!< Size for the first allocated memory page. */
38 MAX_PAGE_SIZE = 8096, /*!< Maximum size for a memory page. */
39 MEM_PAGE_BASE_ALIGN = 4 /*!< Base alignment guarantee for mem page data ptr. */
40 };
41
42 typedef struct MemPage_s MemPage;
43
44 /*--------------------------------------------------------------------*//*!
45 * \internal
46 * \brief Memory page header.
47 *
48 * Represent a page of memory allocate by a memory pool.
49 *//*--------------------------------------------------------------------*/
50 struct MemPage_s
51 {
52 int capacity;
53 int bytesAllocated;
54
55 MemPage *nextPage;
56 };
57
58 #if defined(DE_SUPPORT_DEBUG_POOLS)
59 typedef struct DebugAlloc_s DebugAlloc;
60
61 struct DebugAlloc_s
62 {
63 void *memPtr;
64 DebugAlloc *next;
65 };
66 #endif
67
68 /*--------------------------------------------------------------------*//*!
69 * \brief Memory pool.
70 *
71 * A pool of memory from which individual memory allocations can be made.
72 * The memory pools don't have a freeing operation for individual allocations,
73 * but rather all of the memory allocated from a pool is freed when the pool
74 * is destroyed.
75 *
76 * The pools can be arranged into a hierarchy. If a pool with children is
77 * destroyed, all of the children are first recursively destroyed and then
78 * the pool itself.
79 *
80 * The memory pools support a feature where individual allocations can be
81 * made to simulate failure (i.e., return null). This can be enabled by
82 * creating the root pool with the deMemPool_createFailingRoot() function.
83 * When the feature is enabled, also creation of sub-pools occasionally
84 * fails.
85 *//*--------------------------------------------------------------------*/
86 struct deMemPool_s
87 {
88 uint32_t flags; /*!< Flags. */
89 deMemPool *parent; /*!< Pointer to parent (null for root pools). */
90 deMemPoolUtil *util; /*!< Utilities (callbacks etc.). */
91 int numChildren; /*!< Number of child pools. */
92 deMemPool *firstChild; /*!< Pointer to first child pool in linked list. */
93 deMemPool *prevPool; /*!< Previous pool in parent's linked list. */
94 deMemPool *nextPool; /*!< Next pool in parent's linked list. */
95
96 MemPage *currentPage; /*!< Current memory page from which to allocate. */
97
98 #if defined(DE_SUPPORT_FAILING_POOL_ALLOC)
99 bool allowFailing; /*!< Is allocation failure simulation enabled? */
100 deRandom failRandom; /*!< RNG for failing allocations. */
101 #endif
102 #if defined(DE_SUPPORT_DEBUG_POOLS)
103 bool enableDebugAllocs; /*!< If true, always allocates using deMalloc(). */
104 DebugAlloc *debugAllocListHead; /*!< List of allocation in debug mode. */
105
106 int lastAllocatedIndex; /*!< Index of last allocated pool (rootPool only). */
107 int allocIndex; /*!< Allocation index (running counter). */
108 #endif
109 #if defined(DE_SUPPORT_POOL_MEMORY_TRACKING)
110 int maxMemoryAllocated; /*!< Maximum amount of memory allocated from pools. */
111 int maxMemoryCapacity; /*!< Maximum amount of memory allocated for pools. */
112 #endif
113 };
114
115 /*--------------------------------------------------------------------*//*!
116 * \internal
117 * \brief Initialize a memory page.
118 * \param page Memory page to initialize.
119 * \param capacity Capacity allocated for the memory page.
120 *//*--------------------------------------------------------------------*/
MemPage_init(MemPage * page,size_t capacity)121 static void MemPage_init(MemPage *page, size_t capacity)
122 {
123 memset(page, 0, sizeof(MemPage));
124 #if defined(DE_DEBUG)
125 memset(page + 1, 0xCD, capacity);
126 #endif
127 page->capacity = (int)capacity;
128 }
129
130 /*--------------------------------------------------------------------*//*!
131 * \internal
132 * \brief Create a new memory page.
133 * \param capacity Capacity for the memory page.
134 * \return The created memory page (or null on failure).
135 *//*--------------------------------------------------------------------*/
MemPage_create(size_t capacity)136 static MemPage *MemPage_create(size_t capacity)
137 {
138 MemPage *page = (MemPage *)deMalloc(sizeof(MemPage) + capacity);
139 if (!page)
140 return DE_NULL;
141
142 DE_ASSERT(deIsAlignedPtr(page + 1, MEM_PAGE_BASE_ALIGN));
143
144 MemPage_init(page, capacity);
145 return page;
146 }
147
148 /*--------------------------------------------------------------------*//*!
149 * \internal
150 * \brief Destroy a memory page.
151 * \param page Memory page to destroy.
152 *//*--------------------------------------------------------------------*/
MemPage_destroy(MemPage * page)153 static void MemPage_destroy(MemPage *page)
154 {
155 #if defined(DE_DEBUG)
156 /* Fill with garbage to hopefully catch dangling pointer bugs easier. */
157 uint8_t *dataPtr = (uint8_t *)(page + 1);
158 memset(dataPtr, 0xCD, (size_t)page->capacity);
159 #endif
160 deFree(page);
161 }
162
163 /*--------------------------------------------------------------------*//*!
164 * \internal
165 * \brief Internal function for creating a new memory pool.
166 * \param parent Parent pool (may be null).
167 * \return The created memory pool (or null on failure).
168 *//*--------------------------------------------------------------------*/
createPoolInternal(deMemPool * parent)169 static deMemPool *createPoolInternal(deMemPool *parent)
170 {
171 deMemPool *pool;
172 MemPage *initialPage;
173
174 #if defined(DE_SUPPORT_FAILING_POOL_ALLOC)
175 if (parent && parent->allowFailing)
176 {
177 if ((deRandom_getUint32(&parent->failRandom) & 16383) <= 15)
178 return DE_NULL;
179 }
180 #endif
181
182 /* Init first page. */
183 initialPage = MemPage_create(INITIAL_PAGE_SIZE);
184 if (!initialPage)
185 return DE_NULL;
186
187 /* Alloc pool from initial page. */
188 DE_ASSERT((int)sizeof(deMemPool) <= initialPage->capacity);
189 pool = (deMemPool *)(initialPage + 1);
190 initialPage->bytesAllocated += (int)sizeof(deMemPool);
191
192 memset(pool, 0, sizeof(deMemPool));
193 pool->currentPage = initialPage;
194
195 /* Register to parent. */
196 pool->parent = parent;
197 if (parent)
198 {
199 parent->numChildren++;
200 if (parent->firstChild)
201 parent->firstChild->prevPool = pool;
202 pool->nextPool = parent->firstChild;
203 parent->firstChild = pool;
204 }
205
206 /* Get utils from parent. */
207 pool->util = parent ? parent->util : DE_NULL;
208
209 #if defined(DE_SUPPORT_FAILING_POOL_ALLOC)
210 pool->allowFailing = parent ? parent->allowFailing : false;
211 deRandom_init(&pool->failRandom, parent ? deRandom_getUint32(&parent->failRandom) : 0x1234abcd);
212 #endif
213
214 #if defined(DE_SUPPORT_DEBUG_POOLS)
215 pool->enableDebugAllocs = parent ? parent->enableDebugAllocs : false;
216 pool->debugAllocListHead = DE_NULL;
217
218 /* Pool allocation index. */
219 {
220 deMemPool *root = pool;
221 while (root->parent)
222 root = root->parent;
223
224 if (pool == root)
225 root->lastAllocatedIndex = 0;
226
227 pool->allocIndex = ++root->lastAllocatedIndex;
228
229 /* \note Put the index of leaking pool here and add a breakpoint to catch leaks easily. */
230 /* if (pool->allocIndex == 51)
231 root = root;*/
232 }
233 #endif
234
235 return pool;
236 }
237
238 /*--------------------------------------------------------------------*//*!
239 * \brief Create a new root memory pool.
240 * \return The created memory pool (or null on failure).
241 *//*--------------------------------------------------------------------*/
deMemPool_createRoot(const deMemPoolUtil * util,uint32_t flags)242 deMemPool *deMemPool_createRoot(const deMemPoolUtil *util, uint32_t flags)
243 {
244 deMemPool *pool = createPoolInternal(DE_NULL);
245 if (!pool)
246 return DE_NULL;
247 #if defined(DE_SUPPORT_FAILING_POOL_ALLOC)
248 if (flags & DE_MEMPOOL_ENABLE_FAILING_ALLOCS)
249 pool->allowFailing = true;
250 #endif
251 #if defined(DE_SUPPORT_DEBUG_POOLS)
252 if (flags & DE_MEMPOOL_ENABLE_DEBUG_ALLOCS)
253 {
254 pool->enableDebugAllocs = true;
255 pool->debugAllocListHead = DE_NULL;
256 }
257 #endif
258 DE_UNREF(flags); /* in case no debug features enabled */
259
260 /* Get copy of utilities. */
261 if (util)
262 {
263 deMemPoolUtil *utilCopy = DE_POOL_NEW(pool, deMemPoolUtil);
264 DE_ASSERT(util->allocFailCallback);
265 if (!utilCopy)
266 {
267 deMemPool_destroy(pool);
268 return DE_NULL;
269 }
270
271 memcpy(utilCopy, util, sizeof(deMemPoolUtil));
272 pool->util = utilCopy;
273 }
274
275 return pool;
276 }
277
278 /*--------------------------------------------------------------------*//*!
279 * \brief Create a sub-pool for an existing memory pool.
280 * \return The created memory pool (or null on failure).
281 *//*--------------------------------------------------------------------*/
deMemPool_create(deMemPool * parent)282 deMemPool *deMemPool_create(deMemPool *parent)
283 {
284 deMemPool *pool;
285 DE_ASSERT(parent);
286 pool = createPoolInternal(parent);
287 if (!pool && parent->util)
288 parent->util->allocFailCallback(parent->util->userPointer);
289 return pool;
290 }
291
292 /*--------------------------------------------------------------------*//*!
293 * \brief Destroy a memory pool.
294 * \param pool Pool to be destroyed.
295 *
296 * Frees all the memory allocated from the pool. Also destroyed any child
297 * pools that the pool has (recursively).
298 *//*--------------------------------------------------------------------*/
deMemPool_destroy(deMemPool * pool)299 void deMemPool_destroy(deMemPool *pool)
300 {
301 deMemPool *iter;
302 deMemPool *iterNext;
303
304 #if defined(DE_SUPPORT_POOL_MEMORY_TRACKING)
305 /* Update memory consumption statistics. */
306 if (pool->parent)
307 {
308 deMemPool *root = pool->parent;
309 while (root->parent)
310 root = root->parent;
311 root->maxMemoryAllocated = deMax32(root->maxMemoryAllocated, deMemPool_getNumAllocatedBytes(root, true));
312 root->maxMemoryCapacity = deMax32(root->maxMemoryCapacity, deMemPool_getCapacity(root, true));
313 }
314 #endif
315
316 /* Destroy all children. */
317 iter = pool->firstChild;
318 while (iter)
319 {
320 iterNext = iter->nextPool;
321 deMemPool_destroy(iter);
322 iter = iterNext;
323 }
324
325 DE_ASSERT(pool->numChildren == 0);
326
327 /* Update pointers. */
328 if (pool->prevPool)
329 pool->prevPool->nextPool = pool->nextPool;
330 if (pool->nextPool)
331 pool->nextPool->prevPool = pool->prevPool;
332
333 if (pool->parent)
334 {
335 deMemPool *parent = pool->parent;
336 if (parent->firstChild == pool)
337 parent->firstChild = pool->nextPool;
338
339 parent->numChildren--;
340 DE_ASSERT(parent->numChildren >= 0);
341 }
342
343 #if defined(DE_SUPPORT_DEBUG_POOLS)
344 /* Free all debug allocations. */
345 if (pool->enableDebugAllocs)
346 {
347 DebugAlloc *alloc = pool->debugAllocListHead;
348 DebugAlloc *next;
349
350 while (alloc)
351 {
352 next = alloc->next;
353 deAlignedFree(alloc->memPtr);
354 deFree(alloc);
355 alloc = next;
356 }
357
358 pool->debugAllocListHead = DE_NULL;
359 }
360 #endif
361
362 /* Free pages. */
363 /* \note Pool itself is allocated from first page, so we must not touch the pool after freeing the page! */
364 {
365 MemPage *page = pool->currentPage;
366 MemPage *nextPage;
367
368 while (page)
369 {
370 nextPage = page->nextPage;
371 MemPage_destroy(page);
372 page = nextPage;
373 }
374 }
375 }
376
377 /*--------------------------------------------------------------------*//*!
378 * \brief Get the number of children for a pool.
379 * \return The number of (immediate) child pools a memory pool has.
380 *//*--------------------------------------------------------------------*/
deMemPool_getNumChildren(const deMemPool * pool)381 int deMemPool_getNumChildren(const deMemPool *pool)
382 {
383 return pool->numChildren;
384 }
385
386 /*--------------------------------------------------------------------*//*!
387 * \brief Get the number of bytes allocated (by the user) from the pool.
388 * \param pool Pool pointer.
389 * \param recurse Is operation recursive to child pools?
390 * \return The number of bytes allocated by the pool (including child pools
391 * if 'recurse' is true).
392 *//*--------------------------------------------------------------------*/
deMemPool_getNumAllocatedBytes(const deMemPool * pool,bool recurse)393 int deMemPool_getNumAllocatedBytes(const deMemPool *pool, bool recurse)
394 {
395 int numAllocatedBytes = 0;
396 MemPage *memPage;
397
398 for (memPage = pool->currentPage; memPage; memPage = memPage->nextPage)
399 numAllocatedBytes += memPage->bytesAllocated;
400
401 if (recurse)
402 {
403 deMemPool *child;
404 for (child = pool->firstChild; child; child = child->nextPool)
405 numAllocatedBytes += deMemPool_getNumAllocatedBytes(child, true);
406 }
407
408 return numAllocatedBytes;
409 }
410
deMemPool_getCapacity(const deMemPool * pool,bool recurse)411 int deMemPool_getCapacity(const deMemPool *pool, bool recurse)
412 {
413 int numCapacityBytes = 0;
414 MemPage *memPage;
415
416 for (memPage = pool->currentPage; memPage; memPage = memPage->nextPage)
417 numCapacityBytes += memPage->capacity;
418
419 if (recurse)
420 {
421 deMemPool *child;
422 for (child = pool->firstChild; child; child = child->nextPool)
423 numCapacityBytes += deMemPool_getCapacity(child, true);
424 }
425
426 return numCapacityBytes;
427 }
428
deMemPool_allocInternal(deMemPool * pool,size_t numBytes,uint32_t alignBytes)429 DE_INLINE void *deMemPool_allocInternal(deMemPool *pool, size_t numBytes, uint32_t alignBytes)
430 {
431 MemPage *curPage = pool->currentPage;
432
433 #if defined(DE_SUPPORT_FAILING_POOL_ALLOC)
434 if (pool->allowFailing)
435 {
436 if ((deRandom_getUint32(&pool->failRandom) & 16383) <= 15)
437 return DE_NULL;
438 }
439 #endif
440
441 #if defined(DE_SUPPORT_DEBUG_POOLS)
442 if (pool->enableDebugAllocs)
443 {
444 DebugAlloc *header = DE_NEW(DebugAlloc);
445 void *ptr = deAlignedMalloc(numBytes, alignBytes);
446
447 if (!header || !ptr)
448 {
449 deFree(header);
450 deAlignedFree(ptr);
451 return DE_NULL;
452 }
453
454 header->memPtr = ptr;
455 header->next = pool->debugAllocListHead;
456 pool->debugAllocListHead = header;
457
458 return ptr;
459 }
460 #endif
461
462 DE_ASSERT(curPage);
463 DE_ASSERT(deIsPowerOfTwo32((int)alignBytes));
464 {
465 void *curPagePtr = (void *)((uint8_t *)(curPage + 1) + curPage->bytesAllocated);
466 void *alignedPtr = deAlignPtr(curPagePtr, alignBytes);
467 size_t alignPadding = (size_t)((uintptr_t)alignedPtr - (uintptr_t)curPagePtr);
468
469 if (numBytes + alignPadding > (size_t)(curPage->capacity - curPage->bytesAllocated))
470 {
471 /* Does not fit to current page. */
472 int maxAlignPadding = deMax32(0, ((int)alignBytes) - MEM_PAGE_BASE_ALIGN);
473 int newPageCapacity =
474 deMax32(deMin32(2 * curPage->capacity, MAX_PAGE_SIZE), ((int)numBytes) + maxAlignPadding);
475
476 curPage = MemPage_create((size_t)newPageCapacity);
477 if (!curPage)
478 return DE_NULL;
479
480 curPage->nextPage = pool->currentPage;
481 pool->currentPage = curPage;
482
483 DE_ASSERT(curPage->bytesAllocated == 0);
484
485 curPagePtr = (void *)(curPage + 1);
486 alignedPtr = deAlignPtr(curPagePtr, alignBytes);
487 alignPadding = (size_t)((uintptr_t)alignedPtr - (uintptr_t)curPagePtr);
488
489 DE_ASSERT(numBytes + alignPadding <= (size_t)curPage->capacity);
490 }
491
492 curPage->bytesAllocated += (int)(numBytes + alignPadding);
493 return alignedPtr;
494 }
495 }
496
497 /*--------------------------------------------------------------------*//*!
498 * \brief Allocate memory from a pool.
499 * \param pool Memory pool to allocate from.
500 * \param numBytes Number of bytes to allocate.
501 * \return Pointer to the allocate memory (or null on failure).
502 *//*--------------------------------------------------------------------*/
deMemPool_alloc(deMemPool * pool,size_t numBytes)503 void *deMemPool_alloc(deMemPool *pool, size_t numBytes)
504 {
505 void *ptr;
506 DE_ASSERT(pool);
507 DE_ASSERT(numBytes > 0);
508 ptr = deMemPool_allocInternal(pool, numBytes, DE_POOL_DEFAULT_ALLOC_ALIGNMENT);
509 if (!ptr && pool->util)
510 pool->util->allocFailCallback(pool->util->userPointer);
511 return ptr;
512 }
513
514 /*--------------------------------------------------------------------*//*!
515 * \brief Allocate aligned memory from a pool.
516 * \param pool Memory pool to allocate from.
517 * \param numBytes Number of bytes to allocate.
518 * \param alignBytes Required alignment in bytes, must be power of two.
519 * \return Pointer to the allocate memory (or null on failure).
520 *//*--------------------------------------------------------------------*/
deMemPool_alignedAlloc(deMemPool * pool,size_t numBytes,uint32_t alignBytes)521 void *deMemPool_alignedAlloc(deMemPool *pool, size_t numBytes, uint32_t alignBytes)
522 {
523 void *ptr;
524 DE_ASSERT(pool);
525 DE_ASSERT(numBytes > 0);
526 DE_ASSERT(deIsPowerOfTwo32((int)alignBytes));
527 ptr = deMemPool_allocInternal(pool, numBytes, alignBytes);
528 DE_ASSERT(deIsAlignedPtr(ptr, alignBytes));
529 if (!ptr && pool->util)
530 pool->util->allocFailCallback(pool->util->userPointer);
531 return ptr;
532 }
533
534 /*--------------------------------------------------------------------*//*!
535 * \brief Duplicate a piece of memory into a memory pool.
536 * \param pool Memory pool to allocate from.
537 * \param ptr Piece of memory to duplicate.
538 * \return Pointer to the copied memory block (or null on failure).
539 *//*--------------------------------------------------------------------*/
deMemPool_memDup(deMemPool * pool,const void * ptr,size_t numBytes)540 void *deMemPool_memDup(deMemPool *pool, const void *ptr, size_t numBytes)
541 {
542 void *newPtr = deMemPool_alloc(pool, numBytes);
543 if (newPtr)
544 memcpy(newPtr, ptr, numBytes);
545 return newPtr;
546 }
547
548 /*--------------------------------------------------------------------*//*!
549 * \brief Duplicate a string into a memory pool.
550 * \param pool Memory pool to allocate from.
551 * \param str String to duplicate.
552 * \return Pointer to the new string (or null on failure).
553 *//*--------------------------------------------------------------------*/
deMemPool_strDup(deMemPool * pool,const char * str)554 char *deMemPool_strDup(deMemPool *pool, const char *str)
555 {
556 size_t len = strlen(str);
557 char *newStr = (char *)deMemPool_alloc(pool, len + 1);
558 if (newStr)
559 memcpy(newStr, str, len + 1);
560 return newStr;
561 }
562
563 /*--------------------------------------------------------------------*//*!
564 * \brief Duplicate a string into a memory pool, with a maximum length.
565 * \param pool Memory pool to allocate from.
566 * \param str String to duplicate.
567 * \param maxLength Maximum number of characters to duplicate.
568 * \return Pointer to the new string (or null on failure).
569 *//*--------------------------------------------------------------------*/
deMemPool_strnDup(deMemPool * pool,const char * str,int maxLength)570 char *deMemPool_strnDup(deMemPool *pool, const char *str, int maxLength)
571 {
572 size_t len = (size_t)deMin32((int)strlen(str), deMax32(0, maxLength));
573 char *newStr = (char *)deMemPool_alloc(pool, len + 1);
574
575 DE_ASSERT(maxLength >= 0);
576
577 if (newStr)
578 {
579 memcpy(newStr, str, len);
580 newStr[len] = 0;
581 }
582 return newStr;
583 }
584
585 #if defined(DE_SUPPORT_POOL_MEMORY_TRACKING)
586
deMemPool_getMaxNumAllocatedBytes(const deMemPool * pool)587 int deMemPool_getMaxNumAllocatedBytes(const deMemPool *pool)
588 {
589 DE_ASSERT(pool && !pool->parent); /* must be root */
590 return deMax32(pool->maxMemoryAllocated, deMemPool_getNumAllocatedBytes(pool, true));
591 }
592
deMemPool_getMaxCapacity(const deMemPool * pool)593 int deMemPool_getMaxCapacity(const deMemPool *pool)
594 {
595 DE_ASSERT(pool && !pool->parent); /* must be root */
596 return deMax32(pool->maxMemoryCapacity, deMemPool_getCapacity(pool, true));
597 }
598
599 #endif
600