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