pool.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. *
  6. * License: GPL (version 3 or any later version).
  7. * See LICENSE for details.
  8. * END COPYRIGHT BLOCK **/
  9. #ifdef HAVE_CONFIG_H
  10. # include <config.h>
  11. #endif
  12. /*
  13. * Generic pool handling routines.
  14. *
  15. * Hopefully these reduce the number of malloc/free calls.
  16. *
  17. *
  18. * Thread warning:
  19. * This implementation is thread safe. However, simultaneous
  20. * mallocs/frees to the same "pool" are not safe. If you wish to
  21. * use this module across multiple threads, you should define
  22. * POOL_LOCKING which will make the malloc pools safe.
  23. *
  24. * Mike Belshe
  25. * 11-20-95
  26. *
  27. */
  28. #include "netsite.h"
  29. #include "base/systems.h"
  30. #include "base/systhr.h"
  31. #include <plhash.h>
  32. #ifdef MALLOC_POOLS
  33. #include "base/pool.h"
  34. #include "base/ereport.h"
  35. #include "base/util.h"
  36. #include "base/crit.h"
  37. #include "base/dbtbase.h"
  38. #ifdef DEBUG
  39. #define POOL_ZERO_DEBUG
  40. #endif
  41. #undef POOL_LOCKING
  42. #define BLOCK_SIZE (32 * 1024)
  43. #define MAX_FREELIST_SIZE (BLOCK_SIZE * 32)
  44. /* WORD SIZE 8 sets us up for 8 byte alignment. */
  45. #define WORD_SIZE 8
  46. #undef ALIGN
  47. #define ALIGN(x) ( (x + WORD_SIZE-1) & (~(WORD_SIZE-1)) )
  48. /* block_t
  49. * When the user allocates space, a BLOCK_SIZE (or larger) block is created in
  50. * the pool. This block is used until all the space is eaten within it.
  51. * When all the space is gone, a new block is created.
  52. *
  53. */
  54. typedef struct block_t {
  55. char *data; /* the real alloc'd space */
  56. char *start; /* first free byte in block */
  57. char *end; /* ptr to end of block */
  58. struct block_t *next; /* ptr to next block */
  59. } block_t;
  60. /* pool_t
  61. * A pool is a collection of blocks. The blocks consist of multiple
  62. * allocations of memory, but a single allocation cannot be freed by
  63. * itself. Once the memory is allocated it is allocated until the
  64. * entire pool is freed.
  65. */
  66. typedef struct pool_t {
  67. #ifdef DEBUG_CACHES
  68. time_t time_created;
  69. #endif
  70. #ifdef POOL_LOCKING
  71. CRITICAL lock; /* lock for modifying the pool */
  72. #endif
  73. block_t *curr_block; /* current block being used */
  74. block_t *used_blocks; /* blocks that are all used up */
  75. long size; /* size of memory in pool */
  76. struct pool_t *next; /* known_pools list */
  77. } pool_t;
  78. /* known_pools
  79. * Primarily for debugging, keep a list of all active malloc pools.
  80. */
  81. static pool_t *known_pools = NULL;
  82. static CRITICAL known_pools_lock = NULL;
  83. static unsigned long pool_blocks_created = 0;
  84. static unsigned long pool_blocks_freed = 0;
  85. /* freelist
  86. * Internally we maintain a list of free blocks which we try to pull from
  87. * whenever possible. This list will never have more than MAX_FREELIST_SIZE
  88. * bytes within it.
  89. */
  90. static CRITICAL freelist_lock = NULL;
  91. static block_t *freelist = NULL;
  92. static unsigned long freelist_size = 0;
  93. //static unsigned long freelist_max = MAX_FREELIST_SIZE;
  94. static int pool_disable = 0;
  95. int
  96. pool_internal_init()
  97. {
  98. if (pool_disable == 0) {
  99. if (known_pools_lock == NULL) {
  100. known_pools_lock = crit_init();
  101. freelist_lock = crit_init();
  102. }
  103. }
  104. return 0;
  105. }
  106. static block_t *
  107. _create_block(int size)
  108. {
  109. block_t *newblock = NULL;
  110. long bytes = ALIGN(size);
  111. block_t *free_ptr,
  112. *last_free_ptr = NULL;
  113. /* check freelist for large enough block first */
  114. crit_enter(freelist_lock);
  115. free_ptr = freelist;
  116. while(free_ptr && ((free_ptr->end - free_ptr->data) < bytes)) {
  117. last_free_ptr = free_ptr;
  118. free_ptr = free_ptr->next;
  119. }
  120. if (free_ptr) {
  121. newblock = free_ptr;
  122. if (last_free_ptr)
  123. last_free_ptr->next = free_ptr->next;
  124. else
  125. freelist = free_ptr->next;
  126. freelist_size -= (newblock->end - newblock->data);
  127. crit_exit(freelist_lock);
  128. bytes = free_ptr->end - free_ptr->data;
  129. }
  130. else {
  131. pool_blocks_created++;
  132. crit_exit(freelist_lock);
  133. if (((newblock = (block_t *)PERM_MALLOC(sizeof(block_t))) == NULL) ||
  134. ((newblock->data = (char *)PERM_MALLOC(bytes)) == NULL)) {
  135. ereport(LOG_CATASTROPHE, const_cast<char *>("%s"), XP_GetAdminStr(DBT_poolCreateBlockOutOfMemory_));
  136. if (newblock)
  137. PERM_FREE(newblock);
  138. return NULL;
  139. }
  140. }
  141. newblock->start = newblock->data;
  142. newblock->end = newblock->data + bytes;
  143. newblock->next = NULL;
  144. return newblock;
  145. }
  146. /* Caller must hold lock for the pool */
  147. static void
  148. _free_block(block_t *block)
  149. {
  150. #ifdef POOL_ZERO_DEBUG
  151. memset(block->data, 0xa, block->end-block->data);
  152. #endif /* POOL_ZERO_DEBUG */
  153. /* Just have to delete the whole block! */
  154. crit_enter(freelist_lock);
  155. pool_blocks_freed++;
  156. crit_exit(freelist_lock);
  157. PERM_FREE(block->data);
  158. #ifdef POOL_ZERO_DEBUG
  159. memset(block, 0xa, sizeof(block_t));
  160. #endif /* POOL_ZERO_DEBUG */
  161. PERM_FREE(block);
  162. return;
  163. }
  164. /* ptr_in_pool()
  165. * Checks to see if the given pointer is in the given pool.
  166. * If true, returns a ptr to the block_t containing the ptr;
  167. * otherwise returns NULL
  168. */
  169. block_t *
  170. _ptr_in_pool(pool_t *pool, void *ptr)
  171. {
  172. block_t *block_ptr = NULL;
  173. /* try to find a block which contains this ptr */
  174. if ( ((char *)ptr < (char *)pool->curr_block->end) &&
  175. ((char *)ptr >= (char *)pool->curr_block->data) )
  176. block_ptr = pool->curr_block;
  177. else
  178. for( block_ptr = pool->used_blocks;
  179. block_ptr &&
  180. (((char *)ptr >= (char *)block_ptr->end) &&
  181. ((char *)ptr < (char *)block_ptr->data));
  182. block_ptr = block_ptr->next);
  183. return block_ptr;
  184. }
  185. NSAPI_PUBLIC pool_handle_t *
  186. pool_create()
  187. {
  188. pool_t *newpool;
  189. if (pool_disable)
  190. return NULL;
  191. newpool = (pool_t *)PERM_MALLOC(sizeof(pool_t));
  192. if (newpool) {
  193. /* Have to initialize now, as pools get created sometimes
  194. * before pool_init can be called...
  195. */
  196. if (known_pools_lock == NULL) {
  197. known_pools_lock = crit_init();
  198. freelist_lock = crit_init();
  199. }
  200. if ( (newpool->curr_block =_create_block(BLOCK_SIZE)) == NULL) {
  201. ereport(LOG_CATASTROPHE, const_cast<char *>("%s"), XP_GetAdminStr(DBT_poolCreateOutOfMemory_));
  202. PERM_FREE(newpool);
  203. return NULL;
  204. }
  205. newpool->used_blocks = NULL;
  206. newpool->size = 0;
  207. newpool->next = NULL;
  208. #ifdef POOL_LOCKING
  209. newpool->lock = crit_init();
  210. #endif
  211. #ifdef DEBUG_CACHES
  212. newpool->time_created = time(NULL);
  213. #endif
  214. /* Add to known pools list */
  215. crit_enter(known_pools_lock);
  216. newpool->next = known_pools;
  217. known_pools = newpool;
  218. crit_exit(known_pools_lock);
  219. }
  220. else
  221. ereport(LOG_CATASTROPHE, const_cast<char *>("%s"), XP_GetAdminStr(DBT_poolCreateOutOfMemory_1));
  222. return (pool_handle_t *)newpool;
  223. }
  224. NSAPI_PUBLIC void
  225. pool_destroy(pool_handle_t *pool_handle)
  226. {
  227. pool_t *pool = (pool_t *)pool_handle;
  228. block_t *tmp_blk;
  229. pool_t *last, *search;
  230. if (pool_disable)
  231. return;
  232. crit_enter(known_pools_lock);
  233. #ifdef POOL_LOCKING
  234. crit_enter(pool->lock);
  235. #endif
  236. if (pool->curr_block){
  237. _free_block(pool->curr_block);
  238. }
  239. while(pool->used_blocks) {
  240. tmp_blk = pool->used_blocks;
  241. pool->used_blocks = pool->used_blocks->next;
  242. _free_block(tmp_blk);
  243. }
  244. /* Remove from the known pools list */
  245. for (last = NULL, search = known_pools; search;
  246. last = search, search = search->next)
  247. if (search == pool)
  248. break;
  249. if (search) {
  250. if(last)
  251. last->next = search->next;
  252. else
  253. known_pools = search->next;
  254. }
  255. #ifdef POOL_LOCKING
  256. crit_exit(pool->lock);
  257. crit_terminate(pool->lock);
  258. #endif
  259. crit_exit(known_pools_lock);
  260. #ifdef POOL_ZERO_DEBUG
  261. memset(pool, 0xa, sizeof(pool_t));
  262. #endif /* POOL_ZERO_DEBUG */
  263. PERM_FREE(pool);
  264. return;
  265. }
  266. NSAPI_PUBLIC void
  267. pool_terminate(void)
  268. {
  269. crit_terminate(known_pools_lock);
  270. crit_terminate(freelist_lock);
  271. known_pools_lock = NULL;
  272. freelist_lock = NULL;
  273. }
  274. NSAPI_PUBLIC void *
  275. pool_malloc(pool_handle_t *pool_handle, size_t size)
  276. {
  277. pool_t *pool = (pool_t *)pool_handle;
  278. long reqsize, blocksize;
  279. char *ptr;
  280. if (pool == NULL || pool_disable) {
  281. return PERM_MALLOC(size);
  282. }
  283. #ifdef DEBUG
  284. if (size == 0)
  285. return NULL;
  286. #endif
  287. #ifdef POOL_LOCKING
  288. crit_enter(pool->lock);
  289. #endif
  290. reqsize = ALIGN(size);
  291. ptr = pool->curr_block->start;
  292. pool->curr_block->start += reqsize;
  293. /* does this fit into the last allocated block? */
  294. if (pool->curr_block->start > pool->curr_block->end) {
  295. /* Did not fit; time to allocate a new block */
  296. pool->curr_block->start -= reqsize; /* keep structs in tact */
  297. pool->curr_block->next = pool->used_blocks;
  298. pool->used_blocks = pool->curr_block;
  299. /* Allocate a chunk of memory which is a multiple of BLOCK_SIZE
  300. * bytes
  301. */
  302. blocksize = ( (size + BLOCK_SIZE-1) / BLOCK_SIZE ) * BLOCK_SIZE;
  303. if ( (pool->curr_block = _create_block(blocksize)) == NULL) {
  304. ereport(LOG_CATASTROPHE, const_cast<char *>("%s"), XP_GetAdminStr(DBT_poolMallocOutOfMemory_));
  305. #ifdef POOL_LOCKING
  306. crit_exit(pool->lock);
  307. #endif
  308. return NULL;
  309. }
  310. ptr = pool->curr_block->start;
  311. reqsize = ALIGN(size);
  312. pool->curr_block->start += reqsize;
  313. }
  314. pool->size += reqsize;
  315. #ifdef POOL_LOCKING
  316. crit_exit(pool->lock);
  317. #endif
  318. return ptr;
  319. }
  320. void _pool_free_error()
  321. {
  322. ereport(LOG_WARN, const_cast<char *>("%s"), XP_GetAdminStr(DBT_freeUsedWherePermFreeShouldHaveB_));
  323. return;
  324. }
  325. NSAPI_PUBLIC void
  326. pool_free(pool_handle_t *pool_handle, void *ptr)
  327. {
  328. if (pool_handle == NULL || pool_disable) {
  329. PERM_FREE(ptr);
  330. return;
  331. }
  332. #ifdef DEBUG
  333. /* Just to be nice, check to see if the ptr was allocated in a pool.
  334. * If not, issue a warning and do a REAL free just to make sure that
  335. * we don't leak memory.
  336. */
  337. if ( !_ptr_in_pool((pool_t *)pool_handle, ptr) ) {
  338. _pool_free_error();
  339. PERM_FREE(ptr);
  340. }
  341. #endif
  342. return;
  343. }
  344. NSAPI_PUBLIC void *
  345. pool_calloc(pool_handle_t *pool_handle, size_t nelem, size_t elsize)
  346. {
  347. void *ptr;
  348. if (pool_handle == NULL || pool_disable)
  349. return PERM_CALLOC(elsize * nelem);
  350. ptr = pool_malloc(pool_handle, elsize * nelem);
  351. if (ptr)
  352. memset(ptr, 0, elsize * nelem);
  353. return ptr;
  354. }
  355. NSAPI_PUBLIC void *
  356. pool_realloc(pool_handle_t *pool_handle, void *ptr, size_t size)
  357. {
  358. pool_t *pool = (pool_t *)pool_handle;
  359. void *newptr;
  360. block_t *block_ptr;
  361. size_t oldsize;
  362. if (pool_handle == NULL || pool_disable)
  363. return PERM_REALLOC(ptr, size);
  364. if ( (newptr = pool_malloc(pool_handle, size)) == NULL)
  365. return NULL;
  366. /* With our structure we don't know exactly where the end
  367. * of the original block is. But we do know an upper bound
  368. * which is a valid ptr. Search the outstanding blocks
  369. * for the block which contains this ptr, and copy...
  370. */
  371. #ifdef POOL_LOCKING
  372. crit_enter(pool->lock);
  373. #endif
  374. if ( !(block_ptr = _ptr_in_pool(pool, ptr)) ) {
  375. /* User is trying to realloc nonmalloc'd space! */
  376. return newptr;
  377. }
  378. oldsize = block_ptr->end - (char *)ptr ;
  379. if (oldsize > size)
  380. oldsize = size;
  381. memmove((char *)newptr, (char *)ptr, oldsize);
  382. #ifdef POOL_LOCKING
  383. crit_exit(pool->lock);
  384. #endif
  385. return newptr;
  386. }
  387. NSAPI_PUBLIC char *
  388. pool_strdup(pool_handle_t *pool_handle, const char *orig_str)
  389. {
  390. char *new_str;
  391. int len = strlen(orig_str);
  392. if (pool_handle == NULL || pool_disable)
  393. return PERM_STRDUP(orig_str);
  394. new_str = (char *)pool_malloc(pool_handle, len+1);
  395. if (new_str)
  396. memcpy(new_str, orig_str, len+1);
  397. return new_str;
  398. }
  399. NSAPI_PUBLIC long
  400. pool_space(pool_handle_t *pool_handle)
  401. {
  402. pool_t *pool = (pool_t *)pool_handle;
  403. return pool->size;
  404. }
  405. NSAPI_PUBLIC int pool_enabled()
  406. {
  407. #ifndef THREAD_ANY
  408. /* we don't have USE_NSPR defined so systhread_getdata is undef'ed */
  409. return 0;
  410. #else
  411. if (pool_disable || (getThreadMallocKey() == -1) )
  412. return 0;
  413. if (!systhread_getdata(getThreadMallocKey()))
  414. return 0;
  415. return 1;
  416. #endif
  417. }
  418. /* pool_service_debug()
  419. * NSAPI service routine to print state information about the existing
  420. * pools. Hopefully useful in debugging.
  421. *
  422. */
  423. #define MAX_DEBUG_LINE 1024
  424. #ifdef DEBUG_CACHES /* XXXrobm causes entanglement in install and admserv cgis */
  425. NSAPI_PUBLIC int
  426. pool_service_debug(pblock *pb, Session *sn, Request *rq)
  427. {
  428. char tmp_buf[MAX_DEBUG_LINE];
  429. char cbuf[DEF_CTIMEBUF];
  430. int len;
  431. pool_t *pool_ptr;
  432. block_t *block_ptr;
  433. int pool_cnt, block_cnt;
  434. param_free(pblock_remove("content-type", rq->srvhdrs));
  435. pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
  436. protocol_status(sn, rq, PROTOCOL_OK, NULL);
  437. protocol_start_response(sn, rq);
  438. len = util_sprintf(tmp_buf, "<H2>Memory pool status report</H2>\n");
  439. net_write(sn->csd, tmp_buf, len);
  440. len = util_sprintf(tmp_buf, "Note: The 0 block in each pool is \
  441. the currently used block <P>\n");
  442. net_write(sn->csd, tmp_buf, len);
  443. len = util_sprintf(tmp_buf, "Freelist size: %d/%d<P>", freelist_size,
  444. freelist_max);
  445. net_write(sn->csd, tmp_buf, len);
  446. len = util_sprintf(tmp_buf, "Pool disabled: %d<P>", pool_disable);
  447. net_write(sn->csd, tmp_buf, len);
  448. len = util_sprintf(tmp_buf, "Blocks created: %d<P> Blocks freed: %d",
  449. pool_blocks_created, pool_blocks_freed);
  450. net_write(sn->csd, tmp_buf, len);
  451. /* Create an HTML table */
  452. len = util_sprintf(tmp_buf, "<UL><TABLE BORDER=4>\n");
  453. net_write(sn->csd, tmp_buf, len);
  454. len = util_sprintf(tmp_buf, "<TH>Pool #</TH>\n");
  455. net_write(sn->csd, tmp_buf, len);
  456. len = util_sprintf(tmp_buf, "<TH>Pool size #</TH>\n");
  457. net_write(sn->csd, tmp_buf, len);
  458. #ifdef DEBUG_CACHES
  459. len = util_sprintf(tmp_buf, "<TH>Time Created</TH>\n");
  460. net_write(sn->csd, tmp_buf, len);
  461. #endif
  462. len = util_sprintf(tmp_buf, "<TH>Blocks</TH>\n");
  463. net_write(sn->csd, tmp_buf, len);
  464. crit_enter(known_pools_lock);
  465. for (pool_cnt = 0, pool_ptr = known_pools; pool_ptr;
  466. pool_ptr = pool_ptr->next, pool_cnt++) {
  467. #ifdef POOL_LOCKING
  468. crit_enter(pool_ptr->lock);
  469. #endif
  470. len = util_snprintf(tmp_buf, MAX_DEBUG_LINE,
  471. #ifndef DEBUG_CACHES
  472. "<tr align=right> <td>%d</td> <td>%d</td> <td> <TABLE BORDER=2> <TH>Block #</TH><TH>data</TH><TH>curr size</TH> <TH>max size</TH>\n",
  473. #else
  474. "<tr align=right> <td>%d</td> <td>%d</td> <td>%s</td> <td> <TABLE BORDER=2> <TH>Block #</TH><TH>data</TH><TH>curr size</TH> <TH>max size</TH>\n",
  475. #endif
  476. pool_cnt, pool_space((pool_handle_t *)pool_ptr)
  477. #ifdef DEBUG_CACHES
  478. , util_ctime(&(pool_ptr->time_created), cbuf, DEF_CTIMEBUF));
  479. #else
  480. );
  481. #endif
  482. net_write(sn->csd, tmp_buf, len);
  483. /* Print the first block */
  484. len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, "\
  485. <tr align=right> \
  486. <td>%d</td> \
  487. <td>%d</td> \
  488. <td>%d</td> \
  489. <td>%d</td> \
  490. </tr>\n",
  491. 0, pool_ptr->curr_block->data,
  492. pool_ptr->curr_block->start -pool_ptr->curr_block->data,
  493. pool_ptr->curr_block->end - pool_ptr->curr_block->data);
  494. net_write(sn->csd, tmp_buf, len);
  495. for (block_cnt = 1, block_ptr = pool_ptr->used_blocks; block_ptr;
  496. block_ptr = block_ptr->next, block_cnt++) {
  497. len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, "\
  498. <tr align=right> \
  499. <td>%d</td> \
  500. <td>%d</td> \
  501. <td>%d</td> \
  502. <td>%d</td> \
  503. </tr>\n",
  504. block_cnt, block_ptr->data,
  505. block_ptr->start - block_ptr->data,
  506. block_ptr->end - block_ptr->data);
  507. net_write(sn->csd, tmp_buf, len);
  508. }
  509. #ifdef POOL_LOCKING
  510. crit_exit(pool_ptr->lock);
  511. #endif
  512. len = util_snprintf(tmp_buf, MAX_DEBUG_LINE, "</TABLE></TD></TR>");
  513. net_write(sn->csd, tmp_buf, len);
  514. }
  515. crit_exit(known_pools_lock);
  516. len = util_sprintf(tmp_buf, "</TABLE></UL>\n");
  517. net_write(sn->csd, tmp_buf, len);
  518. return REQ_PROCEED;
  519. }
  520. #endif /* 0 */
  521. #endif /* MALLOC_POOLS */