rpool.c revision 141859
1156952Sume/*
2156952Sume * Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers.
3156952Sume *	All rights reserved.
4156952Sume *
5156952Sume * By using this file, you agree to the terms and conditions set
6156952Sume * forth in the LICENSE file which can be found at the top level of
7156952Sume * the sendmail distribution.
8156952Sume */
9156952Sume
10156952Sume#include <sm/gen.h>
11156952SumeSM_RCSID("@(#)$Id: rpool.c,v 1.28 2004/08/03 20:44:04 ca Exp $")
12156952Sume
13156952Sume/*
14156952Sume**  resource pools
15156952Sume**  For documentation, see rpool.html
16156952Sume*/
17156952Sume
18156952Sume#include <sm/exc.h>
19270838Sume#include <sm/heap.h>
20156952Sume#include <sm/rpool.h>
21156956Sume#include <sm/varargs.h>
22156956Sume#include <sm/conf.h>
23156952Sume#if _FFR_PERF_RPOOL
24156952Sume# include <syslog.h>
25156952Sume#endif /* _FFR_PERF_RPOOL */
26156952Sume
27156952Sumeconst char SmRpoolMagic[] = "sm_rpool";
28156952Sume
29156952Sumetypedef union
30156952Sume{
31156952Sume	SM_POOLLINK_T	link;
32156956Sume	char		align[SM_ALIGN_SIZE];
33156952Sume} SM_POOLHDR_T;
34156952Sume
35156952Sumestatic char	*sm_rpool_allocblock_x __P((SM_RPOOL_T *, size_t));
36156952Sumestatic char	*sm_rpool_allocblock __P((SM_RPOOL_T *, size_t));
37156952Sume
38156952Sume/*
39156952Sume**  Tune this later
40156952Sume*/
41156952Sume
42156952Sume#define POOLSIZE		4096
43156952Sume#define BIG_OBJECT_RATIO	10
44156952Sume
45156952Sume/*
46156952Sume**  SM_RPOOL_ALLOCBLOCK_X -- allocate a new block for an rpool.
47156952Sume**
48156952Sume**	Parameters:
49156952Sume**		rpool -- rpool to which the block should be added.
50156952Sume**		size -- size of block.
51156952Sume**
52156952Sume**	Returns:
53156952Sume**		Pointer to block.
54170244Sume**
55156952Sume**	Exceptions:
56156952Sume**		F:sm_heap -- out of memory
57156952Sume*/
58156952Sume
59156952Sumestatic char *
60156952Sumesm_rpool_allocblock_x(rpool, size)
61156952Sume	SM_RPOOL_T *rpool;
62156952Sume	size_t size;
63156952Sume{
64156952Sume	SM_POOLLINK_T *p;
65156952Sume
66156952Sume	p = sm_malloc_x(sizeof(SM_POOLHDR_T) + size);
67156952Sume	p->sm_pnext = rpool->sm_pools;
68156952Sume	rpool->sm_pools = p;
69156952Sume	return (char*) p + sizeof(SM_POOLHDR_T);
70156952Sume}
71156952Sume
72156952Sume/*
73156952Sume**  SM_RPOOL_ALLOCBLOCK -- allocate a new block for an rpool.
74156952Sume**
75156952Sume**	Parameters:
76156952Sume**		rpool -- rpool to which the block should be added.
77156952Sume**		size -- size of block.
78156952Sume**
79156952Sume**	Returns:
80156952Sume**		Pointer to block, NULL on failure.
81156952Sume*/
82156952Sume
83156952Sumestatic char *
84156952Sumesm_rpool_allocblock(rpool, size)
85156952Sume	SM_RPOOL_T *rpool;
86156952Sume	size_t size;
87156952Sume{
88156952Sume	SM_POOLLINK_T *p;
89156952Sume
90156952Sume	p = sm_malloc(sizeof(SM_POOLHDR_T) + size);
91156952Sume	if (p == NULL)
92156952Sume		return NULL;
93156952Sume	p->sm_pnext = rpool->sm_pools;
94156952Sume	rpool->sm_pools = p;
95156952Sume	return (char*) p + sizeof(SM_POOLHDR_T);
96156952Sume}
97156956Sume
98156952Sume/*
99156952Sume**  SM_RPOOL_MALLOC_TAGGED_X -- allocate memory from rpool
100156952Sume**
101156952Sume**	Parameters:
102156952Sume**		rpool -- rpool from which memory should be allocated;
103156952Sume**			can be NULL, use sm_malloc() then.
104156952Sume**		size -- size of block.
105156952Sume**		file -- filename.
106156952Sume**		line -- line number in file.
107156952Sume**		group -- heap group for debugging.
108156952Sume**
109156952Sume**	Returns:
110156952Sume**		Pointer to block.
111156952Sume**
112156952Sume**	Exceptions:
113156952Sume**		F:sm_heap -- out of memory
114156952Sume**
115156952Sume**	Notice: XXX
116156952Sume**		if size == 0 and the rpool is new (no memory
117156952Sume**		allocated yet) NULL is returned!
118156952Sume**		We could solve this by
119156952Sume**		- wasting 1 byte (size < avail)
120156952Sume**		- checking for rpool->sm_poolptr != NULL
121156952Sume**		- not asking for 0 sized buffer
122156952Sume*/
123156952Sume
124156952Sumevoid *
125156952Sume#if SM_HEAP_CHECK
126156952Sumesm_rpool_malloc_tagged_x(rpool, size, file, line, group)
127156952Sume	SM_RPOOL_T *rpool;
128156952Sume	size_t size;
129156952Sume	char *file;
130156952Sume	int line;
131156952Sume	int group;
132156952Sume#else /* SM_HEAP_CHECK */
133156952Sumesm_rpool_malloc_x(rpool, size)
134156952Sume	SM_RPOOL_T *rpool;
135156952Sume	size_t size;
136156952Sume#endif /* SM_HEAP_CHECK */
137156952Sume{
138156952Sume	char *ptr;
139156952Sume
140156952Sume	if (rpool == NULL)
141156952Sume		return sm_malloc_tagged_x(size, file, line, group);
142156952Sume
143156952Sume	/* Ensure that size is properly aligned. */
144156952Sume	if (size & SM_ALIGN_BITS)
145156952Sume		size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE;
146156952Sume
147156952Sume	/* The common case.  This is optimized for speed. */
148156952Sume	if (size <= rpool->sm_poolavail)
149156952Sume	{
150156952Sume		ptr = rpool->sm_poolptr;
151156952Sume		rpool->sm_poolptr += size;
152156952Sume		rpool->sm_poolavail -= size;
153156952Sume		return ptr;
154156952Sume	}
155156952Sume
156156952Sume	/*
157156952Sume	**  The slow case: we need to call malloc.
158156952Sume	**  The SM_REQUIRE assertion is deferred until now, for speed.
159156952Sume	**  That's okay: we set rpool->sm_poolavail to 0 when we free an rpool,
160156952Sume	**  so the common case code won't be triggered on a dangling pointer.
161156952Sume	*/
162156952Sume
163156952Sume	SM_REQUIRE(rpool->sm_magic == SmRpoolMagic);
164156952Sume
165156952Sume	/*
166156952Sume	**  If size > sm_poolsize, then malloc a new block especially for
167156952Sume	**  this request.  Future requests will be allocated from the
168156952Sume	**  current pool.
169156952Sume	**
170156952Sume	**  What if the current pool is mostly unallocated, and the current
171156952Sume	**  request is larger than the available space, but < sm_poolsize?
172156952Sume	**  If we discard the current pool, and start allocating from a new
173156952Sume	**  pool, then we will be wasting a lot of space.  For this reason,
174156952Sume	**  we malloc a block just for the current request if size >
175156952Sume	**  sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize.
176156952Sume	**  Thus, the most space that we will waste at the end of a pool
177156952Sume	**  is sm_bigobjectsize - 1.
178156952Sume	*/
179156952Sume
180156952Sume	if (size > rpool->sm_bigobjectsize)
181156952Sume	{
182156952Sume#if _FFR_PERF_RPOOL
183156952Sume		++rpool->sm_nbigblocks;
184156952Sume#endif /* _FFR_PERF_RPOOL */
185156952Sume		return sm_rpool_allocblock_x(rpool, size);
186156952Sume	}
187156952Sume	SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize);
188156952Sume	ptr = sm_rpool_allocblock_x(rpool, rpool->sm_poolsize);
189156952Sume	rpool->sm_poolptr = ptr + size;
190156952Sume	rpool->sm_poolavail = rpool->sm_poolsize - size;
191156952Sume#if _FFR_PERF_RPOOL
192156952Sume	++rpool->sm_npools;
193156952Sume#endif /* _FFR_PERF_RPOOL */
194156952Sume	return ptr;
195156952Sume}
196156952Sume
197156952Sume/*
198156952Sume**  SM_RPOOL_MALLOC_TAGGED -- allocate memory from rpool
199156952Sume**
200156952Sume**	Parameters:
201156952Sume**		rpool -- rpool from which memory should be allocated;
202156952Sume**			can be NULL, use sm_malloc() then.
203156952Sume**		size -- size of block.
204156952Sume**		file -- filename.
205156952Sume**		line -- line number in file.
206156952Sume**		group -- heap group for debugging.
207156952Sume**
208156952Sume**	Returns:
209170244Sume**		Pointer to block, NULL on failure.
210156952Sume**
211156952Sume**	Notice: XXX
212156952Sume**		if size == 0 and the rpool is new (no memory
213156952Sume**		allocated yet) NULL is returned!
214156952Sume**		We could solve this by
215156952Sume**		- wasting 1 byte (size < avail)
216156952Sume**		- checking for rpool->sm_poolptr != NULL
217156952Sume**		- not asking for 0 sized buffer
218156952Sume*/
219156952Sume
220156952Sumevoid *
221156952Sume#if SM_HEAP_CHECK
222156952Sumesm_rpool_malloc_tagged(rpool, size, file, line, group)
223156952Sume	SM_RPOOL_T *rpool;
224156952Sume	size_t size;
225156952Sume	char *file;
226156952Sume	int line;
227156952Sume	int group;
228156952Sume#else /* SM_HEAP_CHECK */
229156952Sumesm_rpool_malloc(rpool, size)
230156952Sume	SM_RPOOL_T *rpool;
231156952Sume	size_t size;
232156952Sume#endif /* SM_HEAP_CHECK */
233156952Sume{
234156952Sume	char *ptr;
235156952Sume
236156952Sume	if (rpool == NULL)
237156952Sume		return sm_malloc_tagged(size, file, line, group);
238156952Sume
239156952Sume	/* Ensure that size is properly aligned. */
240156952Sume	if (size & SM_ALIGN_BITS)
241156952Sume		size = (size & ~SM_ALIGN_BITS) + SM_ALIGN_SIZE;
242156952Sume
243156952Sume	/* The common case.  This is optimized for speed. */
244156952Sume	if (size <= rpool->sm_poolavail)
245156952Sume	{
246156952Sume		ptr = rpool->sm_poolptr;
247156952Sume		rpool->sm_poolptr += size;
248156952Sume		rpool->sm_poolavail -= size;
249156952Sume		return ptr;
250156952Sume	}
251156952Sume
252156952Sume	/*
253156952Sume	**  The slow case: we need to call malloc.
254156952Sume	**  The SM_REQUIRE assertion is deferred until now, for speed.
255156952Sume	**  That's okay: we set rpool->sm_poolavail to 0 when we free an rpool,
256156952Sume	**  so the common case code won't be triggered on a dangling pointer.
257156952Sume	*/
258156952Sume
259156952Sume	SM_REQUIRE(rpool->sm_magic == SmRpoolMagic);
260156952Sume
261170244Sume	/*
262156952Sume	**  If size > sm_poolsize, then malloc a new block especially for
263156952Sume	**  this request.  Future requests will be allocated from the
264156952Sume	**  current pool.
265156952Sume	**
266170244Sume	**  What if the current pool is mostly unallocated, and the current
267156952Sume	**  request is larger than the available space, but < sm_poolsize?
268156952Sume	**  If we discard the current pool, and start allocating from a new
269156952Sume	**  pool, then we will be wasting a lot of space.  For this reason,
270170244Sume	**  we malloc a block just for the current request if size >
271156952Sume	**  sm_bigobjectsize, where sm_bigobjectsize <= sm_poolsize.
272170244Sume	**  Thus, the most space that we will waste at the end of a pool
273156952Sume	**  is sm_bigobjectsize - 1.
274156952Sume	*/
275156952Sume
276156952Sume	if (size > rpool->sm_bigobjectsize)
277156952Sume	{
278170244Sume#if _FFR_PERF_RPOOL
279170244Sume		++rpool->sm_nbigblocks;
280#endif /* _FFR_PERF_RPOOL */
281		return sm_rpool_allocblock(rpool, size);
282	}
283	SM_ASSERT(rpool->sm_bigobjectsize <= rpool->sm_poolsize);
284	ptr = sm_rpool_allocblock(rpool, rpool->sm_poolsize);
285	if (ptr == NULL)
286		return NULL;
287	rpool->sm_poolptr = ptr + size;
288	rpool->sm_poolavail = rpool->sm_poolsize - size;
289#if _FFR_PERF_RPOOL
290	++rpool->sm_npools;
291#endif /* _FFR_PERF_RPOOL */
292	return ptr;
293}
294
295/*
296**  SM_RPOOL_NEW_X -- create a new rpool.
297**
298**	Parameters:
299**		parent -- pointer to parent rpool, can be NULL.
300**
301**	Returns:
302**		Pointer to new rpool.
303*/
304
305SM_RPOOL_T *
306sm_rpool_new_x(parent)
307	SM_RPOOL_T *parent;
308{
309	SM_RPOOL_T *rpool;
310
311	rpool = sm_malloc_x(sizeof(SM_RPOOL_T));
312	if (parent == NULL)
313		rpool->sm_parentlink = NULL;
314	else
315	{
316		SM_TRY
317			rpool->sm_parentlink = sm_rpool_attach_x(parent,
318					(SM_RPOOL_RFREE_T) sm_rpool_free,
319					(void *) rpool);
320		SM_EXCEPT(exc, "*")
321			sm_free(rpool);
322			sm_exc_raise_x(exc);
323		SM_END_TRY
324	}
325	rpool->sm_magic = SmRpoolMagic;
326
327	rpool->sm_poolsize = POOLSIZE - sizeof(SM_POOLHDR_T);
328	rpool->sm_bigobjectsize = rpool->sm_poolsize / BIG_OBJECT_RATIO;
329	rpool->sm_poolptr = NULL;
330	rpool->sm_poolavail = 0;
331	rpool->sm_pools = NULL;
332
333	rpool->sm_rptr = NULL;
334	rpool->sm_ravail = 0;
335	rpool->sm_rlists = NULL;
336#if _FFR_PERF_RPOOL
337	rpool->sm_nbigblocks = 0;
338	rpool->sm_npools = 0;
339#endif /* _FFR_PERF_RPOOL */
340
341	return rpool;
342}
343
344/*
345**  SM_RPOOL_SETSIZES -- set sizes for rpool.
346**
347**	Parameters:
348**		poolsize -- size of a single rpool block.
349**		bigobjectsize -- if this size is exceeded, an individual
350**			block is allocated (must be less or equal poolsize).
351**
352**	Returns:
353**		none.
354*/
355
356void
357sm_rpool_setsizes(rpool, poolsize, bigobjectsize)
358	SM_RPOOL_T *rpool;
359	size_t poolsize;
360	size_t bigobjectsize;
361{
362	SM_REQUIRE(poolsize >= bigobjectsize);
363	if (poolsize == 0)
364		poolsize = POOLSIZE - sizeof(SM_POOLHDR_T);
365	if (bigobjectsize == 0)
366		bigobjectsize = poolsize / BIG_OBJECT_RATIO;
367	rpool->sm_poolsize = poolsize;
368	rpool->sm_bigobjectsize = bigobjectsize;
369}
370
371/*
372**  SM_RPOOL_FREE -- free an rpool and release all of its resources.
373**
374**	Parameters:
375**		rpool -- rpool to free.
376**
377**	Returns:
378**		none.
379*/
380
381void
382sm_rpool_free(rpool)
383	SM_RPOOL_T *rpool;
384{
385	SM_RLIST_T *rl, *rnext;
386	SM_RESOURCE_T *r, *rmax;
387	SM_POOLLINK_T *pp, *pnext;
388
389	if (rpool == NULL)
390		return;
391
392	/*
393	**  It's important to free the resources before the memory pools,
394	**  because the resource free functions might modify the contents
395	**  of the memory pools.
396	*/
397
398	rl = rpool->sm_rlists;
399	if (rl != NULL)
400	{
401		rmax = rpool->sm_rptr;
402		for (;;)
403		{
404			for (r = rl->sm_rvec; r < rmax; ++r)
405			{
406				if (r->sm_rfree != NULL)
407					r->sm_rfree(r->sm_rcontext);
408			}
409			rnext = rl->sm_rnext;
410			sm_free(rl);
411			if (rnext == NULL)
412				break;
413			rl = rnext;
414			rmax = &rl->sm_rvec[SM_RLIST_MAX];
415		}
416	}
417
418	/*
419	**  Now free the memory pools.
420	*/
421
422	for (pp = rpool->sm_pools; pp != NULL; pp = pnext)
423	{
424		pnext = pp->sm_pnext;
425		sm_free(pp);
426	}
427
428	/*
429	**  Disconnect rpool from its parent.
430	*/
431
432	if (rpool->sm_parentlink != NULL)
433		*rpool->sm_parentlink = NULL;
434
435	/*
436	**  Setting these fields to zero means that any future to attempt
437	**  to use the rpool after it is freed will cause an assertion failure.
438	*/
439
440	rpool->sm_magic = NULL;
441	rpool->sm_poolavail = 0;
442	rpool->sm_ravail = 0;
443
444#if _FFR_PERF_RPOOL
445	if (rpool->sm_nbigblocks > 0 || rpool->sm_npools > 1)
446		syslog(LOG_NOTICE,
447			"perf: rpool=%lx, sm_nbigblocks=%d, sm_npools=%d",
448			(long) rpool, rpool->sm_nbigblocks, rpool->sm_npools);
449	rpool->sm_nbigblocks = 0;
450	rpool->sm_npools = 0;
451#endif /* _FFR_PERF_RPOOL */
452	sm_free(rpool);
453}
454
455/*
456**  SM_RPOOL_ATTACH_X -- attach a resource to an rpool.
457**
458**	Parameters:
459**		rpool -- rpool to which resource should be attached.
460**		rfree -- function to call when rpool is freed.
461**		rcontext -- argument for function to call when rpool is freed.
462**
463**	Returns:
464**		Pointer to allocated function.
465**
466**	Exceptions:
467**		F:sm_heap -- out of memory
468*/
469
470SM_RPOOL_ATTACH_T
471sm_rpool_attach_x(rpool, rfree, rcontext)
472	SM_RPOOL_T *rpool;
473	SM_RPOOL_RFREE_T rfree;
474	void *rcontext;
475{
476	SM_RLIST_T *rl;
477	SM_RPOOL_ATTACH_T a;
478
479	SM_REQUIRE_ISA(rpool, SmRpoolMagic);
480
481	if (rpool->sm_ravail == 0)
482	{
483		rl = sm_malloc_x(sizeof(SM_RLIST_T));
484		rl->sm_rnext = rpool->sm_rlists;
485		rpool->sm_rlists = rl;
486		rpool->sm_rptr = rl->sm_rvec;
487		rpool->sm_ravail = SM_RLIST_MAX;
488	}
489
490	a = &rpool->sm_rptr->sm_rfree;
491	rpool->sm_rptr->sm_rfree = rfree;
492	rpool->sm_rptr->sm_rcontext = rcontext;
493	++rpool->sm_rptr;
494	--rpool->sm_ravail;
495	return a;
496}
497
498#if DO_NOT_USE_STRCPY
499/*
500**  SM_RPOOL_STRDUP_X -- Create a copy of a C string
501**
502**	Parameters:
503**		rpool -- rpool to use.
504**		s -- the string to copy.
505**
506**	Returns:
507**		pointer to newly allocated string.
508*/
509
510char *
511sm_rpool_strdup_x(rpool, s)
512	SM_RPOOL_T *rpool;
513	const char *s;
514{
515	size_t l;
516	char *n;
517
518	l = strlen(s);
519	SM_ASSERT(l + 1 > l);
520	n = sm_rpool_malloc_x(rpool, l + 1);
521	sm_strlcpy(n, s, l + 1);
522	return n;
523}
524#endif /* DO_NOT_USE_STRCPY */
525