123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954 |
- /* Alternative malloc implementation for multiple threads without
- lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
- Boost Software License - Version 1.0 - August 17th, 2003
- Permission is hereby granted, free of charge, to any person or organization
- obtaining a copy of the software and accompanying documentation covered by
- this license (the "Software") to use, reproduce, display, distribute,
- execute, and transmit the Software, and to prepare derivative works of the
- Software, and to permit third-parties to whom the Software is furnished to
- do so, all subject to the following:
- The copyright notices in the Software and this entire statement, including
- the above license grant, this restriction and the following disclaimer,
- must be included in all copies of the Software, in whole or in part, and
- all derivative works of the Software, unless such copies or derivative
- works are solely in the form of machine-executable object code generated by
- a source language processor.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
- #ifdef _MSC_VER
- /* Enable full aliasing on MSVC */
- /*#pragma optimize("a", on)*/
- #endif
- /*#define FULLSANITYCHECKS*/
- #include "nedmalloc.h"
- #if defined(WIN32)
- #include <malloc.h>
- #endif
- #define MSPACES 1
- #define ONLY_MSPACES 1
- #ifndef USE_LOCKS
- #define USE_LOCKS 1
- #endif
- #define FOOTERS 1 /* Need to enable footers so frees lock the right mspace */
- #undef DEBUG /* dlmalloc wants DEBUG either 0 or 1 */
- #ifdef _DEBUG
- #define DEBUG 1
- #else
- #define DEBUG 0
- #endif
- #ifdef NDEBUG /* Disable assert checking on release builds */
- #undef DEBUG
- #endif
- /* The default of 64Kb means we spend too much time kernel-side */
- #ifndef DEFAULT_GRANULARITY
- #define DEFAULT_GRANULARITY (1*1024*1024)
- #endif
- /*#define USE_SPIN_LOCKS 0*/
- /*#define FORCEINLINE*/
- #include "malloc.c.h"
- #ifdef NDEBUG /* Disable assert checking on release builds */
- #undef DEBUG
- #endif
- /* The maximum concurrent threads in a pool possible */
- #ifndef MAXTHREADSINPOOL
- #define MAXTHREADSINPOOL 16
- #endif
- /* The maximum number of threadcaches which can be allocated */
- #ifndef THREADCACHEMAXCACHES
- #define THREADCACHEMAXCACHES 256
- #endif
- /* The maximum size to be allocated from the thread cache */
- #ifndef THREADCACHEMAX
- #define THREADCACHEMAX 8192
- #endif
- #if 0
- /* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
- #define THREADCACHEMAXBINS ((13-4)*2)
- #else
- /* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
- #define THREADCACHEMAXBINS (13-4)
- #endif
- /* Point at which the free space in a thread cache is garbage collected */
- #ifndef THREADCACHEMAXFREESPACE
- #define THREADCACHEMAXFREESPACE (512*1024)
- #endif
- #ifdef WIN32
- #define TLSVAR DWORD
- #define TLSALLOC(k) (*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
- #define TLSFREE(k) (!TlsFree(k))
- #define TLSGET(k) TlsGetValue(k)
- #define TLSSET(k, a) (!TlsSetValue(k, a))
- #ifdef DEBUG
- static LPVOID ChkedTlsGetValue(DWORD idx)
- {
- LPVOID ret=TlsGetValue(idx);
- assert(S_OK==GetLastError());
- return ret;
- }
- #undef TLSGET
- #define TLSGET(k) ChkedTlsGetValue(k)
- #endif
- #else
- #define TLSVAR pthread_key_t
- #define TLSALLOC(k) pthread_key_create(k, 0)
- #define TLSFREE(k) pthread_key_delete(k)
- #define TLSGET(k) pthread_getspecific(k)
- #define TLSSET(k, a) pthread_setspecific(k, a)
- #endif
- #if 0
- /* Only enable if testing with valgrind. Causes misoperation */
- #define mspace_malloc(p, s) malloc(s)
- #define mspace_realloc(p, m, s) realloc(m, s)
- #define mspace_calloc(p, n, s) calloc(n, s)
- #define mspace_free(p, m) free(m)
- #endif
- #if defined(__cplusplus)
- #if !defined(NO_NED_NAMESPACE)
- namespace nedalloc {
- #else
- extern "C" {
- #endif
- #endif
- size_t nedblksize(void *mem) THROWSPEC
- {
- #if 0
- /* Only enable if testing with valgrind. Causes misoperation */
- return THREADCACHEMAX;
- #else
- if(mem)
- {
- mchunkptr p=mem2chunk(mem);
- assert(cinuse(p)); /* If this fails, someone tried to free a block twice */
- if(cinuse(p))
- return chunksize(p)-overhead_for(p);
- }
- return 0;
- #endif
- }
- void nedsetvalue(void *v) THROWSPEC { nedpsetvalue(0, v); }
- void * nedmalloc(size_t size) THROWSPEC { return nedpmalloc(0, size); }
- void * nedcalloc(size_t no, size_t size) THROWSPEC { return nedpcalloc(0, no, size); }
- void * nedrealloc(void *mem, size_t size) THROWSPEC { return nedprealloc(0, mem, size); }
- void nedfree(void *mem) THROWSPEC { nedpfree(0, mem); }
- void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
- #if !NO_MALLINFO
- struct mallinfo nedmallinfo(void) THROWSPEC { return nedpmallinfo(0); }
- #endif
- int nedmallopt(int parno, int value) THROWSPEC { return nedpmallopt(0, parno, value); }
- int nedmalloc_trim(size_t pad) THROWSPEC { return nedpmalloc_trim(0, pad); }
- void nedmalloc_stats(void) THROWSPEC { nedpmalloc_stats(0); }
- size_t nedmalloc_footprint(void) THROWSPEC { return nedpmalloc_footprint(0); }
- void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC { return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
- void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC { return nedpindependent_comalloc(0, elems, sizes, chunks); }
- struct threadcacheblk_t;
- typedef struct threadcacheblk_t threadcacheblk;
- struct threadcacheblk_t
- { /* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
- #ifdef FULLSANITYCHECKS
- unsigned int magic;
- #endif
- unsigned int lastUsed, size;
- threadcacheblk *next, *prev;
- };
- typedef struct threadcache_t
- {
- #ifdef FULLSANITYCHECKS
- unsigned int magic1;
- #endif
- int mymspace; /* Last mspace entry this thread used */
- long threadid;
- unsigned int mallocs, frees, successes;
- size_t freeInCache; /* How much free space is stored in this cache */
- threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
- #ifdef FULLSANITYCHECKS
- unsigned int magic2;
- #endif
- } threadcache;
- struct nedpool_t
- {
- MLOCK_T mutex;
- void *uservalue;
- int threads; /* Max entries in m to use */
- threadcache *caches[THREADCACHEMAXCACHES];
- TLSVAR mycache; /* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
- mstate m[MAXTHREADSINPOOL+1]; /* mspace entries for this pool */
- };
- static nedpool syspool;
- static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
- { /* 8=1000 16=10000 20=10100 24=11000 32=100000 48=110000 4096=1000000000000 */
- unsigned int topbit, size=(unsigned int)(_size>>4);
- /* 16=1 20=1 24=1 32=10 48=11 64=100 96=110 128=1000 4096=100000000 */
- #if defined(__GNUC__)
- topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
- #elif defined(_MSC_VER) && _MSC_VER>=1300
- {
- unsigned long bsrTopBit;
- _BitScanReverse(&bsrTopBit, size);
- topbit = bsrTopBit;
- }
- #else
- #if 0
- union {
- unsigned asInt[2];
- double asDouble;
- };
- int n;
- asDouble = (double)size + 0.5;
- topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
- #else
- {
- unsigned int x=size;
- x = x | (x >> 1);
- x = x | (x >> 2);
- x = x | (x >> 4);
- x = x | (x >> 8);
- x = x | (x >>16);
- x = ~x;
- x = x - ((x >> 1) & 0x55555555);
- x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
- x = (x + (x >> 4)) & 0x0F0F0F0F;
- x = x + (x << 8);
- x = x + (x << 16);
- topbit=31 - (x >> 24);
- }
- #endif
- #endif
- return topbit;
- }
- #ifdef FULLSANITYCHECKS
- static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
- {
- assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
- if(ptr[0] && ptr[1])
- {
- assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
- assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
- assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
- assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
- assert(!ptr[0]->prev);
- assert(!ptr[1]->next);
- if(ptr[0]==ptr[1])
- {
- assert(!ptr[0]->next);
- assert(!ptr[1]->prev);
- }
- }
- }
- static void tcfullsanitycheck(threadcache *tc) THROWSPEC
- {
- threadcacheblk **tcbptr=tc->bins;
- int n;
- for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
- {
- threadcacheblk *b, *ob=0;
- tcsanitycheck(tcbptr);
- for(b=tcbptr[0]; b; ob=b, b=b->next)
- {
- assert(*(unsigned int *) "NEDN"==b->magic);
- assert(!ob || ob->next==b);
- assert(!ob || b->prev==ob);
- }
- }
- }
- #endif
- static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
- {
- #ifdef FULLSANITYCHECKS
- tcfullsanitycheck(tc);
- #endif
- if(tc->freeInCache)
- {
- threadcacheblk **tcbptr=tc->bins;
- int n;
- for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
- {
- threadcacheblk **tcb=tcbptr+1; /* come from oldest end of list */
- /*tcsanitycheck(tcbptr);*/
- for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
- {
- threadcacheblk *f=*tcb;
- size_t blksize=f->size; /*nedblksize(f);*/
- assert(blksize<=nedblksize(f));
- assert(blksize);
- #ifdef FULLSANITYCHECKS
- assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
- #endif
- *tcb=(*tcb)->prev;
- if(*tcb)
- (*tcb)->next=0;
- else
- *tcbptr=0;
- tc->freeInCache-=blksize;
- assert((long) tc->freeInCache>=0);
- mspace_free(0, f);
- /*tcsanitycheck(tcbptr);*/
- }
- }
- }
- #ifdef FULLSANITYCHECKS
- tcfullsanitycheck(tc);
- #endif
- }
- static void DestroyCaches(nedpool *p) THROWSPEC
- {
- if(p->caches)
- {
- threadcache *tc;
- int n;
- for(n=0; n<THREADCACHEMAXCACHES; n++)
- {
- if((tc=p->caches[n]))
- {
- tc->frees++;
- RemoveCacheEntries(p, tc, 0);
- assert(!tc->freeInCache);
- tc->mymspace=-1;
- tc->threadid=0;
- mspace_free(0, tc);
- p->caches[n]=0;
- }
- }
- }
- }
- static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
- {
- threadcache *tc=0;
- int n, end;
- ACQUIRE_LOCK(&p->mutex);
- for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
- if(THREADCACHEMAXCACHES==n)
- { /* List exhausted, so disable for this thread */
- RELEASE_LOCK(&p->mutex);
- return 0;
- }
- tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
- if(!tc)
- {
- RELEASE_LOCK(&p->mutex);
- return 0;
- }
- #ifdef FULLSANITYCHECKS
- tc->magic1=*(unsigned int *)"NEDMALC1";
- tc->magic2=*(unsigned int *)"NEDMALC2";
- #endif
- tc->threadid=(long)(size_t)CURRENT_THREAD;
- for(end=0; p->m[end]; end++);
- tc->mymspace=tc->threadid % end;
- RELEASE_LOCK(&p->mutex);
- if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
- return tc;
- }
- static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
- {
- void *ret=0;
- unsigned int bestsize;
- unsigned int idx=size2binidx(*size);
- size_t blksize=0;
- threadcacheblk *blk, **binsptr;
- #ifdef FULLSANITYCHECKS
- tcfullsanitycheck(tc);
- #endif
- /* Calculate best fit bin size */
- bestsize=1<<(idx+4);
- #if 0
- /* Finer grained bin fit */
- idx<<=1;
- if(*size>bestsize)
- {
- idx++;
- bestsize+=bestsize>>1;
- }
- if(*size>bestsize)
- {
- idx++;
- bestsize=1<<(4+(idx>>1));
- }
- #else
- if(*size>bestsize)
- {
- idx++;
- bestsize<<=1;
- }
- #endif
- assert(bestsize>=*size);
- if(*size<bestsize) *size=bestsize;
- assert(*size<=THREADCACHEMAX);
- assert(idx<=THREADCACHEMAXBINS);
- binsptr=&tc->bins[idx*2];
- /* Try to match close, but move up a bin if necessary */
- blk=*binsptr;
- if(!blk || blk->size<*size)
- { /* Bump it up a bin */
- if(idx<THREADCACHEMAXBINS)
- {
- idx++;
- binsptr+=2;
- blk=*binsptr;
- }
- }
- if(blk)
- {
- blksize=blk->size; /*nedblksize(blk);*/
- assert(nedblksize(blk)>=blksize);
- assert(blksize>=*size);
- if(blk->next)
- blk->next->prev=0;
- *binsptr=blk->next;
- if(!*binsptr)
- binsptr[1]=0;
- #ifdef FULLSANITYCHECKS
- blk->magic=0;
- #endif
- assert(binsptr[0]!=blk && binsptr[1]!=blk);
- assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
- /*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
- ret=(void *) blk;
- }
- ++tc->mallocs;
- if(ret)
- {
- assert(blksize>=*size);
- ++tc->successes;
- tc->freeInCache-=blksize;
- assert((long) tc->freeInCache>=0);
- }
- #if defined(DEBUG) && 0
- if(!(tc->mallocs & 0xfff))
- {
- printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
- (float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
- }
- #endif
- #ifdef FULLSANITYCHECKS
- tcfullsanitycheck(tc);
- #endif
- return ret;
- }
- static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
- {
- unsigned int age=THREADCACHEMAXFREESPACE/8192;
- /*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
- while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
- {
- RemoveCacheEntries(p, tc, age);
- /*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
- age>>=1;
- }
- /*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
- }
- static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
- {
- unsigned int bestsize;
- unsigned int idx=size2binidx(size);
- threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
- assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
- #ifdef DEBUG
- { /* Make sure this is a valid memory block */
- mchunkptr p = mem2chunk(mem);
- mstate fm = get_mstate_for(p);
- if (!ok_magic(fm)) {
- USAGE_ERROR_ACTION(fm, p);
- return;
- }
- }
- #endif
- #ifdef FULLSANITYCHECKS
- tcfullsanitycheck(tc);
- #endif
- /* Calculate best fit bin size */
- bestsize=1<<(idx+4);
- #if 0
- /* Finer grained bin fit */
- idx<<=1;
- if(size>bestsize)
- {
- unsigned int biggerbestsize=bestsize+bestsize<<1;
- if(size>=biggerbestsize)
- {
- idx++;
- bestsize=biggerbestsize;
- }
- }
- #endif
- if(bestsize!=size) /* dlmalloc can round up, so we round down to preserve indexing */
- size=bestsize;
- binsptr=&tc->bins[idx*2];
- assert(idx<=THREADCACHEMAXBINS);
- if(tck==*binsptr)
- {
- fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
- abort();
- }
- #ifdef FULLSANITYCHECKS
- tck->magic=*(unsigned int *) "NEDN";
- #endif
- tck->lastUsed=++tc->frees;
- tck->size=(unsigned int) size;
- tck->next=*binsptr;
- tck->prev=0;
- if(tck->next)
- tck->next->prev=tck;
- else
- binsptr[1]=tck;
- assert(!*binsptr || (*binsptr)->size==tck->size);
- *binsptr=tck;
- assert(tck==tc->bins[idx*2]);
- assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
- /*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
- tc->freeInCache+=size;
- #ifdef FULLSANITYCHECKS
- tcfullsanitycheck(tc);
- #endif
- #if 1
- if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
- ReleaseFreeInCache(p, tc, mymspace);
- #endif
- }
- static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
- { /* threads is -1 for system pool */
- ensure_initialization();
- ACQUIRE_MALLOC_GLOBAL_LOCK();
- if(p->threads) goto done;
- if(INITIAL_LOCK(&p->mutex)) goto err;
- if(TLSALLOC(&p->mycache)) goto err;
- if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
- p->m[0]->extp=p;
- p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
- done:
- RELEASE_MALLOC_GLOBAL_LOCK();
- return 1;
- err:
- if(threads<0)
- abort(); /* If you can't allocate for system pool, we're screwed */
- DestroyCaches(p);
- if(p->m[0])
- {
- destroy_mspace(p->m[0]);
- p->m[0]=0;
- }
- if(p->mycache)
- {
- if(TLSFREE(p->mycache)) abort();
- p->mycache=0;
- }
- RELEASE_MALLOC_GLOBAL_LOCK();
- return 0;
- }
- static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
- { /* Gets called when thread's last used mspace is in use. The strategy
- is to run through the list of all available mspaces looking for an
- unlocked one and if we fail, we create a new one so long as we don't
- exceed p->threads */
- int n, end;
- for(n=end=*lastUsed+1; p->m[n]; end=++n)
- {
- if(TRY_LOCK(&p->m[n]->mutex)) goto found;
- }
- for(n=0; n<*lastUsed && p->m[n]; n++)
- {
- if(TRY_LOCK(&p->m[n]->mutex)) goto found;
- }
- if(end<p->threads)
- {
- mstate temp;
- if(!(temp=(mstate) create_mspace(size, 1)))
- goto badexit;
- /* Now we're ready to modify the lists, we lock */
- ACQUIRE_LOCK(&p->mutex);
- while(p->m[end] && end<p->threads)
- end++;
- if(end>=p->threads)
- { /* Drat, must destroy it now */
- RELEASE_LOCK(&p->mutex);
- destroy_mspace((mspace) temp);
- goto badexit;
- }
- /* We really want to make sure this goes into memory now but we
- have to be careful of breaking aliasing rules, so write it twice */
- {
- volatile struct malloc_state **_m=(volatile struct malloc_state **) &p->m[end];
- *_m=(p->m[end]=temp);
- }
- ACQUIRE_LOCK(&p->m[end]->mutex);
- /*printf("Created mspace idx %d\n", end);*/
- RELEASE_LOCK(&p->mutex);
- n=end;
- goto found;
- }
- /* Let it lock on the last one it used */
- badexit:
- ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
- return p->m[*lastUsed];
- found:
- *lastUsed=n;
- if(tc)
- tc->mymspace=n;
- else
- {
- if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
- }
- return p->m[n];
- }
- nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
- {
- nedpool *ret;
- if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
- if(!InitPool(ret, capacity, threads))
- {
- nedpfree(0, ret);
- return 0;
- }
- return ret;
- }
- void neddestroypool(nedpool *p) THROWSPEC
- {
- int n;
- ACQUIRE_LOCK(&p->mutex);
- DestroyCaches(p);
- for(n=0; p->m[n]; n++)
- {
- destroy_mspace(p->m[n]);
- p->m[n]=0;
- }
- RELEASE_LOCK(&p->mutex);
- if(TLSFREE(p->mycache)) abort();
- nedpfree(0, p);
- }
- void nedpsetvalue(nedpool *p, void *v) THROWSPEC
- {
- if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
- p->uservalue=v;
- }
- void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
- {
- nedpool *np=0;
- mchunkptr mcp=mem2chunk(mem);
- mstate fm;
- if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
- if(!cinuse(mcp)) return 0;
- if(!next_pinuse(mcp)) return 0;
- if(!is_mmapped(mcp) && !pinuse(mcp))
- {
- if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
- }
- fm=get_mstate_for(mcp);
- if(!ok_magic(fm)) return 0;
- if(!ok_address(fm, mcp)) return 0;
- if(!fm->extp) return 0;
- np=(nedpool *) fm->extp;
- if(p) *p=np;
- return np->uservalue;
- }
- void neddisablethreadcache(nedpool *p) THROWSPEC
- {
- int mycache;
- if(!p)
- {
- p=&syspool;
- if(!syspool.threads) InitPool(&syspool, 0, -1);
- }
- mycache=(int)(size_t) TLSGET(p->mycache);
- if(!mycache)
- { /* Set to mspace 0 */
- if(TLSSET(p->mycache, (void *)-1)) abort();
- }
- else if(mycache>0)
- { /* Set to last used mspace */
- threadcache *tc=p->caches[mycache-1];
- #if defined(DEBUG)
- printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
- 100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
- #endif
- if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
- tc->frees++;
- RemoveCacheEntries(p, tc, 0);
- assert(!tc->freeInCache);
- tc->mymspace=-1;
- tc->threadid=0;
- mspace_free(0, p->caches[mycache-1]);
- p->caches[mycache-1]=0;
- }
- }
- #define GETMSPACE(m,p,tc,ms,s,action) \
- do \
- { \
- mstate m = GetMSpace((p),(tc),(ms),(s)); \
- action; \
- RELEASE_LOCK(&m->mutex); \
- } while (0)
- static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
- { /* Returns a locked and ready for use mspace */
- mstate m=p->m[mymspace];
- assert(m);
- if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
- /*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
- return m;
- }
- static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
- {
- int mycache;
- if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
- if(!*p)
- {
- *p=&syspool;
- if(!syspool.threads) InitPool(&syspool, 0, -1);
- }
- mycache=(int)(size_t) TLSGET((*p)->mycache);
- if(mycache>0)
- {
- *tc=(*p)->caches[mycache-1];
- *mymspace=(*tc)->mymspace;
- }
- else if(!mycache)
- {
- *tc=AllocCache(*p);
- if(!*tc)
- { /* Disable */
- if(TLSSET((*p)->mycache, (void *)-1)) abort();
- *mymspace=0;
- }
- else
- *mymspace=(*tc)->mymspace;
- }
- else
- {
- *tc=0;
- *mymspace=-mycache-1;
- }
- assert(*mymspace>=0);
- assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
- #ifdef FULLSANITYCHECKS
- if(*tc)
- {
- if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
- {
- abort();
- }
- }
- #endif
- }
- void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
- {
- void *ret=0;
- threadcache *tc;
- int mymspace;
- GetThreadCache(&p, &tc, &mymspace, &size);
- #if THREADCACHEMAX
- if(tc && size<=THREADCACHEMAX)
- { /* Use the thread cache */
- ret=threadcache_malloc(p, tc, &size);
- }
- #endif
- if(!ret)
- { /* Use this thread's mspace */
- GETMSPACE(m, p, tc, mymspace, size,
- ret=mspace_malloc(m, size));
- }
- return ret;
- }
- void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
- {
- size_t rsize=size*no;
- void *ret=0;
- threadcache *tc;
- int mymspace;
- GetThreadCache(&p, &tc, &mymspace, &rsize);
- #if THREADCACHEMAX
- if(tc && rsize<=THREADCACHEMAX)
- { /* Use the thread cache */
- if((ret=threadcache_malloc(p, tc, &rsize)))
- memset(ret, 0, rsize);
- }
- #endif
- if(!ret)
- { /* Use this thread's mspace */
- GETMSPACE(m, p, tc, mymspace, rsize,
- ret=mspace_calloc(m, 1, rsize));
- }
- return ret;
- }
- void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
- {
- void *ret=0;
- threadcache *tc;
- int mymspace;
- if(!mem) return nedpmalloc(p, size);
- GetThreadCache(&p, &tc, &mymspace, &size);
- #if THREADCACHEMAX
- if(tc && size && size<=THREADCACHEMAX)
- { /* Use the thread cache */
- size_t memsize=nedblksize(mem);
- assert(memsize);
- if((ret=threadcache_malloc(p, tc, &size)))
- {
- memcpy(ret, mem, memsize<size ? memsize : size);
- if(memsize<=THREADCACHEMAX)
- threadcache_free(p, tc, mymspace, mem, memsize);
- else
- mspace_free(0, mem);
- }
- }
- #endif
- if(!ret)
- { /* Reallocs always happen in the mspace they happened in, so skip
- locking the preferred mspace for this thread */
- ret=mspace_realloc(0, mem, size);
- }
- return ret;
- }
- void nedpfree(nedpool *p, void *mem) THROWSPEC
- { /* Frees always happen in the mspace they happened in, so skip
- locking the preferred mspace for this thread */
- threadcache *tc;
- int mymspace;
- size_t memsize;
- assert(mem);
- GetThreadCache(&p, &tc, &mymspace, 0);
- #if THREADCACHEMAX
- memsize=nedblksize(mem);
- assert(memsize);
- if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
- threadcache_free(p, tc, mymspace, mem, memsize);
- else
- #endif
- mspace_free(0, mem);
- }
- void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
- {
- void *ret;
- threadcache *tc;
- int mymspace;
- GetThreadCache(&p, &tc, &mymspace, &bytes);
- { /* Use this thread's mspace */
- GETMSPACE(m, p, tc, mymspace, bytes,
- ret=mspace_memalign(m, alignment, bytes));
- }
- return ret;
- }
- #if !NO_MALLINFO
- struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
- {
- int n;
- struct mallinfo ret={0};
- if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
- for(n=0; p->m[n]; n++)
- {
- struct mallinfo t=mspace_mallinfo(p->m[n]);
- ret.arena+=t.arena;
- ret.ordblks+=t.ordblks;
- ret.hblkhd+=t.hblkhd;
- ret.usmblks+=t.usmblks;
- ret.uordblks+=t.uordblks;
- ret.fordblks+=t.fordblks;
- ret.keepcost+=t.keepcost;
- }
- return ret;
- }
- #endif
- int nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
- {
- return mspace_mallopt(parno, value);
- }
- int nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
- {
- int n, ret=0;
- if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
- for(n=0; p->m[n]; n++)
- {
- ret+=mspace_trim(p->m[n], pad);
- }
- return ret;
- }
- void nedpmalloc_stats(nedpool *p) THROWSPEC
- {
- int n;
- if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
- for(n=0; p->m[n]; n++)
- {
- mspace_malloc_stats(p->m[n]);
- }
- }
- size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
- {
- size_t ret=0;
- int n;
- if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
- for(n=0; p->m[n]; n++)
- {
- ret+=mspace_footprint(p->m[n]);
- }
- return ret;
- }
- void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
- {
- void **ret;
- threadcache *tc;
- int mymspace;
- GetThreadCache(&p, &tc, &mymspace, &elemsize);
- GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
- ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
- return ret;
- }
- void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
- {
- void **ret;
- threadcache *tc;
- int mymspace;
- size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
- if(!adjustedsizes) return 0;
- for(i=0; i<elems; i++)
- adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
- GetThreadCache(&p, &tc, &mymspace, 0);
- GETMSPACE(m, p, tc, mymspace, 0,
- ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
- return ret;
- }
- #if defined(__cplusplus)
- }
- #endif
|