nedmalloc.c

Go to the documentation of this file.
00001 /* Alternative malloc implementation for multiple threads without
00002 lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
00003 
00004 Boost Software License - Version 1.0 - August 17th, 2003
00005 
00006 Permission is hereby granted, free of charge, to any person or organization
00007 obtaining a copy of the software and accompanying documentation covered by
00008 this license (the "Software") to use, reproduce, display, distribute,
00009 execute, and transmit the Software, and to prepare derivative works of the
00010 Software, and to permit third-parties to whom the Software is furnished to
00011 do so, all subject to the following:
00012 
00013 The copyright notices in the Software and this entire statement, including
00014 the above license grant, this restriction and the following disclaimer,
00015 must be included in all copies of the Software, in whole or in part, and
00016 all derivative works of the Software, unless such copies or derivative
00017 works are solely in the form of machine-executable object code generated by
00018 a source language processor.
00019 
00020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
00023 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
00024 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
00025 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00026 DEALINGS IN THE SOFTWARE.
00027 */
00028 
00029 #ifdef _MSC_VER
00030 /* Enable full aliasing on MSVC */
00031 /*#pragma optimize("a", on)*/
00032 #endif
00033 
00034 /*#define FULLSANITYCHECKS*/
00035 
00036 #include "nedmalloc.h"
00037 #ifdef WIN32
00038  #include <malloc.h>
00039 #endif
00040 #define MSPACES 1
00041 #define ONLY_MSPACES 1
00042 #ifndef USE_LOCKS
00043  #define USE_LOCKS 1
00044 #endif
00045 #define FOOTERS 1           /* Need to enable footers so frees lock the right mspace */
00046 #undef DEBUG                /* dlmalloc wants DEBUG either 0 or 1 */
00047 #ifdef _DEBUG
00048  #define DEBUG 1
00049 #else
00050  #define DEBUG 0
00051 #endif
00052 #ifdef NDEBUG               /* Disable assert checking on release builds */
00053  #undef DEBUG
00054 #endif
00055 /* The default of 64Kb means we spend too much time kernel-side */
00056 #ifndef DEFAULT_GRANULARITY
00057 #define DEFAULT_GRANULARITY (1*1024*1024)
00058 #endif
00059 /*#define USE_SPIN_LOCKS 0*/
00060 
00061 
00062 /*#define FORCEINLINE*/
00063 #include "malloc.c.h"
00064 #ifdef NDEBUG               /* Disable assert checking on release builds */
00065  #undef DEBUG
00066 #endif
00067 
00068 /* The maximum concurrent threads in a pool possible */
00069 #ifndef MAXTHREADSINPOOL
00070 #define MAXTHREADSINPOOL 16
00071 #endif
00072 /* The maximum number of threadcaches which can be allocated */
00073 #ifndef THREADCACHEMAXCACHES
00074 #define THREADCACHEMAXCACHES 256
00075 #endif
00076 /* The maximum size to be allocated from the thread cache */
00077 #ifndef THREADCACHEMAX
00078 #define THREADCACHEMAX 8192
00079 #endif
00080 #if 0
00081 /* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
00082 #define THREADCACHEMAXBINS ((13-4)*2)
00083 #else
00084 /* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
00085 #define THREADCACHEMAXBINS (13-4)
00086 #endif
00087 /* Point at which the free space in a thread cache is garbage collected */
00088 #ifndef THREADCACHEMAXFREESPACE
00089 #define THREADCACHEMAXFREESPACE (512*1024)
00090 #endif
00091 
00092 
00093 #ifdef WIN32
00094  #define TLSVAR         DWORD
00095  #define TLSALLOC(k)    (*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
00096  #define TLSFREE(k)     (!TlsFree(k))
00097  #define TLSGET(k)      TlsGetValue(k)
00098  #define TLSSET(k, a)   (!TlsSetValue(k, a))
00099  #ifdef DEBUG
00100 static LPVOID ChkedTlsGetValue(DWORD idx)
00101 {
00102     LPVOID ret=TlsGetValue(idx);
00103     assert(S_OK==GetLastError());
00104     return ret;
00105 }
00106   #undef TLSGET
00107   #define TLSGET(k) ChkedTlsGetValue(k)
00108  #endif
00109 #else
00110  #define TLSVAR         pthread_key_t
00111  #define TLSALLOC(k)    pthread_key_create(k, 0)
00112  #define TLSFREE(k)     pthread_key_delete(k)
00113  #define TLSGET(k)      pthread_getspecific(k)
00114  #define TLSSET(k, a)   pthread_setspecific(k, a)
00115 #endif
00116 
00117 #if 0
00118 /* Only enable if testing with valgrind. Causes misoperation */
00119 #define mspace_malloc(p, s) malloc(s)
00120 #define mspace_realloc(p, m, s) realloc(m, s)
00121 #define mspace_calloc(p, n, s) calloc(n, s)
00122 #define mspace_free(p, m) free(m)
00123 #endif
00124 
00125 
00126 #if defined(__cplusplus)
00127 #if !defined(NO_NED_NAMESPACE)
00128 namespace nedalloc {
00129 #else
00130 extern "C" {
00131 #endif
00132 #endif
00133 
00134 size_t nedblksize(void *mem) THROWSPEC
00135 {
00136 #if 0
00137     /* Only enable if testing with valgrind. Causes misoperation */
00138     return THREADCACHEMAX;
00139 #else
00140     if(mem)
00141     {
00142         mchunkptr p=mem2chunk(mem);
00143         assert(cinuse(p));  /* If this fails, someone tried to free a block twice */
00144         if(cinuse(p))
00145             return chunksize(p)-overhead_for(p);
00146     }
00147     return 0;
00148 #endif
00149 }
00150 
00151 void nedsetvalue(void *v) THROWSPEC                 { nedpsetvalue(0, v); }
00152 void * nedmalloc(size_t size) THROWSPEC             { return nedpmalloc(0, size); }
00153 void * nedcalloc(size_t no, size_t size) THROWSPEC  { return nedpcalloc(0, no, size); }
00154 void * nedrealloc(void *mem, size_t size) THROWSPEC { return nedprealloc(0, mem, size); }
00155 void   nedfree(void *mem) THROWSPEC                 { nedpfree(0, mem); }
00156 void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
00157 #if !NO_MALLINFO
00158 struct mallinfo nedmallinfo(void) THROWSPEC         { return nedpmallinfo(0); }
00159 #endif
00160 int    nedmallopt(int parno, int value) THROWSPEC   { return nedpmallopt(0, parno, value); }
00161 int    nedmalloc_trim(size_t pad) THROWSPEC         { return nedpmalloc_trim(0, pad); }
00162 void   nedmalloc_stats() THROWSPEC                  { nedpmalloc_stats(0); }
00163 size_t nedmalloc_footprint() THROWSPEC              { return nedpmalloc_footprint(0); }
00164 void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC  { return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
00165 void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC    { return nedpindependent_comalloc(0, elems, sizes, chunks); }
00166 
00167 struct threadcacheblk_t;
00168 typedef struct threadcacheblk_t threadcacheblk;
00169 struct threadcacheblk_t
00170 {   /* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
00171 #ifdef FULLSANITYCHECKS
00172     unsigned int magic;
00173 #endif
00174     unsigned int lastUsed, size;
00175     threadcacheblk *next, *prev;
00176 };
00177 typedef struct threadcache_t
00178 {
00179 #ifdef FULLSANITYCHECKS
00180     unsigned int magic1;
00181 #endif
00182     int mymspace;                       /* Last mspace entry this thread used */
00183     long threadid;
00184     unsigned int mallocs, frees, successes;
00185     size_t freeInCache;                 /* How much free space is stored in this cache */
00186     threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
00187 #ifdef FULLSANITYCHECKS
00188     unsigned int magic2;
00189 #endif
00190 } threadcache;
00191 struct nedpool_t
00192 {
00193     MLOCK_T mutex;
00194     void *uservalue;
00195     int threads;                        /* Max entries in m to use */
00196     threadcache *caches[THREADCACHEMAXCACHES];
00197     TLSVAR mycache;                     /* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
00198     mstate m[MAXTHREADSINPOOL+1];       /* mspace entries for this pool */
00199 };
00200 static nedpool syspool;
00201 
00202 static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
00203 {   /* 8=1000   16=10000    20=10100    24=11000    32=100000   48=110000   4096=1000000000000 */
00204     unsigned int topbit, size=(unsigned int)(_size>>4);
00205     /* 16=1     20=1    24=1    32=10   48=11   64=100  96=110  128=1000    4096=100000000 */
00206 
00207 #if defined(__GNUC__)
00208         topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
00209 #elif defined(_MSC_VER) && _MSC_VER>=1300
00210     {
00211             unsigned long bsrTopBit;
00212 
00213             _BitScanReverse(&bsrTopBit, size);
00214 
00215             topbit = bsrTopBit;
00216         }
00217 #else
00218 #if 0
00219     union {
00220         unsigned asInt[2];
00221         double asDouble;
00222     };
00223     int n;
00224 
00225     asDouble = (double)size + 0.5;
00226     topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
00227 #else
00228     {
00229         unsigned int x=size;
00230         x = x | (x >> 1);
00231         x = x | (x >> 2);
00232         x = x | (x >> 4);
00233         x = x | (x >> 8);
00234         x = x | (x >>16);
00235         x = ~x;
00236         x = x - ((x >> 1) & 0x55555555);
00237         x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
00238         x = (x + (x >> 4)) & 0x0F0F0F0F;
00239         x = x + (x << 8);
00240         x = x + (x << 16);
00241         topbit=31 - (x >> 24);
00242     }
00243 #endif
00244 #endif
00245     return topbit;
00246 }
00247 
00248 
00249 #ifdef FULLSANITYCHECKS
00250 static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
00251 {
00252     assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
00253     if(ptr[0] && ptr[1])
00254     {
00255         assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
00256         assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
00257         assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
00258         assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
00259         assert(!ptr[0]->prev);
00260         assert(!ptr[1]->next);
00261         if(ptr[0]==ptr[1])
00262         {
00263             assert(!ptr[0]->next);
00264             assert(!ptr[1]->prev);
00265         }
00266     }
00267 }
00268 static void tcfullsanitycheck(threadcache *tc) THROWSPEC
00269 {
00270     threadcacheblk **tcbptr=tc->bins;
00271     int n;
00272     for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
00273     {
00274         threadcacheblk *b, *ob=0;
00275         tcsanitycheck(tcbptr);
00276         for(b=tcbptr[0]; b; ob=b, b=b->next)
00277         {
00278             assert(*(unsigned int *) "NEDN"==b->magic);
00279             assert(!ob || ob->next==b);
00280             assert(!ob || b->prev==ob);
00281         }
00282     }
00283 }
00284 #endif
00285 
00286 static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
00287 {
00288 #ifdef FULLSANITYCHECKS
00289     tcfullsanitycheck(tc);
00290 #endif
00291     if(tc->freeInCache)
00292     {
00293         threadcacheblk **tcbptr=tc->bins;
00294         int n;
00295         for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
00296         {
00297             threadcacheblk **tcb=tcbptr+1;      /* come from oldest end of list */
00298             /*tcsanitycheck(tcbptr);*/
00299             for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
00300             {
00301                 threadcacheblk *f=*tcb;
00302                 size_t blksize=f->size; /*nedblksize(f);*/
00303                 assert(blksize<=nedblksize(f));
00304                 assert(blksize);
00305 #ifdef FULLSANITYCHECKS
00306                 assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
00307 #endif
00308                 *tcb=(*tcb)->prev;
00309                 if(*tcb)
00310                     (*tcb)->next=0;
00311                 else
00312                     *tcbptr=0;
00313                 tc->freeInCache-=blksize;
00314                 assert((long) tc->freeInCache>=0);
00315                 mspace_free(0, f);
00316                 /*tcsanitycheck(tcbptr);*/
00317             }
00318         }
00319     }
00320 #ifdef FULLSANITYCHECKS
00321     tcfullsanitycheck(tc);
00322 #endif
00323 }
00324 static void DestroyCaches(nedpool *p) THROWSPEC
00325 {
00326     if(p->caches)
00327     {
00328         threadcache *tc;
00329         int n;
00330         for(n=0; n<THREADCACHEMAXCACHES; n++)
00331         {
00332             if((tc=p->caches[n]))
00333             {
00334                 tc->frees++;
00335                 RemoveCacheEntries(p, tc, 0);
00336                 assert(!tc->freeInCache);
00337                 tc->mymspace=-1;
00338                 tc->threadid=0;
00339                 mspace_free(0, tc);
00340                 p->caches[n]=0;
00341             }
00342         }
00343     }
00344 }
00345 
00346 static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
00347 {
00348     threadcache *tc=0;
00349     int n, end;
00350     ACQUIRE_LOCK(&p->mutex);
00351     for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
00352     if(THREADCACHEMAXCACHES==n)
00353     {   /* List exhausted, so disable for this thread */
00354         RELEASE_LOCK(&p->mutex);
00355         return 0;
00356     }
00357     tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
00358     if(!tc)
00359     {
00360         RELEASE_LOCK(&p->mutex);
00361         return 0;
00362     }
00363 #ifdef FULLSANITYCHECKS
00364     tc->magic1=*(unsigned int *)"NEDMALC1";
00365     tc->magic2=*(unsigned int *)"NEDMALC2";
00366 #endif
00367     tc->threadid=(long)(size_t)CURRENT_THREAD;
00368     for(end=0; p->m[end]; end++);
00369     tc->mymspace=tc->threadid % end;
00370     RELEASE_LOCK(&p->mutex);
00371     if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
00372     return tc;
00373 }
00374 
00375 static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
00376 {
00377     void *ret=0;
00378     unsigned int bestsize;
00379     unsigned int idx=size2binidx(*size);
00380     size_t blksize=0;
00381     threadcacheblk *blk, **binsptr;
00382 #ifdef FULLSANITYCHECKS
00383     tcfullsanitycheck(tc);
00384 #endif
00385     /* Calculate best fit bin size */
00386     bestsize=1<<(idx+4);
00387 #if 0
00388     /* Finer grained bin fit */
00389     idx<<=1;
00390     if(*size>bestsize)
00391     {
00392         idx++;
00393         bestsize+=bestsize>>1;
00394     }
00395     if(*size>bestsize)
00396     {
00397         idx++;
00398         bestsize=1<<(4+(idx>>1));
00399     }
00400 #else
00401     if(*size>bestsize)
00402     {
00403         idx++;
00404         bestsize<<=1;
00405     }
00406 #endif
00407     assert(bestsize>=*size);
00408     if(*size<bestsize) *size=bestsize;
00409     assert(*size<=THREADCACHEMAX);
00410     assert(idx<=THREADCACHEMAXBINS);
00411     binsptr=&tc->bins[idx*2];
00412     /* Try to match close, but move up a bin if necessary */
00413     blk=*binsptr;
00414     if(!blk || blk->size<*size)
00415     {   /* Bump it up a bin */
00416         if(idx<THREADCACHEMAXBINS)
00417         {
00418             idx++;
00419             binsptr+=2;
00420             blk=*binsptr;
00421         }
00422     }
00423     if(blk)
00424     {
00425         blksize=blk->size; /*nedblksize(blk);*/
00426         assert(nedblksize(blk)>=blksize);
00427         assert(blksize>=*size);
00428         if(blk->next)
00429             blk->next->prev=0;
00430         *binsptr=blk->next;
00431         if(!*binsptr)
00432             binsptr[1]=0;
00433 #ifdef FULLSANITYCHECKS
00434         blk->magic=0;
00435 #endif
00436         assert(binsptr[0]!=blk && binsptr[1]!=blk);
00437         assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
00438         /*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
00439         ret=(void *) blk;
00440     }
00441     ++tc->mallocs;
00442     if(ret)
00443     {
00444         assert(blksize>=*size);
00445         ++tc->successes;
00446         tc->freeInCache-=blksize;
00447         assert((long) tc->freeInCache>=0);
00448     }
00449 #if defined(DEBUG) && 0
00450     if(!(tc->mallocs & 0xfff))
00451     {
00452         printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
00453             (float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
00454     }
00455 #endif
00456 #ifdef FULLSANITYCHECKS
00457     tcfullsanitycheck(tc);
00458 #endif
00459     return ret;
00460 }
00461 static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
00462 {
00463     unsigned int age=THREADCACHEMAXFREESPACE/8192;
00464     /*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
00465     while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
00466     {
00467         RemoveCacheEntries(p, tc, age);
00468         /*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
00469         age>>=1;
00470     }
00471     /*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
00472 }
00473 static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
00474 {
00475     unsigned int bestsize;
00476     unsigned int idx=size2binidx(size);
00477     threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
00478     assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
00479 #ifdef DEBUG
00480     {   /* Make sure this is a valid memory block */
00481         mchunkptr p  = mem2chunk(mem);
00482         mstate fm = get_mstate_for(p);
00483         if (!ok_magic(fm)) {
00484           USAGE_ERROR_ACTION(fm, p);
00485           return;
00486         }
00487     }
00488 #endif
00489 #ifdef FULLSANITYCHECKS
00490     tcfullsanitycheck(tc);
00491 #endif
00492     /* Calculate best fit bin size */
00493     bestsize=1<<(idx+4);
00494 #if 0
00495     /* Finer grained bin fit */
00496     idx<<=1;
00497     if(size>bestsize)
00498     {
00499         unsigned int biggerbestsize=bestsize+bestsize<<1;
00500         if(size>=biggerbestsize)
00501         {
00502             idx++;
00503             bestsize=biggerbestsize;
00504         }
00505     }
00506 #endif
00507     if(bestsize!=size)  /* dlmalloc can round up, so we round down to preserve indexing */
00508         size=bestsize;
00509     binsptr=&tc->bins[idx*2];
00510     assert(idx<=THREADCACHEMAXBINS);
00511     if(tck==*binsptr)
00512     {
00513         fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
00514         abort();
00515     }
00516 #ifdef FULLSANITYCHECKS
00517     tck->magic=*(unsigned int *) "NEDN";
00518 #endif
00519     tck->lastUsed=++tc->frees;
00520     tck->size=(unsigned int) size;
00521     tck->next=*binsptr;
00522     tck->prev=0;
00523     if(tck->next)
00524         tck->next->prev=tck;
00525     else
00526         binsptr[1]=tck;
00527     assert(!*binsptr || (*binsptr)->size==tck->size);
00528     *binsptr=tck;
00529     assert(tck==tc->bins[idx*2]);
00530     assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
00531     /*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
00532     tc->freeInCache+=size;
00533 #ifdef FULLSANITYCHECKS
00534     tcfullsanitycheck(tc);
00535 #endif
00536 #if 1
00537     if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
00538         ReleaseFreeInCache(p, tc, mymspace);
00539 #endif
00540 }
00541 
00542 
00543 
00544 
00545 static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
00546 {   /* threads is -1 for system pool */
00547     ensure_initialization();
00548     ACQUIRE_MALLOC_GLOBAL_LOCK();
00549     if(p->threads) goto done;
00550     if(INITIAL_LOCK(&p->mutex)) goto err;
00551     if(TLSALLOC(&p->mycache)) goto err;
00552     if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
00553     p->m[0]->extp=p;
00554     p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
00555 done:
00556     RELEASE_MALLOC_GLOBAL_LOCK();
00557     return 1;
00558 err:
00559     if(threads<0)
00560         abort();            /* If you can't allocate for system pool, we're screwed */
00561     DestroyCaches(p);
00562     if(p->m[0])
00563     {
00564         destroy_mspace(p->m[0]);
00565         p->m[0]=0;
00566     }
00567     if(p->mycache)
00568     {
00569         if(TLSFREE(p->mycache)) abort();
00570         p->mycache=0;
00571     }
00572     RELEASE_MALLOC_GLOBAL_LOCK();
00573     return 0;
00574 }
00575 static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
00576 {   /* Gets called when thread's last used mspace is in use. The strategy
00577     is to run through the list of all available mspaces looking for an
00578     unlocked one and if we fail, we create a new one so long as we don't
00579     exceed p->threads */
00580     int n, end;
00581     for(n=end=*lastUsed+1; p->m[n]; end=++n)
00582     {
00583         if(TRY_LOCK(&p->m[n]->mutex)) goto found;
00584     }
00585     for(n=0; n<*lastUsed && p->m[n]; n++)
00586     {
00587         if(TRY_LOCK(&p->m[n]->mutex)) goto found;
00588     }
00589     if(end<p->threads)
00590     {
00591         mstate temp;
00592         if(!(temp=(mstate) create_mspace(size, 1)))
00593             goto badexit;
00594         /* Now we're ready to modify the lists, we lock */
00595         ACQUIRE_LOCK(&p->mutex);
00596         while(p->m[end] && end<p->threads)
00597             end++;
00598         if(end>=p->threads)
00599         {   /* Drat, must destroy it now */
00600             RELEASE_LOCK(&p->mutex);
00601             destroy_mspace((mspace) temp);
00602             goto badexit;
00603         }
00604         /* We really want to make sure this goes into memory now but we
00605         have to be careful of breaking aliasing rules, so write it twice */
00606         *((volatile struct malloc_state **) &p->m[end])=p->m[end]=temp;
00607         ACQUIRE_LOCK(&p->m[end]->mutex);
00608         /*printf("Created mspace idx %d\n", end);*/
00609         RELEASE_LOCK(&p->mutex);
00610         n=end;
00611         goto found;
00612     }
00613     /* Let it lock on the last one it used */
00614 badexit:
00615     ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
00616     return p->m[*lastUsed];
00617 found:
00618     *lastUsed=n;
00619     if(tc)
00620         tc->mymspace=n;
00621     else
00622     {
00623         if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
00624     }
00625     return p->m[n];
00626 }
00627 
00628 nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
00629 {
00630     nedpool *ret;
00631     if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
00632     if(!InitPool(ret, capacity, threads))
00633     {
00634         nedpfree(0, ret);
00635         return 0;
00636     }
00637     return ret;
00638 }
00639 void neddestroypool(nedpool *p) THROWSPEC
00640 {
00641     int n;
00642     ACQUIRE_LOCK(&p->mutex);
00643     DestroyCaches(p);
00644     for(n=0; p->m[n]; n++)
00645     {
00646         destroy_mspace(p->m[n]);
00647         p->m[n]=0;
00648     }
00649     RELEASE_LOCK(&p->mutex);
00650     if(TLSFREE(p->mycache)) abort();
00651     nedpfree(0, p);
00652 }
00653 
00654 void nedpsetvalue(nedpool *p, void *v) THROWSPEC
00655 {
00656     if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
00657     p->uservalue=v;
00658 }
00659 void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
00660 {
00661     nedpool *np=0;
00662     mchunkptr mcp=mem2chunk(mem);
00663     mstate fm;
00664     if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
00665     if(!cinuse(mcp)) return 0;
00666     if(!next_pinuse(mcp)) return 0;
00667     if(!is_mmapped(mcp) && !pinuse(mcp))
00668     {
00669         if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
00670     }
00671     fm=get_mstate_for(mcp);
00672     if(!ok_magic(fm)) return 0;
00673     if(!ok_address(fm, mcp)) return 0;
00674     if(!fm->extp) return 0;
00675     np=(nedpool *) fm->extp;
00676     if(p) *p=np;
00677     return np->uservalue;
00678 }
00679 
00680 void neddisablethreadcache(nedpool *p) THROWSPEC
00681 {
00682     int mycache;
00683     if(!p)
00684     {
00685         p=&syspool;
00686         if(!syspool.threads) InitPool(&syspool, 0, -1);
00687     }
00688     mycache=(int)(size_t) TLSGET(p->mycache);
00689     if(!mycache)
00690     {   /* Set to mspace 0 */
00691         if(TLSSET(p->mycache, (void *)-1)) abort();
00692     }
00693     else if(mycache>0)
00694     {   /* Set to last used mspace */
00695         threadcache *tc=p->caches[mycache-1];
00696 #if defined(DEBUG)
00697         printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
00698             100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
00699 #endif
00700         if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
00701         tc->frees++;
00702         RemoveCacheEntries(p, tc, 0);
00703         assert(!tc->freeInCache);
00704         tc->mymspace=-1;
00705         tc->threadid=0;
00706         mspace_free(0, p->caches[mycache-1]);
00707         p->caches[mycache-1]=0;
00708     }
00709 }
00710 
00711 #define GETMSPACE(m,p,tc,ms,s,action)           \
00712   do                                            \
00713   {                                             \
00714     mstate m = GetMSpace((p),(tc),(ms),(s));    \
00715     action;                                     \
00716     RELEASE_LOCK(&m->mutex);                    \
00717   } while (0)
00718 
00719 static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
00720 {   /* Returns a locked and ready for use mspace */
00721     mstate m=p->m[mymspace];
00722     assert(m);
00723     if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
00724     /*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
00725     return m;
00726 }
00727 static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
00728 {
00729     int mycache;
00730     if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
00731     if(!*p)
00732     {
00733         *p=&syspool;
00734         if(!syspool.threads) InitPool(&syspool, 0, -1);
00735     }
00736     mycache=(int)(size_t) TLSGET((*p)->mycache);
00737     if(mycache>0)
00738     {
00739         *tc=(*p)->caches[mycache-1];
00740         *mymspace=(*tc)->mymspace;
00741     }
00742     else if(!mycache)
00743     {
00744         *tc=AllocCache(*p);
00745         if(!*tc)
00746         {   /* Disable */
00747             if(TLSSET((*p)->mycache, (void *)-1)) abort();
00748             *mymspace=0;
00749         }
00750         else
00751             *mymspace=(*tc)->mymspace;
00752     }
00753     else
00754     {
00755         *tc=0;
00756         *mymspace=-mycache-1;
00757     }
00758     assert(*mymspace>=0);
00759     assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
00760 #ifdef FULLSANITYCHECKS
00761     if(*tc)
00762     {
00763         if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
00764         {
00765             abort();
00766         }
00767     }
00768 #endif
00769 }
00770 
00771 void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
00772 {
00773     void *ret=0;
00774     threadcache *tc;
00775     int mymspace;
00776     GetThreadCache(&p, &tc, &mymspace, &size);
00777 #if THREADCACHEMAX
00778     if(tc && size<=THREADCACHEMAX)
00779     {   /* Use the thread cache */
00780         ret=threadcache_malloc(p, tc, &size);
00781     }
00782 #endif
00783     if(!ret)
00784     {   /* Use this thread's mspace */
00785         GETMSPACE(m, p, tc, mymspace, size,
00786                   ret=mspace_malloc(m, size));
00787     }
00788     return ret;
00789 }
00790 void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
00791 {
00792     size_t rsize=size*no;
00793     void *ret=0;
00794     threadcache *tc;
00795     int mymspace;
00796     GetThreadCache(&p, &tc, &mymspace, &rsize);
00797 #if THREADCACHEMAX
00798     if(tc && rsize<=THREADCACHEMAX)
00799     {   /* Use the thread cache */
00800         if((ret=threadcache_malloc(p, tc, &rsize)))
00801             memset(ret, 0, rsize);
00802     }
00803 #endif
00804     if(!ret)
00805     {   /* Use this thread's mspace */
00806         GETMSPACE(m, p, tc, mymspace, rsize,
00807                   ret=mspace_calloc(m, 1, rsize));
00808     }
00809     return ret;
00810 }
00811 void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
00812 {
00813     void *ret=0;
00814     threadcache *tc;
00815     int mymspace;
00816     if(!mem) return nedpmalloc(p, size);
00817     GetThreadCache(&p, &tc, &mymspace, &size);
00818 #if THREADCACHEMAX
00819     if(tc && size && size<=THREADCACHEMAX)
00820     {   /* Use the thread cache */
00821         size_t memsize=nedblksize(mem);
00822         assert(memsize);
00823         if((ret=threadcache_malloc(p, tc, &size)))
00824         {
00825             memcpy(ret, mem, memsize<size ? memsize : size);
00826             if(memsize<=THREADCACHEMAX)
00827                 threadcache_free(p, tc, mymspace, mem, memsize);
00828             else
00829                 mspace_free(0, mem);
00830         }
00831     }
00832 #endif
00833     if(!ret)
00834     {   /* Reallocs always happen in the mspace they happened in, so skip
00835         locking the preferred mspace for this thread */
00836         ret=mspace_realloc(0, mem, size);
00837     }
00838     return ret;
00839 }
00840 void   nedpfree(nedpool *p, void *mem) THROWSPEC
00841 {   /* Frees always happen in the mspace they happened in, so skip
00842     locking the preferred mspace for this thread */
00843     threadcache *tc;
00844     int mymspace;
00845     size_t memsize;
00846     assert(mem);
00847     GetThreadCache(&p, &tc, &mymspace, 0);
00848 #if THREADCACHEMAX
00849     memsize=nedblksize(mem);
00850     assert(memsize);
00851     if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
00852         threadcache_free(p, tc, mymspace, mem, memsize);
00853     else
00854 #endif
00855         mspace_free(0, mem);
00856 }
00857 void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
00858 {
00859     void *ret;
00860     threadcache *tc;
00861     int mymspace;
00862     GetThreadCache(&p, &tc, &mymspace, &bytes);
00863     {   /* Use this thread's mspace */
00864         GETMSPACE(m, p, tc, mymspace, bytes,
00865                   ret=mspace_memalign(m, alignment, bytes));
00866     }
00867     return ret;
00868 }
00869 #if !NO_MALLINFO
00870 struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
00871 {
00872     int n;
00873     struct mallinfo ret={0};
00874     if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
00875     for(n=0; p->m[n]; n++)
00876     {
00877         struct mallinfo t=mspace_mallinfo(p->m[n]);
00878         ret.arena+=t.arena;
00879         ret.ordblks+=t.ordblks;
00880         ret.hblkhd+=t.hblkhd;
00881         ret.usmblks+=t.usmblks;
00882         ret.uordblks+=t.uordblks;
00883         ret.fordblks+=t.fordblks;
00884         ret.keepcost+=t.keepcost;
00885     }
00886     return ret;
00887 }
00888 #endif
00889 int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
00890 {
00891     return mspace_mallopt(parno, value);
00892 }
00893 int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
00894 {
00895     int n, ret=0;
00896     if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
00897     for(n=0; p->m[n]; n++)
00898     {
00899         ret+=mspace_trim(p->m[n], pad);
00900     }
00901     return ret;
00902 }
00903 void   nedpmalloc_stats(nedpool *p) THROWSPEC
00904 {
00905     int n;
00906     if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
00907     for(n=0; p->m[n]; n++)
00908     {
00909         mspace_malloc_stats(p->m[n]);
00910     }
00911 }
00912 size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
00913 {
00914     size_t ret=0;
00915     int n;
00916     if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
00917     for(n=0; p->m[n]; n++)
00918     {
00919         ret+=mspace_footprint(p->m[n]);
00920     }
00921     return ret;
00922 }
00923 void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
00924 {
00925     void **ret;
00926     threadcache *tc;
00927     int mymspace;
00928     GetThreadCache(&p, &tc, &mymspace, &elemsize);
00929     GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
00930               ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
00931     return ret;
00932 }
00933 void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
00934 {
00935     void **ret;
00936     threadcache *tc;
00937     int mymspace;
00938     size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
00939     if(!adjustedsizes) return 0;
00940     for(i=0; i<elems; i++)
00941         adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
00942     GetThreadCache(&p, &tc, &mymspace, 0);
00943     GETMSPACE(m, p, tc, mymspace, 0,
00944               ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
00945     return ret;
00946 }
00947 
00948 #if defined(__cplusplus)
00949 }
00950 #endif

Generated on Wed May 23 06:00:15 2012 for cpp by  doxygen 1.5.6