compat / nedmalloc / nedmalloc.con commit Merge branch 'mk/blame-first-parent' (6784eb5)
   1/* Alternative malloc implementation for multiple threads without
   2lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
   3
   4Boost Software License - Version 1.0 - August 17th, 2003
   5
   6Permission is hereby granted, free of charge, to any person or organization
   7obtaining a copy of the software and accompanying documentation covered by
   8this license (the "Software") to use, reproduce, display, distribute,
   9execute, and transmit the Software, and to prepare derivative works of the
  10Software, and to permit third-parties to whom the Software is furnished to
  11do so, all subject to the following:
  12
  13The copyright notices in the Software and this entire statement, including
  14the above license grant, this restriction and the following disclaimer,
  15must be included in all copies of the Software, in whole or in part, and
  16all derivative works of the Software, unless such copies or derivative
  17works are solely in the form of machine-executable object code generated by
  18a source language processor.
  19
  20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  23SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  24FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  25ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  26DEALINGS IN THE SOFTWARE.
  27*/
  28
  29#ifdef _MSC_VER
  30/* Enable full aliasing on MSVC */
  31/*#pragma optimize("a", on)*/
  32#endif
  33
  34/*#define FULLSANITYCHECKS*/
  35
  36#include "nedmalloc.h"
  37#if defined(WIN32)
  38 #include <malloc.h>
  39#endif
  40#define MSPACES 1
  41#define ONLY_MSPACES 1
  42#ifndef USE_LOCKS
  43 #define USE_LOCKS 1
  44#endif
  45#define FOOTERS 1           /* Need to enable footers so frees lock the right mspace */
  46#undef DEBUG                            /* dlmalloc wants DEBUG either 0 or 1 */
  47#ifdef _DEBUG
  48 #define DEBUG 1
  49#else
  50 #define DEBUG 0
  51#endif
  52#ifdef NDEBUG               /* Disable assert checking on release builds */
  53 #undef DEBUG
  54#endif
  55/* The default of 64Kb means we spend too much time kernel-side */
  56#ifndef DEFAULT_GRANULARITY
  57#define DEFAULT_GRANULARITY (1*1024*1024)
  58#endif
  59/*#define USE_SPIN_LOCKS 0*/
  60
  61
  62/*#define FORCEINLINE*/
  63#include "malloc.c.h"
  64#ifdef NDEBUG               /* Disable assert checking on release builds */
  65 #undef DEBUG
  66#endif
  67
  68/* The maximum concurrent threads in a pool possible */
  69#ifndef MAXTHREADSINPOOL
  70#define MAXTHREADSINPOOL 16
  71#endif
  72/* The maximum number of threadcaches which can be allocated */
  73#ifndef THREADCACHEMAXCACHES
  74#define THREADCACHEMAXCACHES 256
  75#endif
  76/* The maximum size to be allocated from the thread cache */
  77#ifndef THREADCACHEMAX
  78#define THREADCACHEMAX 8192
  79#endif
  80#if 0
  81/* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
  82#define THREADCACHEMAXBINS ((13-4)*2)
  83#else
  84/* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
  85#define THREADCACHEMAXBINS (13-4)
  86#endif
  87/* Point at which the free space in a thread cache is garbage collected */
  88#ifndef THREADCACHEMAXFREESPACE
  89#define THREADCACHEMAXFREESPACE (512*1024)
  90#endif
  91
  92
  93#ifdef WIN32
  94 #define TLSVAR                 DWORD
  95 #define TLSALLOC(k)    (*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
  96 #define TLSFREE(k)             (!TlsFree(k))
  97 #define TLSGET(k)              TlsGetValue(k)
  98 #define TLSSET(k, a)   (!TlsSetValue(k, a))
  99 #ifdef DEBUG
 100static LPVOID ChkedTlsGetValue(DWORD idx)
 101{
 102        LPVOID ret=TlsGetValue(idx);
 103        assert(S_OK==GetLastError());
 104        return ret;
 105}
 106  #undef TLSGET
 107  #define TLSGET(k) ChkedTlsGetValue(k)
 108 #endif
 109#else
 110 #define TLSVAR                 pthread_key_t
 111 #define TLSALLOC(k)    pthread_key_create(k, 0)
 112 #define TLSFREE(k)             pthread_key_delete(k)
 113 #define TLSGET(k)              pthread_getspecific(k)
 114 #define TLSSET(k, a)   pthread_setspecific(k, a)
 115#endif
 116
 117#if 0
 118/* Only enable if testing with valgrind. Causes misoperation */
 119#define mspace_malloc(p, s) malloc(s)
 120#define mspace_realloc(p, m, s) realloc(m, s)
 121#define mspace_calloc(p, n, s) calloc(n, s)
 122#define mspace_free(p, m) free(m)
 123#endif
 124
 125
 126#if defined(__cplusplus)
 127#if !defined(NO_NED_NAMESPACE)
 128namespace nedalloc {
 129#else
 130extern "C" {
 131#endif
 132#endif
 133
 134size_t nedblksize(void *mem) THROWSPEC
 135{
 136#if 0
 137        /* Only enable if testing with valgrind. Causes misoperation */
 138        return THREADCACHEMAX;
 139#else
 140        if(mem)
 141        {
 142                mchunkptr p=mem2chunk(mem);
 143                assert(cinuse(p));      /* If this fails, someone tried to free a block twice */
 144                if(cinuse(p))
 145                        return chunksize(p)-overhead_for(p);
 146        }
 147        return 0;
 148#endif
 149}
 150
 151void nedsetvalue(void *v) THROWSPEC                                     { nedpsetvalue(0, v); }
 152void * nedmalloc(size_t size) THROWSPEC                         { return nedpmalloc(0, size); }
 153void * nedcalloc(size_t no, size_t size) THROWSPEC      { return nedpcalloc(0, no, size); }
 154void * nedrealloc(void *mem, size_t size) THROWSPEC     { return nedprealloc(0, mem, size); }
 155void   nedfree(void *mem) THROWSPEC                                     { nedpfree(0, mem); }
 156void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
 157#if !NO_MALLINFO
 158struct mallinfo nedmallinfo(void) THROWSPEC                     { return nedpmallinfo(0); }
 159#endif
 160int    nedmallopt(int parno, int value) THROWSPEC       { return nedpmallopt(0, parno, value); }
 161int    nedmalloc_trim(size_t pad) THROWSPEC                     { return nedpmalloc_trim(0, pad); }
 162void   nedmalloc_stats(void) THROWSPEC                                  { nedpmalloc_stats(0); }
 163size_t nedmalloc_footprint(void) THROWSPEC                              { return nedpmalloc_footprint(0); }
 164void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC  { return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
 165void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC    { return nedpindependent_comalloc(0, elems, sizes, chunks); }
 166
 167struct threadcacheblk_t;
 168typedef struct threadcacheblk_t threadcacheblk;
 169struct threadcacheblk_t
 170{       /* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
 171#ifdef FULLSANITYCHECKS
 172        unsigned int magic;
 173#endif
 174        unsigned int lastUsed, size;
 175        threadcacheblk *next, *prev;
 176};
 177typedef struct threadcache_t
 178{
 179#ifdef FULLSANITYCHECKS
 180        unsigned int magic1;
 181#endif
 182        int mymspace;                                           /* Last mspace entry this thread used */
 183        long threadid;
 184        unsigned int mallocs, frees, successes;
 185        size_t freeInCache;                                     /* How much free space is stored in this cache */
 186        threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
 187#ifdef FULLSANITYCHECKS
 188        unsigned int magic2;
 189#endif
 190} threadcache;
 191struct nedpool_t
 192{
 193        MLOCK_T mutex;
 194        void *uservalue;
 195        int threads;                                            /* Max entries in m to use */
 196        threadcache *caches[THREADCACHEMAXCACHES];
 197        TLSVAR mycache;                                         /* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
 198        mstate m[MAXTHREADSINPOOL+1];           /* mspace entries for this pool */
 199};
 200static nedpool syspool;
 201
 202static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
 203{       /* 8=1000       16=10000        20=10100        24=11000        32=100000       48=110000       4096=1000000000000 */
 204        unsigned int topbit, size=(unsigned int)(_size>>4);
 205        /* 16=1         20=1    24=1    32=10   48=11   64=100  96=110  128=1000        4096=100000000 */
 206
 207#if defined(__GNUC__)
 208        topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
 209#elif defined(_MSC_VER) && _MSC_VER>=1300
 210        {
 211            unsigned long bsrTopBit;
 212
 213            _BitScanReverse(&bsrTopBit, size);
 214
 215            topbit = bsrTopBit;
 216        }
 217#else
 218#if 0
 219        union {
 220                unsigned asInt[2];
 221                double asDouble;
 222        };
 223        int n;
 224
 225        asDouble = (double)size + 0.5;
 226        topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
 227#else
 228        {
 229                unsigned int x=size;
 230                x = x | (x >> 1);
 231                x = x | (x >> 2);
 232                x = x | (x >> 4);
 233                x = x | (x >> 8);
 234                x = x | (x >>16);
 235                x = ~x;
 236                x = x - ((x >> 1) & 0x55555555);
 237                x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
 238                x = (x + (x >> 4)) & 0x0F0F0F0F;
 239                x = x + (x << 8);
 240                x = x + (x << 16);
 241                topbit=31 - (x >> 24);
 242        }
 243#endif
 244#endif
 245        return topbit;
 246}
 247
 248
 249#ifdef FULLSANITYCHECKS
 250static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
 251{
 252        assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
 253        if(ptr[0] && ptr[1])
 254        {
 255                assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
 256                assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
 257                assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
 258                assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
 259                assert(!ptr[0]->prev);
 260                assert(!ptr[1]->next);
 261                if(ptr[0]==ptr[1])
 262                {
 263                        assert(!ptr[0]->next);
 264                        assert(!ptr[1]->prev);
 265                }
 266        }
 267}
 268static void tcfullsanitycheck(threadcache *tc) THROWSPEC
 269{
 270        threadcacheblk **tcbptr=tc->bins;
 271        int n;
 272        for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
 273        {
 274                threadcacheblk *b, *ob=0;
 275                tcsanitycheck(tcbptr);
 276                for(b=tcbptr[0]; b; ob=b, b=b->next)
 277                {
 278                        assert(*(unsigned int *) "NEDN"==b->magic);
 279                        assert(!ob || ob->next==b);
 280                        assert(!ob || b->prev==ob);
 281                }
 282        }
 283}
 284#endif
 285
 286static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
 287{
 288#ifdef FULLSANITYCHECKS
 289        tcfullsanitycheck(tc);
 290#endif
 291        if(tc->freeInCache)
 292        {
 293                threadcacheblk **tcbptr=tc->bins;
 294                int n;
 295                for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
 296                {
 297                        threadcacheblk **tcb=tcbptr+1;          /* come from oldest end of list */
 298                        /*tcsanitycheck(tcbptr);*/
 299                        for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
 300                        {
 301                                threadcacheblk *f=*tcb;
 302                                size_t blksize=f->size; /*nedblksize(f);*/
 303                                assert(blksize<=nedblksize(f));
 304                                assert(blksize);
 305#ifdef FULLSANITYCHECKS
 306                                assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
 307#endif
 308                                *tcb=(*tcb)->prev;
 309                                if(*tcb)
 310                                        (*tcb)->next=0;
 311                                else
 312                                        *tcbptr=0;
 313                                tc->freeInCache-=blksize;
 314                                assert((long) tc->freeInCache>=0);
 315                                mspace_free(0, f);
 316                                /*tcsanitycheck(tcbptr);*/
 317                        }
 318                }
 319        }
 320#ifdef FULLSANITYCHECKS
 321        tcfullsanitycheck(tc);
 322#endif
 323}
 324static void DestroyCaches(nedpool *p) THROWSPEC
 325{
 326        if(p->caches)
 327        {
 328                threadcache *tc;
 329                int n;
 330                for(n=0; n<THREADCACHEMAXCACHES; n++)
 331                {
 332                        if((tc=p->caches[n]))
 333                        {
 334                                tc->frees++;
 335                                RemoveCacheEntries(p, tc, 0);
 336                                assert(!tc->freeInCache);
 337                                tc->mymspace=-1;
 338                                tc->threadid=0;
 339                                mspace_free(0, tc);
 340                                p->caches[n]=0;
 341                        }
 342                }
 343        }
 344}
 345
 346static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
 347{
 348        threadcache *tc=0;
 349        int n, end;
 350        ACQUIRE_LOCK(&p->mutex);
 351        for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
 352        if(THREADCACHEMAXCACHES==n)
 353        {       /* List exhausted, so disable for this thread */
 354                RELEASE_LOCK(&p->mutex);
 355                return 0;
 356        }
 357        tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
 358        if(!tc)
 359        {
 360                RELEASE_LOCK(&p->mutex);
 361                return 0;
 362        }
 363#ifdef FULLSANITYCHECKS
 364        tc->magic1=*(unsigned int *)"NEDMALC1";
 365        tc->magic2=*(unsigned int *)"NEDMALC2";
 366#endif
 367        tc->threadid=(long)(size_t)CURRENT_THREAD;
 368        for(end=0; p->m[end]; end++);
 369        tc->mymspace=tc->threadid % end;
 370        RELEASE_LOCK(&p->mutex);
 371        if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
 372        return tc;
 373}
 374
 375static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
 376{
 377        void *ret=0;
 378        unsigned int bestsize;
 379        unsigned int idx=size2binidx(*size);
 380        size_t blksize=0;
 381        threadcacheblk *blk, **binsptr;
 382#ifdef FULLSANITYCHECKS
 383        tcfullsanitycheck(tc);
 384#endif
 385        /* Calculate best fit bin size */
 386        bestsize=1<<(idx+4);
 387#if 0
 388        /* Finer grained bin fit */
 389        idx<<=1;
 390        if(*size>bestsize)
 391        {
 392                idx++;
 393                bestsize+=bestsize>>1;
 394        }
 395        if(*size>bestsize)
 396        {
 397                idx++;
 398                bestsize=1<<(4+(idx>>1));
 399        }
 400#else
 401        if(*size>bestsize)
 402        {
 403                idx++;
 404                bestsize<<=1;
 405        }
 406#endif
 407        assert(bestsize>=*size);
 408        if(*size<bestsize) *size=bestsize;
 409        assert(*size<=THREADCACHEMAX);
 410        assert(idx<=THREADCACHEMAXBINS);
 411        binsptr=&tc->bins[idx*2];
 412        /* Try to match close, but move up a bin if necessary */
 413        blk=*binsptr;
 414        if(!blk || blk->size<*size)
 415        {       /* Bump it up a bin */
 416                if(idx<THREADCACHEMAXBINS)
 417                {
 418                        idx++;
 419                        binsptr+=2;
 420                        blk=*binsptr;
 421                }
 422        }
 423        if(blk)
 424        {
 425                blksize=blk->size; /*nedblksize(blk);*/
 426                assert(nedblksize(blk)>=blksize);
 427                assert(blksize>=*size);
 428                if(blk->next)
 429                        blk->next->prev=0;
 430                *binsptr=blk->next;
 431                if(!*binsptr)
 432                        binsptr[1]=0;
 433#ifdef FULLSANITYCHECKS
 434                blk->magic=0;
 435#endif
 436                assert(binsptr[0]!=blk && binsptr[1]!=blk);
 437                assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
 438                /*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
 439                ret=(void *) blk;
 440        }
 441        ++tc->mallocs;
 442        if(ret)
 443        {
 444                assert(blksize>=*size);
 445                ++tc->successes;
 446                tc->freeInCache-=blksize;
 447                assert((long) tc->freeInCache>=0);
 448        }
 449#if defined(DEBUG) && 0
 450        if(!(tc->mallocs & 0xfff))
 451        {
 452                printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
 453                        (float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
 454        }
 455#endif
 456#ifdef FULLSANITYCHECKS
 457        tcfullsanitycheck(tc);
 458#endif
 459        return ret;
 460}
 461static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
 462{
 463        unsigned int age=THREADCACHEMAXFREESPACE/8192;
 464        /*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
 465        while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
 466        {
 467                RemoveCacheEntries(p, tc, age);
 468                /*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
 469                age>>=1;
 470        }
 471        /*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
 472}
 473static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
 474{
 475        unsigned int bestsize;
 476        unsigned int idx=size2binidx(size);
 477        threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
 478        assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
 479#ifdef DEBUG
 480        {       /* Make sure this is a valid memory block */
 481            mchunkptr p  = mem2chunk(mem);
 482            mstate fm = get_mstate_for(p);
 483            if (!ok_magic(fm)) {
 484              USAGE_ERROR_ACTION(fm, p);
 485              return;
 486            }
 487        }
 488#endif
 489#ifdef FULLSANITYCHECKS
 490        tcfullsanitycheck(tc);
 491#endif
 492        /* Calculate best fit bin size */
 493        bestsize=1<<(idx+4);
 494#if 0
 495        /* Finer grained bin fit */
 496        idx<<=1;
 497        if(size>bestsize)
 498        {
 499                unsigned int biggerbestsize=bestsize+bestsize<<1;
 500                if(size>=biggerbestsize)
 501                {
 502                        idx++;
 503                        bestsize=biggerbestsize;
 504                }
 505        }
 506#endif
 507        if(bestsize!=size)      /* dlmalloc can round up, so we round down to preserve indexing */
 508                size=bestsize;
 509        binsptr=&tc->bins[idx*2];
 510        assert(idx<=THREADCACHEMAXBINS);
 511        if(tck==*binsptr)
 512        {
 513                fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
 514                abort();
 515        }
 516#ifdef FULLSANITYCHECKS
 517        tck->magic=*(unsigned int *) "NEDN";
 518#endif
 519        tck->lastUsed=++tc->frees;
 520        tck->size=(unsigned int) size;
 521        tck->next=*binsptr;
 522        tck->prev=0;
 523        if(tck->next)
 524                tck->next->prev=tck;
 525        else
 526                binsptr[1]=tck;
 527        assert(!*binsptr || (*binsptr)->size==tck->size);
 528        *binsptr=tck;
 529        assert(tck==tc->bins[idx*2]);
 530        assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
 531        /*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
 532        tc->freeInCache+=size;
 533#ifdef FULLSANITYCHECKS
 534        tcfullsanitycheck(tc);
 535#endif
 536#if 1
 537        if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
 538                ReleaseFreeInCache(p, tc, mymspace);
 539#endif
 540}
 541
 542
 543
 544
 545static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
 546{       /* threads is -1 for system pool */
 547        ensure_initialization();
 548        ACQUIRE_MALLOC_GLOBAL_LOCK();
 549        if(p->threads) goto done;
 550        if(INITIAL_LOCK(&p->mutex)) goto err;
 551        if(TLSALLOC(&p->mycache)) goto err;
 552        if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
 553        p->m[0]->extp=p;
 554        p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
 555done:
 556        RELEASE_MALLOC_GLOBAL_LOCK();
 557        return 1;
 558err:
 559        if(threads<0)
 560                abort();                        /* If you can't allocate for system pool, we're screwed */
 561        DestroyCaches(p);
 562        if(p->m[0])
 563        {
 564                destroy_mspace(p->m[0]);
 565                p->m[0]=0;
 566        }
 567        if(p->mycache)
 568        {
 569                if(TLSFREE(p->mycache)) abort();
 570                p->mycache=0;
 571        }
 572        RELEASE_MALLOC_GLOBAL_LOCK();
 573        return 0;
 574}
 575static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
 576{       /* Gets called when thread's last used mspace is in use. The strategy
 577        is to run through the list of all available mspaces looking for an
 578        unlocked one and if we fail, we create a new one so long as we don't
 579        exceed p->threads */
 580        int n, end;
 581        for(n=end=*lastUsed+1; p->m[n]; end=++n)
 582        {
 583                if(TRY_LOCK(&p->m[n]->mutex)) goto found;
 584        }
 585        for(n=0; n<*lastUsed && p->m[n]; n++)
 586        {
 587                if(TRY_LOCK(&p->m[n]->mutex)) goto found;
 588        }
 589        if(end<p->threads)
 590        {
 591                mstate temp;
 592                if(!(temp=(mstate) create_mspace(size, 1)))
 593                        goto badexit;
 594                /* Now we're ready to modify the lists, we lock */
 595                ACQUIRE_LOCK(&p->mutex);
 596                while(p->m[end] && end<p->threads)
 597                        end++;
 598                if(end>=p->threads)
 599                {       /* Drat, must destroy it now */
 600                        RELEASE_LOCK(&p->mutex);
 601                        destroy_mspace((mspace) temp);
 602                        goto badexit;
 603                }
 604                /* We really want to make sure this goes into memory now but we
 605                have to be careful of breaking aliasing rules, so write it twice */
 606                {
 607                        volatile struct malloc_state **_m=(volatile struct malloc_state **) &p->m[end];
 608                        *_m=(p->m[end]=temp);
 609                }
 610                ACQUIRE_LOCK(&p->m[end]->mutex);
 611                /*printf("Created mspace idx %d\n", end);*/
 612                RELEASE_LOCK(&p->mutex);
 613                n=end;
 614                goto found;
 615        }
 616        /* Let it lock on the last one it used */
 617badexit:
 618        ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
 619        return p->m[*lastUsed];
 620found:
 621        *lastUsed=n;
 622        if(tc)
 623                tc->mymspace=n;
 624        else
 625        {
 626                if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
 627        }
 628        return p->m[n];
 629}
 630
 631nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
 632{
 633        nedpool *ret;
 634        if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
 635        if(!InitPool(ret, capacity, threads))
 636        {
 637                nedpfree(0, ret);
 638                return 0;
 639        }
 640        return ret;
 641}
 642void neddestroypool(nedpool *p) THROWSPEC
 643{
 644        int n;
 645        ACQUIRE_LOCK(&p->mutex);
 646        DestroyCaches(p);
 647        for(n=0; p->m[n]; n++)
 648        {
 649                destroy_mspace(p->m[n]);
 650                p->m[n]=0;
 651        }
 652        RELEASE_LOCK(&p->mutex);
 653        if(TLSFREE(p->mycache)) abort();
 654        nedpfree(0, p);
 655}
 656
 657void nedpsetvalue(nedpool *p, void *v) THROWSPEC
 658{
 659        if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
 660        p->uservalue=v;
 661}
 662void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
 663{
 664        nedpool *np=0;
 665        mchunkptr mcp=mem2chunk(mem);
 666        mstate fm;
 667        if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
 668        if(!cinuse(mcp)) return 0;
 669        if(!next_pinuse(mcp)) return 0;
 670        if(!is_mmapped(mcp) && !pinuse(mcp))
 671        {
 672                if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
 673        }
 674        fm=get_mstate_for(mcp);
 675        if(!ok_magic(fm)) return 0;
 676        if(!ok_address(fm, mcp)) return 0;
 677        if(!fm->extp) return 0;
 678        np=(nedpool *) fm->extp;
 679        if(p) *p=np;
 680        return np->uservalue;
 681}
 682
 683void neddisablethreadcache(nedpool *p) THROWSPEC
 684{
 685        int mycache;
 686        if(!p)
 687        {
 688                p=&syspool;
 689                if(!syspool.threads) InitPool(&syspool, 0, -1);
 690        }
 691        mycache=(int)(size_t) TLSGET(p->mycache);
 692        if(!mycache)
 693        {       /* Set to mspace 0 */
 694                if(TLSSET(p->mycache, (void *)-1)) abort();
 695        }
 696        else if(mycache>0)
 697        {       /* Set to last used mspace */
 698                threadcache *tc=p->caches[mycache-1];
 699#if defined(DEBUG)
 700                printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
 701                        100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
 702#endif
 703                if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
 704                tc->frees++;
 705                RemoveCacheEntries(p, tc, 0);
 706                assert(!tc->freeInCache);
 707                tc->mymspace=-1;
 708                tc->threadid=0;
 709                mspace_free(0, p->caches[mycache-1]);
 710                p->caches[mycache-1]=0;
 711        }
 712}
 713
 714#define GETMSPACE(m,p,tc,ms,s,action)           \
 715  do                                            \
 716  {                                             \
 717    mstate m = GetMSpace((p),(tc),(ms),(s));    \
 718    action;                                     \
 719    RELEASE_LOCK(&m->mutex);                    \
 720  } while (0)
 721
 722static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
 723{       /* Returns a locked and ready for use mspace */
 724        mstate m=p->m[mymspace];
 725        assert(m);
 726        if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
 727        /*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
 728        return m;
 729}
 730static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
 731{
 732        int mycache;
 733        if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
 734        if(!*p)
 735        {
 736                *p=&syspool;
 737                if(!syspool.threads) InitPool(&syspool, 0, -1);
 738        }
 739        mycache=(int)(size_t) TLSGET((*p)->mycache);
 740        if(mycache>0)
 741        {
 742                *tc=(*p)->caches[mycache-1];
 743                *mymspace=(*tc)->mymspace;
 744        }
 745        else if(!mycache)
 746        {
 747                *tc=AllocCache(*p);
 748                if(!*tc)
 749                {       /* Disable */
 750                        if(TLSSET((*p)->mycache, (void *)-1)) abort();
 751                        *mymspace=0;
 752                }
 753                else
 754                        *mymspace=(*tc)->mymspace;
 755        }
 756        else
 757        {
 758                *tc=0;
 759                *mymspace=-mycache-1;
 760        }
 761        assert(*mymspace>=0);
 762        assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
 763#ifdef FULLSANITYCHECKS
 764        if(*tc)
 765        {
 766                if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
 767                {
 768                        abort();
 769                }
 770        }
 771#endif
 772}
 773
 774void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
 775{
 776        void *ret=0;
 777        threadcache *tc;
 778        int mymspace;
 779        GetThreadCache(&p, &tc, &mymspace, &size);
 780#if THREADCACHEMAX
 781        if(tc && size<=THREADCACHEMAX)
 782        {       /* Use the thread cache */
 783                ret=threadcache_malloc(p, tc, &size);
 784        }
 785#endif
 786        if(!ret)
 787        {       /* Use this thread's mspace */
 788        GETMSPACE(m, p, tc, mymspace, size,
 789                  ret=mspace_malloc(m, size));
 790        }
 791        return ret;
 792}
 793void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
 794{
 795        size_t rsize=size*no;
 796        void *ret=0;
 797        threadcache *tc;
 798        int mymspace;
 799        GetThreadCache(&p, &tc, &mymspace, &rsize);
 800#if THREADCACHEMAX
 801        if(tc && rsize<=THREADCACHEMAX)
 802        {       /* Use the thread cache */
 803                if((ret=threadcache_malloc(p, tc, &rsize)))
 804                        memset(ret, 0, rsize);
 805        }
 806#endif
 807        if(!ret)
 808        {       /* Use this thread's mspace */
 809        GETMSPACE(m, p, tc, mymspace, rsize,
 810                  ret=mspace_calloc(m, 1, rsize));
 811        }
 812        return ret;
 813}
 814void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
 815{
 816        void *ret=0;
 817        threadcache *tc;
 818        int mymspace;
 819        if(!mem) return nedpmalloc(p, size);
 820        GetThreadCache(&p, &tc, &mymspace, &size);
 821#if THREADCACHEMAX
 822        if(tc && size && size<=THREADCACHEMAX)
 823        {       /* Use the thread cache */
 824                size_t memsize=nedblksize(mem);
 825                assert(memsize);
 826                if((ret=threadcache_malloc(p, tc, &size)))
 827                {
 828                        memcpy(ret, mem, memsize<size ? memsize : size);
 829                        if(memsize<=THREADCACHEMAX)
 830                                threadcache_free(p, tc, mymspace, mem, memsize);
 831                        else
 832                                mspace_free(0, mem);
 833                }
 834        }
 835#endif
 836        if(!ret)
 837        {       /* Reallocs always happen in the mspace they happened in, so skip
 838                locking the preferred mspace for this thread */
 839                ret=mspace_realloc(0, mem, size);
 840        }
 841        return ret;
 842}
 843void   nedpfree(nedpool *p, void *mem) THROWSPEC
 844{       /* Frees always happen in the mspace they happened in, so skip
 845        locking the preferred mspace for this thread */
 846        threadcache *tc;
 847        int mymspace;
 848        size_t memsize;
 849        assert(mem);
 850        GetThreadCache(&p, &tc, &mymspace, 0);
 851#if THREADCACHEMAX
 852        memsize=nedblksize(mem);
 853        assert(memsize);
 854        if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
 855                threadcache_free(p, tc, mymspace, mem, memsize);
 856        else
 857#endif
 858                mspace_free(0, mem);
 859}
 860void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
 861{
 862        void *ret;
 863        threadcache *tc;
 864        int mymspace;
 865        GetThreadCache(&p, &tc, &mymspace, &bytes);
 866        {       /* Use this thread's mspace */
 867        GETMSPACE(m, p, tc, mymspace, bytes,
 868                  ret=mspace_memalign(m, alignment, bytes));
 869        }
 870        return ret;
 871}
 872#if !NO_MALLINFO
 873struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
 874{
 875        int n;
 876        struct mallinfo ret={0};
 877        if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
 878        for(n=0; p->m[n]; n++)
 879        {
 880                struct mallinfo t=mspace_mallinfo(p->m[n]);
 881                ret.arena+=t.arena;
 882                ret.ordblks+=t.ordblks;
 883                ret.hblkhd+=t.hblkhd;
 884                ret.usmblks+=t.usmblks;
 885                ret.uordblks+=t.uordblks;
 886                ret.fordblks+=t.fordblks;
 887                ret.keepcost+=t.keepcost;
 888        }
 889        return ret;
 890}
 891#endif
 892int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
 893{
 894        return mspace_mallopt(parno, value);
 895}
 896int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
 897{
 898        int n, ret=0;
 899        if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
 900        for(n=0; p->m[n]; n++)
 901        {
 902                ret+=mspace_trim(p->m[n], pad);
 903        }
 904        return ret;
 905}
 906void   nedpmalloc_stats(nedpool *p) THROWSPEC
 907{
 908        int n;
 909        if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
 910        for(n=0; p->m[n]; n++)
 911        {
 912                mspace_malloc_stats(p->m[n]);
 913        }
 914}
 915size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
 916{
 917        size_t ret=0;
 918        int n;
 919        if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
 920        for(n=0; p->m[n]; n++)
 921        {
 922                ret+=mspace_footprint(p->m[n]);
 923        }
 924        return ret;
 925}
 926void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
 927{
 928        void **ret;
 929        threadcache *tc;
 930        int mymspace;
 931        GetThreadCache(&p, &tc, &mymspace, &elemsize);
 932    GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
 933              ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
 934        return ret;
 935}
 936void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
 937{
 938        void **ret;
 939        threadcache *tc;
 940        int mymspace;
 941    size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
 942    if(!adjustedsizes) return 0;
 943    for(i=0; i<elems; i++)
 944        adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
 945        GetThreadCache(&p, &tc, &mymspace, 0);
 946        GETMSPACE(m, p, tc, mymspace, 0,
 947              ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
 948        return ret;
 949}
 950
 951#ifdef OVERRIDE_STRDUP
 952/*
 953 * This implementation is purely there to override the libc version, to
 954 * avoid a crash due to allocation and free on different 'heaps'.
 955 */
 956char *strdup(const char *s1)
 957{
 958        char *s2 = 0;
 959        if (s1) {
 960                size_t len = strlen(s1) + 1;
 961                s2 = malloc(len);
 962                memcpy(s2, s1, len);
 963        }
 964        return s2;
 965}
 966#endif
 967
 968#if defined(__cplusplus)
 969}
 970#endif