chiark / gitweb /
@@@ fltfmt wip
[mLib] / mem / pool.c
1 /* -*-c-*-
2  *
3  * Resource pool handling
4  *
5  * (c) 2000 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "config.h"
31
32 #include <string.h>
33
34 #ifdef HAVE_VALGRIND_VALGRIND_H
35 #  include <valgrind/valgrind.h>
36 #  include <valgrind/memcheck.h>
37 #  define VG(x) x
38 #else
39 #  define VG(x)
40 #endif
41
42 #include "align.h"
43 #include "alloc.h"
44 #include "arena.h"
45 #include "pool.h"
46
47 /*----- Constants ---------------------------------------------------------*/
48
49 #define REDZONE_SIZE (2*ALIGNOF(union align))
50
51 /*----- Main code ---------------------------------------------------------*/
52
53 /* --- @doalloc@ --- *
54  *
55  * Arguments:   @arena *a@ = pointer to arena to allocate memory from
56  *              @pool_chunk **cc@ = pointer to chunk list
57  *              @size_t sz@ = size of memory wanted
58  *              @unsigned f@ = flags (@PF_...@)
59  *
60  * Returns:     Pointer to the allocated block.
61  *
62  * Use:         The basic allocator for resource pools.  This is also used
63  *              during pool creation, hence the slightly bizarre interface.
64  */
65
66 static void *doalloc(arena *a, pool_chunk **cc, size_t sz, unsigned f)
67 {
68   pool_chunk *c;
69   void *p;
70   size_t csz, ssz;
71   size_t redsz = VG( f&PF_VALGRIND ? REDZONE_SIZE : ) 0;
72   VG( size_t sz0 = sz; )
73
74   /* --- See if there's enough space --- *
75    *
76    * The chunks are sorted by available space, so if there's not enough space
77    * in the first chunk there isn't enough space anywhere.
78    */
79
80   ALIGN(sz); sz += redsz;
81   c = *cc;
82   if (c && c->left >= sz) {
83     p = c->p; VG( if (f&PF_VALGRIND) VALGRIND_MEMPOOL_ALLOC(c, p, sz0); )
84     c->p += sz; c->left -= sz;
85     *cc = c->next;
86   }
87
88   /* --- Failed to find anything --- *
89    *
90    * I must allocate a new block from the arena, then.
91    */
92
93   else {
94     ssz = sizeof(pool_chunk);
95     ALIGN(ssz);
96     csz = (ssz + redsz + sz + POOL_CHUNKSZ - 1); csz -= csz % POOL_CHUNKSZ;
97     c = x_alloc(a, csz);
98     p = (unsigned char *)c + ssz + redsz;
99     VG( if (f&PF_VALGRIND) {
100           VALGRIND_CREATE_MEMPOOL(c, REDZONE_SIZE, 0);
101           VALGRIND_MEMPOOL_ALLOC(c, p, sz0);
102         } )
103     c->p = (unsigned char *)p + sz;
104     c->left = csz - ssz - redsz - sz;
105   }
106
107   /* --- Move this chunk in the list so that it's sorted --- */
108
109   while (*cc && (*cc)->left > c->left) cc = &(*cc)->next;
110   c->next = *cc; *cc = c;
111
112   /* --- Done --- */
113
114   return (p);
115 }
116
117 /* --- @pool_alloc@ --- *
118  *
119  * Arguments:   @pool *p@ = pool to allocate from
120  *              @size_t sz@ = size of block wanted
121  *
122  * Returns:     Pointer to the requested block.
123  *
124  * Use:         Allocates memory from a resource pool.  Memory is never freed
125  *              from pools: it is released when the pool is destroyed.
126  */
127
128 void *pool_alloc(pool *p, size_t sz)
129   { return (doalloc(p->pa, &p->c, sz, p->f)); }
130
131 /* --- @pool_strdup@ --- *
132  *
133  * Arguments:   @pool *p@ = pool to allocate from
134  *              @const char *s@ = pointer to string
135  *
136  * Returns:     A pointer to a copy of the string.
137  *
138  * Use:         Allocates a copy of a string.
139  */
140
141 char *pool_strdup(pool *p, const char *s)
142 {
143   size_t sz = strlen(s) + 1;
144   char *pp = doalloc(p->pa, &p->c, sz, p->f);
145
146   memcpy(pp, s, sz);
147   return (pp);
148 }
149
150 /* --- Arena operations --- */
151
152 static void *palloc(arena *a, size_t sz)
153 {
154   pool *p = (pool *)a;
155
156   return (doalloc(p->pa, &p->c, sz, p->f));
157 }
158
159 static void pfree(arena *a, void *p) { return; } /* Trivial */
160
161 static arena_ops pool_ops = { palloc, arena_fakerealloc, pfree, 0 };
162
163 /* --- @pool_create@ --- *
164  *
165  * Arguments:   @arena *a@ = pointer to an arena to allocate memory from
166  *
167  * Returns:     A newly created resource pool.
168  *
169  * Use:         Creates a resource pool which is not a child of any other
170  *              resource pool.
171  */
172
173 pool *pool_create(arena *a)
174 {
175   pool_chunk *c = 0;
176   pool *p;
177   unsigned f = 0;
178
179   VG( if (RUNNING_ON_VALGRIND) f |= PF_VALGRIND; )
180   p = doalloc(a, &c, sizeof(pool), f);
181   p->c = c; p->r = 0; p->pa = a; p->f = f;
182   p->a.ops = &pool_ops;
183   return (p);
184 }
185
186 /* --- @pool_destroy@ --- *
187  *
188  * Arguments:   @pool *p@ = pointer to pool to destroy
189  *
190  * Returns:     ---
191  *
192  * Use:         Destroys a pool, freeing all of the resources within it.  If
193  *              this is a pool created by @pool_create@, its memory will be
194  *              deallocated; if it's a subpool or it was initialized by
195  *              @pool_init@, it is emptied and can be used again.
196  */
197
198 void pool_destroy(pool *p)
199 {
200   pool_resource *r, *rr;
201   arena *a;
202   pool_chunk *c, *cc;
203
204   /* --- Dispose of all of the resources --- */
205
206   r = p->r;
207   while (r) {
208     rr = r->next;
209     if (r->destroy)
210       r->destroy(r);
211     r = rr;
212   }
213   p->r = 0;
214
215   /* --- Free all of the memory --- *
216    *
217    * Since root pools are allocated in their own memory, this will free the
218    * root pool block.  Subpools are allocated in their parent's memory, so
219    * the pool block itself will be left around.
220    */
221
222   a = p->pa; c = p->c; p->c = 0;
223   while (c) {
224     cc = c->next;
225     VG( if (p->f&PF_VALGRIND) VALGRIND_DESTROY_MEMPOOL(c); )
226     x_free(a, c);
227     c = cc;
228   }
229 }
230
231 /* --- @pool_add@ --- *
232  *
233  * Arguments:   @pool *p@ = pointer to pool to add the resource to
234  *              @pool_resource *r@ = pointer to resource block
235  *              @void (*dfn)(pool_resource *r)@ = destruction function
236  *
237  * Returns:     ---
238  *
239  * Use:         Adds a resource to a pool.
240  */
241
242 void pool_add(pool *p, pool_resource *r, void (*dfn)(pool_resource *r))
243 {
244   POOL_ADD(p, r, dfn);
245 }
246
247 /* --- @pool_sub@ --- *
248  *
249  * Arguments:   @pool *p@ = pointer to parent pool
250  *
251  * Returns:     A new child pool of the parent.
252  *
253  * Use:         Creates a subpool.  The subpool can either be destroyed on
254  *              its own, or will be automatically destroyed at the same time
255  *              as the parent.
256  */
257
258 typedef struct subpool {
259   pool_resource r;
260   pool p;
261 } subpool;
262
263 static void subpool_destroy(pool_resource *r)
264 {
265   subpool *p = (subpool *)r;
266   pool_destroy(&p->p);
267 }
268
269 pool *pool_sub(pool *p)
270 {
271   subpool *pp = pool_alloc(p, sizeof(subpool));
272   POOL_ADD(p, &pp->r, subpool_destroy);
273   pp->p.a.ops = &pool_ops;
274   pp->p.c = 0;
275   pp->p.r = 0;
276   pp->p.pa = p->pa;
277   return (&pp->p);
278 }
279
280 /*----- That's all, folks -------------------------------------------------*/