nsprpub/lib/ds/plarena.c

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * Lifetime-based fast allocation, inspired by much prior art, including
     8  * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
     9  * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
    10  */
    11 #include <stdlib.h>
    12 #include <string.h>
    13 #include "plarena.h"
    14 #include "prmem.h"
    15 #include "prbit.h"
    16 #include "prlog.h"
    17 #include "prlock.h"
    18 #include "prinit.h"
    20 static PLArena *arena_freelist;
    22 #ifdef PL_ARENAMETER
    23 static PLArenaStats *arena_stats_list;
    25 #define COUNT(pool,what)  (pool)->stats.what++
    26 #else
    27 #define COUNT(pool,what)  /* nothing */
    28 #endif
    30 #define PL_ARENA_DEFAULT_ALIGN  sizeof(double)
    32 static PRLock    *arenaLock;
    33 static PRCallOnceType once;
    34 static const PRCallOnceType pristineCallOnce;
    36 /*
    37 ** InitializeArenas() -- Initialize arena operations.
    38 **
    39 ** InitializeArenas() is called exactly once and only once from 
    40 ** LockArena(). This function creates the arena protection 
    41 ** lock: arenaLock.
    42 **
    43 ** Note: If the arenaLock cannot be created, InitializeArenas()
    44 ** fails quietly, returning only PR_FAILURE. This percolates up
    45 ** to the application using the Arena API. He gets no arena
    46 ** from PL_ArenaAllocate(). It's up to him to fail gracefully
    47 ** or recover.
    48 **
    49 */
    50 static PRStatus InitializeArenas( void )
    51 {
    52     PR_ASSERT( arenaLock == NULL );
    53     arenaLock = PR_NewLock();
    54     if ( arenaLock == NULL )
    55         return PR_FAILURE;
    56     else
    57         return PR_SUCCESS;
    58 } /* end ArenaInitialize() */
    60 static PRStatus LockArena( void )
    61 {
    62     PRStatus rc = PR_CallOnce( &once, InitializeArenas );
    64     if ( PR_FAILURE != rc )
    65         PR_Lock( arenaLock );
    66     return(rc);
    67 } /* end LockArena() */
    69 static void UnlockArena( void )
    70 {
    71     PR_Unlock( arenaLock );
    72     return;
    73 } /* end UnlockArena() */
    75 PR_IMPLEMENT(void) PL_InitArenaPool(
    76     PLArenaPool *pool, const char *name, PRUint32 size, PRUint32 align)
    77 {
    78     /*
    79      * Look-up table of PR_BITMASK(PR_CeilingLog2(align)) values for
    80      * align = 1 to 32.
    81      */
    82     static const PRUint8 pmasks[33] = {
    83          0,                                               /*  not used */
    84          0, 1, 3, 3, 7, 7, 7, 7,15,15,15,15,15,15,15,15,  /*  1 ... 16 */
    85         31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31}; /* 17 ... 32 */
    87     if (align == 0)
    88         align = PL_ARENA_DEFAULT_ALIGN;
    90     if (align < sizeof(pmasks)/sizeof(pmasks[0]))
    91         pool->mask = pmasks[align];
    92     else
    93         pool->mask = PR_BITMASK(PR_CeilingLog2(align));
    95     pool->first.next = NULL;
    96     pool->first.base = pool->first.avail = pool->first.limit =
    97         (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
    98     pool->current = &pool->first;
    99     /*
   100      * Compute the net size so that each arena's gross size is |size|.
   101      * sizeof(PLArena) + pool->mask is the header and alignment slop
   102      * that PL_ArenaAllocate adds to the net size.
   103      */
   104     if (size > sizeof(PLArena) + pool->mask)
   105         pool->arenasize = size - (sizeof(PLArena) + pool->mask);
   106     else
   107         pool->arenasize = size;
   108 #ifdef PL_ARENAMETER
   109     memset(&pool->stats, 0, sizeof pool->stats);
   110     pool->stats.name = strdup(name);
   111     pool->stats.next = arena_stats_list;
   112     arena_stats_list = &pool->stats;
   113 #endif
   114 }
   117 /*
   118 ** PL_ArenaAllocate() -- allocate space from an arena pool
   119 ** 
   120 ** Description: PL_ArenaAllocate() allocates space from an arena
   121 ** pool. 
   122 **
   123 ** First, try to satisfy the request from arenas starting at
   124 ** pool->current.
   125 **
   126 ** If there is not enough space in the arena pool->current, try
   127 ** to claim an arena, on a first fit basis, from the global
   128 ** freelist (arena_freelist).
   129 ** 
   130 ** If no arena in arena_freelist is suitable, then try to
   131 ** allocate a new arena from the heap.
   132 **
   133 ** Returns: pointer to allocated space or NULL
   134 ** 
   135 ** Notes: The original implementation had some difficult to
   136 ** solve bugs; the code was difficult to read. Sometimes it's
   137 ** just easier to rewrite it. I did that. larryh.
   138 **
   139 ** See also: bugzilla: 45343.
   140 **
   141 */
   143 PR_IMPLEMENT(void *) PL_ArenaAllocate(PLArenaPool *pool, PRUint32 nb)
   144 {
   145     PLArena *a;   
   146     char *rp;     /* returned pointer */
   148     PR_ASSERT((nb & pool->mask) == 0);
   150     nb = (PRUword)PL_ARENA_ALIGN(pool, nb); /* force alignment */
   152     /* attempt to allocate from arenas at pool->current */
   153     {
   154         a = pool->current;
   155         do {
   156             if ( nb <= a->limit - a->avail )  {
   157                 pool->current = a;
   158                 rp = (char *)a->avail;
   159                 a->avail += nb;
   160                 return rp;
   161             }
   162         } while( NULL != (a = a->next) );
   163     }
   165     /* attempt to allocate from arena_freelist */
   166     {
   167         PLArena *p; /* previous pointer, for unlinking from freelist */
   169         /* lock the arena_freelist. Make access to the freelist MT-Safe */
   170         if ( PR_FAILURE == LockArena())
   171             return(0);
   173         for ( a = arena_freelist, p = NULL; a != NULL ; p = a, a = a->next ) {
   174             if ( nb <= a->limit - a->base )  {
   175                 if ( p == NULL )
   176                     arena_freelist = a->next;
   177                 else
   178                     p->next = a->next;
   179                 UnlockArena();
   180                 a->avail = a->base;
   181                 rp = (char *)a->avail;
   182                 a->avail += nb;
   183                 /* the newly allocated arena is linked after pool->current 
   184                 *  and becomes pool->current */
   185                 a->next = pool->current->next;
   186                 pool->current->next = a;
   187                 pool->current = a;
   188                 if ( NULL == pool->first.next )
   189                     pool->first.next = a;
   190                 return(rp);
   191             }
   192         }
   193         UnlockArena();
   194     }
   196     /* attempt to allocate from the heap */ 
   197     {  
   198         PRUint32 sz = PR_MAX(pool->arenasize, nb);
   199         if (PR_UINT32_MAX - sz < sizeof *a + pool->mask) {
   200             a = NULL;
   201         } else {
   202             sz += sizeof *a + pool->mask;  /* header and alignment slop */
   203             a = (PLArena*)PR_MALLOC(sz);
   204         }
   205         if ( NULL != a )  {
   206             a->limit = (PRUword)a + sz;
   207             a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
   208             PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
   209             rp = (char *)a->avail;
   210             a->avail += nb;
   211             /* the newly allocated arena is linked after pool->current 
   212             *  and becomes pool->current */
   213             a->next = pool->current->next;
   214             pool->current->next = a;
   215             pool->current = a;
   216             if ( NULL == pool->first.next )
   217                 pool->first.next = a;
   218             PL_COUNT_ARENA(pool,++);
   219             COUNT(pool, nmallocs);
   220             return(rp);
   221         }
   222     }
   224     /* we got to here, and there's no memory to allocate */
   225     return(NULL);
   226 } /* --- end PL_ArenaAllocate() --- */
   228 PR_IMPLEMENT(void *) PL_ArenaGrow(
   229     PLArenaPool *pool, void *p, PRUint32 size, PRUint32 incr)
   230 {
   231     void *newp;
   233     PL_ARENA_ALLOCATE(newp, pool, size + incr);
   234     if (newp)
   235         memcpy(newp, p, size);
   236     return newp;
   237 }
   239 static void ClearArenaList(PLArena *a, PRInt32 pattern)
   240 {
   242     for (; a; a = a->next) {
   243         PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
   244         a->avail = a->base;
   245         PL_CLEAR_UNUSED_PATTERN(a, pattern);
   246         PL_MAKE_MEM_NOACCESS((void*)a->avail, a->limit - a->avail);
   247     }
   248 }
   250 PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool *pool, PRInt32 pattern)
   251 {
   252     ClearArenaList(pool->first.next, pattern);
   253 }
   255 /*
   256  * Free tail arenas linked after head, which may not be the true list head.
   257  * Reset pool->current to point to head in case it pointed at a tail arena.
   258  */
   259 static void FreeArenaList(PLArenaPool *pool, PLArena *head, PRBool reallyFree)
   260 {
   261     PLArena **ap, *a;
   263     ap = &head->next;
   264     a = *ap;
   265     if (!a)
   266         return;
   268 #ifdef DEBUG
   269     ClearArenaList(a, PL_FREE_PATTERN);
   270 #endif
   272     if (reallyFree) {
   273         do {
   274             *ap = a->next;
   275             PL_CLEAR_ARENA(a);
   276             PL_COUNT_ARENA(pool,--);
   277             PR_DELETE(a);
   278         } while ((a = *ap) != 0);
   279     } else {
   280         /* Insert the whole arena chain at the front of the freelist. */
   281         do {
   282             PL_MAKE_MEM_NOACCESS((void*)(*ap)->base,
   283                                  (*ap)->limit - (*ap)->base);
   284             ap = &(*ap)->next;
   285         } while (*ap);
   286         LockArena();
   287         *ap = arena_freelist;
   288         arena_freelist = a;
   289         head->next = 0;
   290         UnlockArena();
   291     }
   293     pool->current = head;
   294 }
   296 PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool *pool, char *mark)
   297 {
   298     PLArena *a;
   300     for (a = &pool->first; a; a = a->next) {
   301         if (PR_UPTRDIFF(mark, a->base) <= PR_UPTRDIFF(a->avail, a->base)) {
   302             a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
   303             FreeArenaList(pool, a, PR_FALSE);
   304             return;
   305         }
   306     }
   307 }
   309 PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool *pool)
   310 {
   311     FreeArenaList(pool, &pool->first, PR_FALSE);
   312     COUNT(pool, ndeallocs);
   313 }
   315 PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool *pool)
   316 {
   317     FreeArenaList(pool, &pool->first, PR_TRUE);
   318 #ifdef PL_ARENAMETER
   319     {
   320         PLArenaStats *stats, **statsp;
   322         if (pool->stats.name)
   323             PR_DELETE(pool->stats.name);
   324         for (statsp = &arena_stats_list; (stats = *statsp) != 0;
   325              statsp = &stats->next) {
   326             if (stats == &pool->stats) {
   327                 *statsp = stats->next;
   328                 return;
   329             }
   330         }
   331     }
   332 #endif
   333 }
   335 PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool *ap)
   336 {
   337 }
   339 PR_IMPLEMENT(void) PL_ArenaFinish(void)
   340 {
   341     PLArena *a, *next;
   343     for (a = arena_freelist; a; a = next) {
   344         next = a->next;
   345         PR_DELETE(a);
   346     }
   347     arena_freelist = NULL;
   349     if (arenaLock) {
   350         PR_DestroyLock(arenaLock);
   351         arenaLock = NULL;
   352     }
   353     once = pristineCallOnce;
   354 }
   356 PR_IMPLEMENT(size_t) PL_SizeOfArenaPoolExcludingPool(
   357     const PLArenaPool *pool, PLMallocSizeFn mallocSizeOf)
   358 {
   359     /*
   360      * The first PLArena is within |pool|, so don't measure it.  Subsequent
   361      * PLArenas are separate and must be measured.
   362      */
   363     size_t size = 0;
   364     const PLArena *arena = pool->first.next;
   365     while (arena) {
   366         size += mallocSizeOf(arena);
   367         arena = arena->next;
   368     }
   369     return size;
   370 }
   372 #ifdef PL_ARENAMETER
   373 PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb)
   374 {
   375     pool->stats.nallocs++;
   376     pool->stats.nbytes += nb;
   377     if (nb > pool->stats.maxalloc)
   378         pool->stats.maxalloc = nb;
   379     pool->stats.variance += nb * nb;
   380 }
   382 PR_IMPLEMENT(void) PL_ArenaCountInplaceGrowth(
   383     PLArenaPool *pool, PRUint32 size, PRUint32 incr)
   384 {
   385     pool->stats.ninplace++;
   386 }
   388 PR_IMPLEMENT(void) PL_ArenaCountGrowth(
   389     PLArenaPool *pool, PRUint32 size, PRUint32 incr)
   390 {
   391     pool->stats.ngrows++;
   392     pool->stats.nbytes += incr;
   393     pool->stats.variance -= size * size;
   394     size += incr;
   395     if (size > pool->stats.maxalloc)
   396         pool->stats.maxalloc = size;
   397     pool->stats.variance += size * size;
   398 }
   400 PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark)
   401 {
   402     pool->stats.nreleases++;
   403 }
   405 PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark)
   406 {
   407     pool->stats.nfastrels++;
   408 }
   410 #include <math.h>
   411 #include <stdio.h>
   413 PR_IMPLEMENT(void) PL_DumpArenaStats(FILE *fp)
   414 {
   415     PLArenaStats *stats;
   416     double mean, variance;
   418     for (stats = arena_stats_list; stats; stats = stats->next) {
   419         if (stats->nallocs != 0) {
   420             mean = (double)stats->nbytes / stats->nallocs;
   421             variance = fabs(stats->variance / stats->nallocs - mean * mean);
   422         } else {
   423             mean = variance = 0;
   424         }
   426         fprintf(fp, "\n%s allocation statistics:\n", stats->name);
   427         fprintf(fp, "              number of arenas: %u\n", stats->narenas);
   428         fprintf(fp, "         number of allocations: %u\n", stats->nallocs);
   429         fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
   430         fprintf(fp, "        number of malloc calls: %u\n", stats->nmallocs);
   431         fprintf(fp, "       number of deallocations: %u\n", stats->ndeallocs);
   432         fprintf(fp, "  number of allocation growths: %u\n", stats->ngrows);
   433         fprintf(fp, "    number of in-place growths: %u\n", stats->ninplace);
   434         fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
   435         fprintf(fp, "       number of fast releases: %u\n", stats->nfastrels);
   436         fprintf(fp, "         total bytes allocated: %u\n", stats->nbytes);
   437         fprintf(fp, "          mean allocation size: %g\n", mean);
   438         fprintf(fp, "            standard deviation: %g\n", sqrt(variance));
   439         fprintf(fp, "       maximum allocation size: %u\n", stats->maxalloc);
   440     }
   441 }
   442 #endif /* PL_ARENAMETER */

mercurial