LCOV - code coverage report
Current view: directory - nsprpub/lib/ds - plarena.c (source / functions) Found Hit Coverage
Test: app.info Lines: 127 107 84.3 %
Date: 2012-06-02 Functions: 14 12 85.7 %

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

Generated by: LCOV version 1.7