1 /* An Apache-like pool allocator.
2 * By Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: pool.c,v 1.8 2002/10/13 12:25:42 rich Exp $
23 #if SIZEOF_VOID_P != SIZEOF_LONG
24 #error "This library currently assumes that sizeof (void *) == sizeof (long), which is not the case on your platform. This may cause the library to crash at runtime."
27 /* If set, then calls to pmalloc will initialise the memory to 0xefefef...,
28 * helping to catch uninitialised memory problems. This is very useful for
29 * debugging new code, but should be turned off on production systems.
31 #define DEBUG_UNINITIALISED_MEMORY 1
52 struct _pool_allocs *next;
54 /* The flags field contains:
55 * bit 31 == if set, this structure shouldn't be freed
56 * bits 30-16 == number of slots in the structure
57 * bits 15- 0 == number of slots used in the structure.
61 #define _PA_NO_FREE(pa) ((pa)->flags & 0x80000000U)
62 #define _PA_SLOTS(pa) (((pa)->flags & 0x7fff0000U) >> 16)
63 #define _PA_SLOTS_USED(pa) ((pa)->flags & 0xffffU)
66 } __attribute__((packed));
68 struct _pool_cleanup_slot
76 struct _pool_cleanups *next;
78 /* The flags field contains:
79 * bit 31 == if set, this structure shouldn't be freed
80 * bits 30-16 == number of slots in the structure
81 * bits 15- 0 == number of slots used in the structure.
85 #define _PC_NO_FREE(pc) ((pc)->flags & 0x80000000U)
86 #define _PC_SLOTS(pc) (((pc)->flags & 0x7fff0000U) >> 16)
87 #define _PC_SLOTS_USED(pc) ((pc)->flags & 0xffffU)
89 struct _pool_cleanup_slot slot[0];
90 } __attribute__((packed));
92 #define INITIAL_PA_SLOTS 16U
93 #define MAX_PA_SLOTS 16384U /* Must be <= 16384 */
94 #define INITIAL_PC_SLOTS 2U
95 #define MAX_PC_SLOTS 16384U /* Must be <= 16384 */
99 /* If this is a subpool, then this points to the parent. */
100 struct pool *parent_pool;
102 /* When subpools are stored on a list, this is used to link the list. */
106 struct pool *subpool_list;
108 /* Pointer to head block of memory allocations. */
109 struct _pool_allocs *allocs;
111 /* Pointer to head block of clean-up functions. */
112 struct _pool_cleanups *cleanups;
115 #ifndef NO_GLOBAL_POOL
119 static int trace_fd = -1;
120 static const char *trace_filename = 0;
122 static void (*bad_malloc_handler) (void) = abort;
123 #ifndef NO_GLOBAL_POOL
124 static void alloc_global_pool (void) __attribute__((constructor));
125 static void free_global_pool (void) __attribute__((destructor));
127 static void open_trace_file (void) __attribute__((constructor));
128 static void trace (const char *fn, void *caller, struct pool *ptr1, void *ptr2, void *ptr3, int i1);
130 #define TRACE(ptr1, ptr2, ptr3, i1) do { if (trace_filename) trace (__PRETTY_FUNCTION__, __builtin_return_address (0), (ptr1), (ptr2), (ptr3), (i1)); } while (0)
135 /* The amount of space required for pool + allocs + cleanups. */
137 = sizeof (struct pool) +
138 sizeof (struct _pool_allocs) +
139 INITIAL_PA_SLOTS * sizeof (void *) +
140 sizeof (struct _pool_cleanups) +
141 INITIAL_PC_SLOTS * sizeof (struct _pool_cleanup_slot);
143 pool p = malloc (size);
144 if (p == 0) bad_malloc_handler ();
148 p->allocs = (struct _pool_allocs *) ((void *)p + sizeof (struct pool));
149 p->cleanups = (struct _pool_cleanups *)
150 ((void *)p + sizeof (struct pool) + sizeof (struct _pool_allocs)
151 + INITIAL_PA_SLOTS * sizeof (void *));
153 p->allocs->flags = 0x80000000U | INITIAL_PA_SLOTS << 16;
154 p->cleanups->flags = 0x80000000U | INITIAL_PC_SLOTS << 16;
162 new_subpool (pool parent)
164 pool p = new_pool ();
165 p->parent_pool = parent;
167 p->next = parent->subpool_list;
168 parent->subpool_list = p;
170 TRACE (p, parent, 0, 0);
176 _do_cleanups (pool p)
178 struct _pool_cleanups *pc, *pc_next;
181 for (pc = p->cleanups; pc; pc = pc_next)
185 for (i = 0; i < _PC_SLOTS_USED (pc); ++i)
186 pc->slot[i].fn (pc->slot[i].data);
187 if (!_PC_NO_FREE (pc))
195 struct _pool_allocs *pa, *pa_next;
198 for (pa = p->allocs; pa; pa = pa_next)
202 for (i = 0; i < _PA_SLOTS_USED (pa); ++i)
204 if (!_PA_NO_FREE (pa))
214 /* Clean up any sub-pools. */
215 while (p->subpool_list) delete_pool (p->subpool_list);
219 /* Do I have a parent? If so, remove myself from my parent's subpool
224 pool parent = p->parent_pool, this, last = 0;
226 for (this = parent->subpool_list; this; last = this, this = this->next)
230 /* Remove this one. */
232 last->next = this->next;
234 parent->subpool_list = this->next;
240 abort (); /* Oops - self not found on subpool list. */
250 pmalloc (pool p, size_t n)
255 if (ptr == 0) bad_malloc_handler ();
257 #if DEBUG_UNINITIALISED_MEMORY
258 memset (ptr, 0xef, n);
261 pool_register_malloc (p, ptr);
263 TRACE (p, ptr, 0, n);
269 pcalloc (pool p, size_t nmemb, size_t size)
271 void *ptr = pmalloc (p, nmemb * size);
272 if (ptr) memset (ptr, 0, nmemb * size);
277 prealloc (pool p, void *ptr, size_t n)
279 struct _pool_allocs *pa;
284 return pmalloc (p, n);
286 new_ptr = realloc (ptr, n);
287 if (new_ptr == 0) bad_malloc_handler ();
289 /* XXX This is inefficient. We need to search through the
290 * allocations to find this one and update the pointer.
294 for (pa = p->allocs; pa; pa = pa->next)
296 for (i = 0; i < _PA_SLOTS_USED (pa); ++i)
297 if (pa->slot[i] == ptr)
299 pa->slot[i] = new_ptr;
308 TRACE (p, ptr, new_ptr, n);
314 pool_register_cleanup_fn (pool p, void (*fn) (void *), void *data)
317 struct _pool_cleanups *pc;
319 if (_PC_SLOTS_USED (p->cleanups) < _PC_SLOTS (p->cleanups))
322 p->cleanups->slot[_PC_SLOTS_USED(p->cleanups)].fn = fn;
323 p->cleanups->slot[_PC_SLOTS_USED(p->cleanups)].data = data;
324 p->cleanups->flags++;
328 /* Allocate a new block of cleanup slots. */
329 nr_slots = _PC_SLOTS (p->cleanups);
330 if (nr_slots < MAX_PC_SLOTS)
333 pc = malloc (sizeof (struct _pool_cleanups) +
334 nr_slots * sizeof (struct _pool_cleanup_slot));
335 if (pc == 0) bad_malloc_handler ();
336 pc->next = p->cleanups;
337 pc->flags = nr_slots << 16;
344 pool_register_malloc (pool p, void *ptr)
347 struct _pool_allocs *pa;
349 if (_PA_SLOTS_USED (p->allocs) < _PA_SLOTS (p->allocs))
352 p->allocs->slot[_PA_SLOTS_USED(p->allocs)] = ptr;
357 /* Allocate a new block of slots. */
358 nr_slots = _PA_SLOTS (p->allocs);
359 if (nr_slots < MAX_PA_SLOTS)
362 pa = malloc (sizeof (struct _pool_allocs) + nr_slots * sizeof (void *));
363 if (pa == 0) bad_malloc_handler ();
364 pa->next = p->allocs;
365 pa->flags = nr_slots << 16;
372 _pool_close (void *fdv)
374 long fd = (long) fdv;
379 pool_register_fd (pool p, int fd)
381 pool_register_cleanup_fn (p, _pool_close, (void *) (long) fd);
384 #ifndef NO_GLOBAL_POOL
388 global_pool = new_pool ();
394 delete_pool (global_pool);
396 #endif /* !NO_GLOBAL_POOL */
399 pool_set_bad_malloc_handler (void (*fn) (void))) (void)
401 void (*old_fn) (void) = bad_malloc_handler;
402 bad_malloc_handler = fn;
407 _get_struct_size (const pool p)
410 struct _pool_allocs *pa;
411 struct _pool_cleanups *pc;
416 for (pa = p->allocs; pa; pa = pa->next)
417 size += sizeof (struct _pool_allocs)
418 + _PA_SLOTS (pa) * sizeof (void *);
420 for (pc = p->cleanups; pc; pc = pc->next)
421 size += sizeof (struct _pool_cleanups)
422 + _PC_SLOTS (pc) * sizeof (struct _pool_cleanup_slot);
424 for (subpool = p->subpool_list; subpool; subpool = subpool->next)
425 size += _get_struct_size (subpool);
431 _get_nr_subpools (const pool p)
436 for (subpool = p->subpool_list; subpool; subpool = subpool->next)
437 count += _get_nr_subpools (subpool);
443 pool_get_stats (const pool p, struct pool_stats *stats, size_t n)
447 s.nr_subpools = _get_nr_subpools (p);
448 s.struct_size = _get_struct_size (p);
450 memcpy (stats, &s, n);
458 "Pool allocator running in trace mode.\n"
459 "Trace is being saved to file ";
460 char msg2[] = "\n\n";
462 trace_filename = getenv ("POOL_TRACE");
466 trace_fd = open (trace_filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
469 perror (trace_filename);
473 write (2, msg1, sizeof msg1);
474 write (2, trace_filename, strlen (trace_filename));
475 write (2, msg2, sizeof msg2);
480 trace (const char *fn, void *caller, struct pool *ptr1, void *ptr2, void *ptr3, int i1)
485 "%s caller: %p ptr1: %p ptr2: %p ptr3: %p i1: %d\n",
486 fn, caller, ptr1, ptr2, ptr3, i1);
487 write (trace_fd, buffer, strlen (buffer));