source: trunk/third/sendmail/libsm/rpool.c @ 19204

Revision 19204, 11.1 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
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>
11SM_RCSID("@(#)$Id: rpool.c,v 1.1.1.1 2003-04-08 15:06:48 zacheiss 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
27const char SmRpoolMagic[] = "sm_rpool";
28
29typedef 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
56static char *
57sm_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
80static char *
81sm_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
121void *
122#if SM_HEAP_CHECK
123sm_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 */
130sm_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
217void *
218#if SM_HEAP_CHECK
219sm_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 */
226sm_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
302SM_RPOOL_T *
303sm_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
353void
354sm_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
378void
379sm_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
467SM_RPOOL_ATTACH_T
468sm_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}
Note: See TracBrowser for help on using the repository browser.