/*------------------------------------------------------------------------*/ /*--- A simple pool (memory) allocator implementation. m_poolalloc.c --- */ /*------------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2011-2015 OpenWorks LLP info@open-works.co.uk, Philippe Waroquiers philippe.waroquiers@skynet.be 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "pub_core_basics.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_xarray.h" #include "pub_core_poolalloc.h" /* self */ struct _PoolAlloc { UWord nrRef; /* nr reference to this pool allocator */ UWord elemSzB; /* element size */ UWord nPerPool; /* # elems per pool */ void* (*alloc_fn)(const HChar*, SizeT); /* pool allocator */ const HChar* cc; /* pool allocator's cost centre */ void (*free_fn)(void*); /* pool allocator's free-er */ /* XArray of void* (pointers to pools). The pools themselves. Each element is a pointer to a block of size (elemSzB * nPerPool) bytes. */ XArray* pools; /* next free element. Is a pointer to an element in one of the pools pointed to by .pools */ void* nextFree; }; PoolAlloc* VG_(newPA) ( UWord elemSzB, UWord nPerPool, void* (*alloc_fn)(const HChar*, SizeT), const HChar* cc, void (*free_fn)(void*) ) { PoolAlloc* pa; vg_assert(0 == (elemSzB % sizeof(UWord))); vg_assert(elemSzB >= sizeof(UWord)); vg_assert(nPerPool >= 100); /* let's say */ vg_assert(alloc_fn); vg_assert(cc); vg_assert(free_fn); pa = alloc_fn(cc, sizeof(*pa)); VG_(memset)(pa, 0, sizeof(*pa)); pa->nrRef = 0; pa->elemSzB = elemSzB; pa->nPerPool = nPerPool; pa->pools = NULL; pa->alloc_fn = alloc_fn; pa->cc = cc; pa->free_fn = free_fn; pa->pools = VG_(newXA)( alloc_fn, cc, free_fn, sizeof(void*) ); pa->nextFree = NULL; return pa; } void VG_(deletePA) ( PoolAlloc* pa) { Word i; vg_assert(pa->nrRef == 0); for (i = 0; i < VG_(sizeXA) (pa->pools); i++) pa->free_fn (*(UWord **)VG_(indexXA) ( pa->pools, i )); VG_(deleteXA) (pa->pools); pa->free_fn (pa); } /* The freelist is empty. Allocate a new pool and put all the new elements in it onto the freelist. */ __attribute__((noinline)) static void pal_add_new_pool ( PoolAlloc* pa ) { Word i; UWord* pool; vg_assert(pa); vg_assert(pa->nextFree == NULL); pool = pa->alloc_fn( pa->cc, pa->elemSzB * pa->nPerPool ); /* extend the freelist through the new pool. Place the freelist pointer in the first word of each element. That's why the element size must be at least one word. */ for (i = pa->nPerPool-1; i >= 0; i--) { UChar* elemC = ((UChar*)pool) + i * pa->elemSzB; UWord* elem = (UWord*)elemC; vg_assert(0 == (((UWord)elem) % sizeof(UWord))); *elem = (UWord)pa->nextFree; pa->nextFree = elem; } /* and add to our collection of pools */ VG_(addToXA)( pa->pools, &pool ); } UWord VG_(sizePA) ( PoolAlloc* pa) { vg_assert(pa); return pa->nPerPool * VG_(sizeXA) (pa->pools); } void* VG_(allocEltPA) ( PoolAlloc* pa) { UWord* elem; if (UNLIKELY(pa->nextFree == NULL)) { pal_add_new_pool(pa); } elem = pa->nextFree; pa->nextFree = (void*)*elem; *elem = 0; /* unnecessary, but just to be on the safe side */ return elem; } void VG_(freeEltPA) ( PoolAlloc* pa, void* p) { UWord* elem = (UWord*)p; *elem = (UWord)pa->nextFree; pa->nextFree = elem; } void VG_(addRefPA) ( PoolAlloc* pa) { pa->nrRef++; } UWord VG_(releasePA)(PoolAlloc* pa) { UWord nrRef; vg_assert(pa->nrRef > 0); nrRef = --pa->nrRef; if (nrRef == 0) VG_(deletePA)(pa); return nrRef; }