Merge from vendor branch FILE:
[dragonfly.git] / contrib / sendmail / libsm / rpool.c
1 /*
2  * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  */
9
10 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: rpool.c,v 1.24 2002/01/11 21:54:43 ca Exp $")
12
13 /*
14 **  resource pools
15 **  For documentation, see rpool.html
16 */
17
18 #include <sm/exc.h>
19 #include <sm/heap.h>
20 #include <sm/rpool.h>
21 #include <sm/varargs.h>
22 #include <sm/conf.h>
23 #if _FFR_PERF_RPOOL
24 # include <syslog.h>
25 #endif /* _FFR_PERF_RPOOL */
26
27 const char SmRpoolMagic[] = "sm_rpool";
28
29 typedef union
30 {
31         SM_POOLLINK_T   link;
32         char            align[SM_ALIGN_SIZE];
33 } SM_POOLHDR_T;
34
35 /*
36 **  Tune this later
37 */
38
39 #define POOLSIZE                4096
40 #define BIG_OBJECT_RATIO        10
41
42 /*
43 **  SM_RPOOL_ALLOCBLOCK_X -- allocate a new block for an rpool.
44 **
45 **      Parameters:
46 **              rpool -- rpool to which the block should be added.
47 **              size -- size of block.
48 **
49 **      Returns:
50 **              Pointer to block.
51 **
52 **      Exceptions:
53 **              F:sm_heap -- out of memory
54 */
55
56 static char *
57 sm_rpool_allocblock_x(rpool, size)
58         SM_RPOOL_T *rpool;
59         size_t size;
60 {
61         SM_POOLLINK_T *p;
62
63         p = sm_malloc_x(sizeof(SM_POOLHDR_T) + size);
64         p->sm_pnext = rpool->sm_pools;
65         rpool->sm_pools = p;
66         return (char*) p + sizeof(SM_POOLHDR_T);
67 }
68
69 /*
70 **  SM_RPOOL_ALLOCBLOCK -- allocate a new block for an rpool.
71 **
72 **      Parameters:
73 **              rpool -- rpool to which the block should be added.
74 **              size -- size of block.
75 **
76 **      Returns:
77 **              Pointer to block, NULL on failure.
78 */
79
80 static char *
81 sm_rpool_allocblock(rpool, size)
82         SM_RPOOL_T *rpool;
83         size_t size;
84 {
85         SM_POOLLINK_T *p;
86
87         p = sm_malloc(sizeof(SM_POOLHDR_T) + size);
88         if (p == NULL)
89                 return NULL;
90         p->sm_pnext = rpool->sm_pools;
91         rpool->sm_pools = p;
92         return (char*) p + sizeof(SM_POOLHDR_T);
93 }
94
95 /*
96 **  SM_RPOOL_MALLOC_TAGGED_X -- allocate memory from rpool
97 **
98 **      Parameters:
99 **              rpool -- rpool from which memory should be allocated;
100 **                      can be NULL, use sm_malloc() then.
101 **              size -- size of block.
102 **              file -- filename.
103 **              line -- line number in file.
104 **              group -- heap group for debugging.
105 **
106 **      Returns:
107 **              Pointer to block.
108 **
109 **      Exceptions:
110 **              F:sm_heap -- out of memory
111 **
112 **      Notice: XXX
113 **              if size == 0 and the rpool is new (no memory
114 **              allocated yet) NULL is returned!
115 **              We could solve this by
116 **              - wasting 1 byte (size < avail)
117 **              - checking for rpool->sm_poolptr != NULL
118 **              - not asking for 0 sized buffer
119 */
120
121 void *
122 #if SM_HEAP_CHECK
123 sm_rpool_malloc_tagged_x(rpool, size, file, line, group)
124         SM_RPOOL_T *rpool;
125         size_t size;
126         char *file;
127         int line;
128         int group;
129 #else /* SM_HEAP_CHECK */
130 sm_rpool_malloc_x(rpool, size)
131         SM_RPOOL_T *rpool;
132         size_t size;
133 #endif /* SM_HEAP_CHECK */
134 {
135         char *ptr;
136
137         if (rpool == NULL)
138                 return sm_malloc_tagged_x(size, file, line, group);
139
140         /* Ensure that size is properly aligned. */
141         if (size & SM_ALIGN_BITS)
142                 size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE;
143
144         /* The common case.  This is optimized for speed. */
145         if (size <= rpool->sm_poolavail)
146         {
147                 ptr = rpool->sm_poolptr;
148                 rpool->sm_poolptr += size;
149                 rpool->sm_poolavail -= size;
150                 return ptr;
151         }
152
153         /*
154         **  The slow case: we need to call malloc.
155         **  The SM_REQUIRE assertion is deferred until now, for speed.
156         **  That's okay: we set rpool->sm_poolavail to 0 when we free an rpool,
157         **  so the common case code won't be triggered on a dangling pointer.
158         */
159
160         SM_REQUIRE(rpool->sm_magic == SmRpoolMagic);
161
162         /*
163         **  If size > sm_poolsize, then malloc a new block especially for
164         **  this request.  Future requests will be allocated from the
165         **  current pool.
166         **
167         **  What if the current pool is mostly unallocated, and the current
168         **  request is larger than the available space, but < sm_poolsize?
169         **  If we discard the current pool, and start allocating from a new
170         **  pool, then we will be wasting a lot of space.  For this reason,
171         **  we malloc a block just for the current request if size >
172         **  sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize.
173         **  Thus, the most space that we will waste at the end of a pool
174         **  is sm_bigobjectsize - 1.
175         */
176
177         if (size > rpool->sm_bigobjectsize)
178         {
179 #if _FFR_PERF_RPOOL
180                 ++rpool->sm_nbigblocks;
181 #endif /* _FFR_PERF_RPOOL */
182                 return sm_rpool_allocblock_x(rpool, size);
183         }
184         SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize);
185         ptr = sm_rpool_allocblock_x(rpool, rpool->sm_poolsize);
186         rpool->sm_poolptr = ptr + size;
187         rpool->sm_poolavail = rpool->sm_poolsize - size;
188 #if _FFR_PERF_RPOOL
189         ++rpool->sm_npools;
190 #endif /* _FFR_PERF_RPOOL */
191         return ptr;
192 }
193
194 /*
195 **  SM_RPOOL_MALLOC_TAGGED -- allocate memory from rpool
196 **
197 **      Parameters:
198 **              rpool -- rpool from which memory should be allocated;
199 **                      can be NULL, use sm_malloc() then.
200 **              size -- size of block.
201 **              file -- filename.
202 **              line -- line number in file.
203 **              group -- heap group for debugging.
204 **
205 **      Returns:
206 **              Pointer to block, NULL on failure.
207 **
208 **      Notice: XXX
209 **              if size == 0 and the rpool is new (no memory
210 **              allocated yet) NULL is returned!
211 **              We could solve this by
212 **              - wasting 1 byte (size < avail)
213 **              - checking for rpool->sm_poolptr != NULL
214 **              - not asking for 0 sized buffer
215 */
216
217 void *
218 #if SM_HEAP_CHECK
219 sm_rpool_malloc_tagged(rpool, size, file, line, group)
220         SM_RPOOL_T *rpool;
221         size_t size;
222         char *file;
223         int line;
224         int group;
225 #else /* SM_HEAP_CHECK */
226 sm_rpool_malloc(rpool, size)
227         SM_RPOOL_T *rpool;
228         size_t size;
229 #endif /* SM_HEAP_CHECK */
230 {
231         char *ptr;
232
233         if (rpool == NULL)
234                 return sm_malloc_tagged(size, file, line, group);
235
236         /* Ensure that size is properly aligned. */
237         if (size & SM_ALIGN_BITS)
238                 size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE;
239
240         /* The common case.  This is optimized for speed. */
241         if (size <= rpool->sm_poolavail)
242         {
243                 ptr = rpool->sm_poolptr;
244                 rpool->sm_poolptr += size;
245                 rpool->sm_poolavail -= size;
246                 return ptr;
247         }
248
249         /*
250         **  The slow case: we need to call malloc.
251         **  The SM_REQUIRE assertion is deferred until now, for speed.
252         **  That's okay: we set rpool->sm_poolavail to 0 when we free an rpool,
253         **  so the common case code won't be triggered on a dangling pointer.
254         */
255
256         SM_REQUIRE(rpool->sm_magic == SmRpoolMagic);
257
258         /*
259         **  If size > sm_poolsize, then malloc a new block especially for
260         **  this request.  Future requests will be allocated from the
261         **  current pool.
262         **
263         **  What if the current pool is mostly unallocated, and the current
264         **  request is larger than the available space, but < sm_poolsize?
265         **  If we discard the current pool, and start allocating from a new
266         **  pool, then we will be wasting a lot of space.  For this reason,
267         **  we malloc a block just for the current request if size >
268         **  sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize.
269         **  Thus, the most space that we will waste at the end of a pool
270         **  is sm_bigobjectsize - 1.
271         */
272
273         if (size > rpool->sm_bigobjectsize)
274         {
275 #if _FFR_PERF_RPOOL
276                 ++rpool->sm_nbigblocks;
277 #endif /* _FFR_PERF_RPOOL */
278                 return sm_rpool_allocblock(rpool, size);
279         }
280         SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize);
281         ptr = sm_rpool_allocblock(rpool, rpool->sm_poolsize);
282         if (ptr == NULL)
283                 return NULL;
284         rpool->sm_poolptr = ptr + size;
285         rpool->sm_poolavail = rpool->sm_poolsize - size;
286 #if _FFR_PERF_RPOOL
287         ++rpool->sm_npools;
288 #endif /* _FFR_PERF_RPOOL */
289         return ptr;
290 }
291
292 /*
293 **  SM_RPOOL_NEW_X -- create a new rpool.
294 **
295 **      Parameters:
296 **              parent -- pointer to parent rpool, can be NULL.
297 **
298 **      Returns:
299 **              Pointer to new rpool.
300 */
301
302 SM_RPOOL_T *
303 sm_rpool_new_x(parent)
304         SM_RPOOL_T *parent;
305 {
306         SM_RPOOL_T *rpool;
307
308         rpool = sm_malloc_x(sizeof(SM_RPOOL_T));
309         if (parent == NULL)
310                 rpool->sm_parentlink = NULL;
311         else
312         {
313                 SM_TRY
314                         rpool->sm_parentlink = sm_rpool_attach_x(parent,
315                                         (SM_RPOOL_RFREE_T) sm_rpool_free,
316                                         (void *) rpool);
317                 SM_EXCEPT(exc, "*")
318                         sm_free(rpool);
319                         sm_exc_raise_x(exc);
320                 SM_END_TRY
321         }
322         rpool->sm_magic = SmRpoolMagic;
323
324         rpool->sm_poolsize = POOLSIZE - sizeof(SM_POOLHDR_T);
325         rpool->sm_bigobjectsize = rpool->sm_poolsize / BIG_OBJECT_RATIO;
326         rpool->sm_poolptr = NULL;
327         rpool->sm_poolavail = 0;
328         rpool->sm_pools = NULL;
329
330         rpool->sm_rptr = NULL;
331         rpool->sm_ravail = 0;
332         rpool->sm_rlists = NULL;
333 #if _FFR_PERF_RPOOL
334         rpool->sm_nbigblocks = 0;
335         rpool->sm_npools = 0;
336 #endif /* _FFR_PERF_RPOOL */
337
338         return rpool;
339 }
340
341 /*
342 **  SM_RPOOL_SETSIZES -- set sizes for rpool.
343 **
344 **      Parameters:
345 **              poolsize -- size of a single rpool block.
346 **              bigobjectsize -- if this size is exceeded, an individual
347 **                      block is allocated (must be less or equal poolsize).
348 **
349 **      Returns:
350 **              none.
351 */
352
353 void
354 sm_rpool_setsizes(rpool, poolsize, bigobjectsize)
355         SM_RPOOL_T *rpool;
356         size_t poolsize;
357         size_t bigobjectsize;
358 {
359         SM_REQUIRE(poolsize >= bigobjectsize);
360         if (poolsize == 0)
361                 poolsize = POOLSIZE - sizeof(SM_POOLHDR_T);
362         if (bigobjectsize == 0)
363                 bigobjectsize = poolsize / BIG_OBJECT_RATIO;
364         rpool->sm_poolsize = poolsize;
365         rpool->sm_bigobjectsize = bigobjectsize;
366 }
367
368 /*
369 **  SM_RPOOL_FREE -- free an rpool and release all of its resources.
370 **
371 **      Parameters:
372 **              rpool -- rpool to free.
373 **
374 **      Returns:
375 **              none.
376 */
377
378 void
379 sm_rpool_free(rpool)
380         SM_RPOOL_T *rpool;
381 {
382         SM_RLIST_T *rl, *rnext;
383         SM_RESOURCE_T *r, *rmax;
384         SM_POOLLINK_T *pp, *pnext;
385
386         if (rpool == NULL)
387                 return;
388
389         /*
390         **  It's important to free the resources before the memory pools,
391         **  because the resource free functions might modify the contents
392         **  of the memory pools.
393         */
394
395         rl = rpool->sm_rlists;
396         if (rl != NULL)
397         {
398                 rmax = rpool->sm_rptr;
399                 for (;;)
400                 {
401                         for (r = rl->sm_rvec; r < rmax; ++r)
402                         {
403                                 if (r->sm_rfree != NULL)
404                                         r->sm_rfree(r->sm_rcontext);
405                         }
406                         rnext = rl->sm_rnext;
407                         sm_free(rl);
408                         if (rnext == NULL)
409                                 break;
410                         rl = rnext;
411                         rmax = &rl->sm_rvec[SM_RLIST_MAX];
412                 }
413         }
414
415         /*
416         **  Now free the memory pools.
417         */
418
419         for (pp = rpool->sm_pools; pp != NULL; pp = pnext)
420         {
421                 pnext = pp->sm_pnext;
422                 sm_free(pp);
423         }
424
425         /*
426         **  Disconnect rpool from its parent.
427         */
428
429         if (rpool->sm_parentlink != NULL)
430                 *rpool->sm_parentlink = NULL;
431
432         /*
433         **  Setting these fields to zero means that any future to attempt
434         **  to use the rpool after it is freed will cause an assertion failure.
435         */
436
437         rpool->sm_magic = NULL;
438         rpool->sm_poolavail = 0;
439         rpool->sm_ravail = 0;
440
441 #if _FFR_PERF_RPOOL
442         if (rpool->sm_nbigblocks > 0 || rpool->sm_npools > 1)
443                 syslog(LOG_NOTICE,
444                         "perf: rpool=%lx, sm_nbigblocks=%d, sm_npools=%d",
445                         (long) rpool, rpool->sm_nbigblocks, rpool->sm_npools);
446         rpool->sm_nbigblocks = 0;
447         rpool->sm_npools = 0;
448 #endif /* _FFR_PERF_RPOOL */
449         sm_free(rpool);
450 }
451
452 /*
453 **  SM_RPOOL_ATTACH_X -- attach a resource to an rpool.
454 **
455 **      Parameters:
456 **              rpool -- rpool to which resource should be attached.
457 **              rfree -- function to call when rpool is freed.
458 **              rcontext -- argument for function to call when rpool is freed.
459 **
460 **      Returns:
461 **              Pointer to allocated function.
462 **
463 **      Exceptions:
464 **              F:sm_heap -- out of memory
465 */
466
467 SM_RPOOL_ATTACH_T
468 sm_rpool_attach_x(rpool, rfree, rcontext)
469         SM_RPOOL_T *rpool;
470         SM_RPOOL_RFREE_T rfree;
471         void *rcontext;
472 {
473         SM_RLIST_T *rl;
474         SM_RPOOL_ATTACH_T a;
475
476         SM_REQUIRE_ISA(rpool, SmRpoolMagic);
477
478         if (rpool->sm_ravail == 0)
479         {
480                 rl = sm_malloc_x(sizeof(SM_RLIST_T));
481                 rl->sm_rnext = rpool->sm_rlists;
482                 rpool->sm_rlists = rl;
483                 rpool->sm_rptr = rl->sm_rvec;
484                 rpool->sm_ravail = SM_RLIST_MAX;
485         }
486
487         a = &rpool->sm_rptr->sm_rfree;
488         rpool->sm_rptr->sm_rfree = rfree;
489         rpool->sm_rptr->sm_rcontext = rcontext;
490         ++rpool->sm_rptr;
491         --rpool->sm_ravail;
492         return a;
493 }