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 */
|