1/*
2 * Copyright (c) 2003-2006,2008,2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * The contents of this file are subject to the Mozilla Public
25 * License Version 1.1 (the "License"); you may not use this file
26 * except in compliance with the License. You may obtain a copy of
27 * the License at http://www.mozilla.org/MPL/
28 *
29 * Software distributed under the License is distributed on an "AS
30 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
31 * implied. See the License for the specific language governing
32 * rights and limitations under the License.
33 *
34 * The Original Code is the Netscape Portable Runtime (NSPR).
35 *
36 * The Initial Developer of the Original Code is Netscape
37 * Communications Corporation.  Portions created by Netscape are
38 * Copyright (C) 1998-2000 Netscape Communications Corporation.  All
39 * Rights Reserved.
40 *
41 * Contributor(s):
42 *
43 * Alternatively, the contents of this file may be used under the
44 * terms of the GNU General Public License Version 2 or later (the
45 * "GPL"), in which case the provisions of the GPL are applicable
46 * instead of those above.  If you wish to allow use of your
47 * version of this file only under the terms of the GPL and not to
48 * allow others to use your version of this file under the MPL,
49 * indicate your decision by deleting the provisions above and
50 * replace them with the notice and other provisions required by
51 * the GPL.  If you do not delete the provisions above, a recipient
52 * may use your version of this file under either the MPL or the
53 * GPL.
54 */
55
56/*
57 * Lifetime-based fast allocation, inspired by much prior art, including
58 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
59 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
60 */
61#include <stdlib.h>
62#include <string.h>
63#include "plarena.h"
64#include "prmem.h"
65#include "prbit.h"
66#include "prlog.h"
67#include "prinit.h"
68
69#ifdef PL_ARENAMETER
70static PLArenaStats *arena_stats_list;
71
72#define COUNT(pool,what)  (pool)->stats.what++
73#else
74#define COUNT(pool,what)  /* nothing */
75#endif
76
77#define PL_ARENA_DEFAULT_ALIGN  sizeof(double)
78
79PR_IMPLEMENT(void) PL_InitArenaPool(
80    PLArenaPool *pool, const char *name, PRUint32 size, PRUint32 align)
81{
82#if !defined (__GNUC__)
83#pragma unused (name)
84#endif
85
86    if (align == 0)
87        align = PL_ARENA_DEFAULT_ALIGN;
88    pool->mask = PR_BITMASK(PR_CeilingLog2(align));
89    pool->first.next = NULL;
90    pool->first.base = pool->first.avail = pool->first.limit =
91        (PRUword)PL_ARENA_ALIGN(pool, &pool->first + 1);
92    pool->current = &pool->first;
93    pool->arenasize = size;
94#ifdef PL_ARENAMETER
95    memset(&pool->stats, 0, sizeof pool->stats);
96    pool->stats.name = strdup(name);
97    pool->stats.next = arena_stats_list;
98    arena_stats_list = &pool->stats;
99#endif
100}
101
102
103/*
104** PL_ArenaAllocate() -- allocate space from an arena pool
105**
106** Description: PL_ArenaAllocate() allocates space from an arena
107** pool.
108**
109** First, try to satisfy the request from arenas starting at
110** pool->current. Then try to allocate a new arena from the heap.
111**
112** Returns: pointer to allocated space or NULL
113**
114** Notes: The original implementation had some difficult to
115** solve bugs; the code was difficult to read. Sometimes it's
116** just easier to rewrite it. I did that. larryh.
117**
118** See also: bugzilla: 45343.
119**
120*/
121
122PR_IMPLEMENT(void *) PL_ArenaAllocate(PLArenaPool *pool, PRUint32 nb)
123{
124    PLArena *a;
125    char *rp;     /* returned pointer */
126
127    PR_ASSERT((nb & pool->mask) == 0);
128
129    nb = (PRUint32)PL_ARENA_ALIGN(pool, nb); /* force alignment */
130
131    /* attempt to allocate from arenas at pool->current */
132    {
133        a = pool->current;
134        do {
135            if ( a->avail +nb <= a->limit )  {
136                pool->current = a;
137                rp = (char *)a->avail;
138                a->avail += nb;
139                return rp;
140            }
141        } while( NULL != (a = a->next) );
142    }
143
144    /* attempt to allocate from the heap */
145    {
146        PRSize sz = PR_MAX(pool->arenasize, nb);
147        sz += sizeof(*a) + pool->mask;  /* header and alignment slop */
148        a = (PLArena*)PR_MALLOC(sz);
149        if ( NULL != a )  {
150            a->limit = (PRUword)a + sz;
151            a->base = a->avail = (PRUword)PL_ARENA_ALIGN(pool, a + 1);
152            rp = (char *)a->avail;
153            a->avail += nb;
154            /* the newly allocated arena is linked after pool->current
155            *  and becomes pool->current */
156            a->next = pool->current->next;
157            pool->current->next = a;
158            pool->current = a;
159            if ( NULL == pool->first.next )
160                pool->first.next = a;
161            PL_COUNT_ARENA(pool,++);
162            COUNT(pool, nmallocs);
163            return(rp);
164        }
165    }
166
167    /* we got to here, and there's no memory to allocate */
168    return(NULL);
169} /* --- end PL_ArenaAllocate() --- */
170
171/*
172 * Grow, a.k.a. realloc. The PL_ARENA_GROW macro has already handled
173 * the possible grow-in-place action in which the current PLArena is the
174 * source of the incoming pointer, and there is room in that arena for
175 * the requested size.
176 */
177PR_IMPLEMENT(void *) PL_ArenaGrow(
178    PLArenaPool *pool, void *p, PRUint32 origSize, PRUint32 incr)
179{
180    void *newp;
181	PLArena *thisArena;
182	PLArena *lastArena;
183	PRUint32 origAlignSize;		// bytes currently reserved for caller
184	PRUint32 newSize;			// bytes actually mallocd here
185
186	/* expand at least by 2x */
187	origAlignSize = PL_ARENA_ALIGN(pool, origSize);
188	newSize = PR_MAX(origAlignSize+incr, 2*origAlignSize);
189	newSize = PL_ARENA_ALIGN(pool, newSize);
190    PL_ARENA_ALLOCATE(newp, pool, newSize);
191    if (newp == NULL) {
192		return NULL;
193	}
194
195	/*
196	 * Trim back the memory we just allocated to the amount our caller really
197	 * needs, leaving the remainder for grow-in-place on subsequent calls
198	 * to PL_ARENA_GROW.
199	 */
200	PRUint32 newAlignSize = PL_ARENA_ALIGN(pool, origSize+incr);
201	PR_ASSERT(pool->current->avail == ((PRUword)newp + newSize));
202	pool->current->avail = (PRUword)newp + newAlignSize;
203	PR_ASSERT(pool->current->avail <= pool->current->limit);
204
205	/* "realloc" */
206	memcpy(newp, p, origSize);
207
208	/*
209	 * Free old memory only if it's the entire outstanding allocated
210	 * memory associated with one of our known PLArenas.
211	 */
212	lastArena = &pool->first;			/* pool->first always empty */
213	thisArena = lastArena->next;		/* so, start here */
214
215	PRUword origPtr = (PRUword)p;
216	while(thisArena != NULL) {
217		if(origPtr == thisArena->base) {
218			if((origPtr + origAlignSize) == thisArena->avail) {
219				/* unlink */
220				lastArena->next = thisArena->next;
221
222				/* and free */
223				PL_CLEAR_ARENA(thisArena);
224				PL_COUNT_ARENA(pool,--);
225				PR_DELETE(thisArena);
226				break;
227			}
228		}
229		lastArena = thisArena;
230		thisArena = thisArena->next;
231	}
232	/*
233	 * Note: inability to free is not an error; it just causes a temporary leak
234	 * of the old buffer (until the arena pool is freed, of course).
235	 */
236    return newp;
237}
238
239/*
240 * Free tail arenas linked after head, which may not be the true list head.
241 * Reset pool->current to point to head in case it pointed at a tail arena.
242 */
243static void FreeArenaList(PLArenaPool *pool, PLArena *head, PRBool reallyFree)
244{
245    PLArena **ap, *a;
246
247    ap = &head->next;
248    a = *ap;
249    if (!a)
250        return;
251
252	do {
253		*ap = a->next;
254		PL_CLEAR_ARENA(a);
255		PL_COUNT_ARENA(pool,--);
256		PR_DELETE(a);
257	} while ((a = *ap) != 0);
258
259    pool->current = head;
260}
261
262PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool *pool, char *mark)
263{
264	#if ARENA_MARK_ENABLE
265    PLArena *a;
266
267    for (a = pool->first.next; a; a = a->next) {
268        if (PR_UPTRDIFF(mark, a->base) < PR_UPTRDIFF(a->avail, a->base)) {
269            a->avail = (PRUword)PL_ARENA_ALIGN(pool, mark);
270            FreeArenaList(pool, a, PR_FALSE);
271            return;
272        }
273    }
274	#endif	/* ARENA_MARK_ENABLE */
275}
276
277PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool *pool)
278{
279    FreeArenaList(pool, &pool->first, PR_FALSE);
280    COUNT(pool, ndeallocs);
281}
282
283PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool *pool)
284{
285    FreeArenaList(pool, &pool->first, PR_TRUE);
286#ifdef PL_ARENAMETER
287    {
288        PLArenaStats *stats, **statsp;
289
290        if (pool->stats.name)
291            PR_DELETE(pool->stats.name);
292        for (statsp = &arena_stats_list; (stats = *statsp) != 0;
293             statsp = &stats->next) {
294            if (stats == &pool->stats) {
295                *statsp = stats->next;
296                return;
297            }
298        }
299    }
300#endif
301}
302
303PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool *ap)
304{
305}
306
307PR_IMPLEMENT(void) PL_ArenaFinish(void)
308{
309}
310
311#ifdef PL_ARENAMETER
312PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool *pool, PRUint32 nb)
313{
314    pool->stats.nallocs++;
315    pool->stats.nbytes += nb;
316    if (nb > pool->stats.maxalloc)
317        pool->stats.maxalloc = nb;
318    pool->stats.variance += nb * nb;
319}
320
321PR_IMPLEMENT(void) PL_ArenaCountInplaceGrowth(
322    PLArenaPool *pool, PRUint32 size, PRUint32 incr)
323{
324    pool->stats.ninplace++;
325}
326
327PR_IMPLEMENT(void) PL_ArenaCountGrowth(
328    PLArenaPool *pool, PRUint32 size, PRUint32 incr)
329{
330    pool->stats.ngrows++;
331    pool->stats.nbytes += incr;
332    pool->stats.variance -= size * size;
333    size += incr;
334    if (size > pool->stats.maxalloc)
335        pool->stats.maxalloc = size;
336    pool->stats.variance += size * size;
337}
338
339PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool *pool, char *mark)
340{
341    pool->stats.nreleases++;
342}
343
344PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool *pool, char *mark)
345{
346    pool->stats.nfastrels++;
347}
348
349#include <math.h>
350#include <stdio.h>
351
352PR_IMPLEMENT(void) PL_DumpArenaStats(FILE *fp)
353{
354    PLArenaStats *stats;
355    double mean, variance;
356
357    for (stats = arena_stats_list; stats; stats = stats->next) {
358        if (stats->nallocs != 0) {
359            mean = (double)stats->nbytes / stats->nallocs;
360            variance = fabs(stats->variance / stats->nallocs - mean * mean);
361        } else {
362            mean = variance = 0;
363        }
364
365        fprintf(fp, "\n%s allocation statistics:\n", stats->name);
366        fprintf(fp, "              number of arenas: %u\n", stats->narenas);
367        fprintf(fp, "         number of allocations: %u\n", stats->nallocs);
368        fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
369        fprintf(fp, "        number of malloc calls: %u\n", stats->nmallocs);
370        fprintf(fp, "       number of deallocations: %u\n", stats->ndeallocs);
371        fprintf(fp, "  number of allocation growths: %u\n", stats->ngrows);
372        fprintf(fp, "    number of in-place growths: %u\n", stats->ninplace);
373        fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
374        fprintf(fp, "       number of fast releases: %u\n", stats->nfastrels);
375        fprintf(fp, "         total bytes allocated: %u\n", stats->nbytes);
376        fprintf(fp, "          mean allocation size: %g\n", mean);
377        fprintf(fp, "            standard deviation: %g\n", sqrt(variance));
378        fprintf(fp, "       maximum allocation size: %u\n", stats->maxalloc);
379    }
380}
381#endif /* PL_ARENAMETER */
382