xref: /aosp_15_r20/external/deqp/framework/delibs/depool/deMemPool.c (revision 35238bce31c2a825756842865a792f8cf7f89930)
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