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