mcabber/libjabber/pool.c
changeset 1598 a087125d8fc8
parent 1597 4f59a414217e
child 1599 dcd5d4c75199
equal deleted inserted replaced
1597:4f59a414217e 1598:a087125d8fc8
     1 /*
       
     2  *  pool.c
       
     3  * This code comes from jabberd - Jabber Open Source Server
       
     4  *  Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
       
     5  *                    Ryan Eatmon, Robert Norris
       
     6  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
       
     7  *
       
     8  * This program is free software; you can redistribute it and/or modify
       
     9  * it under the terms of the GNU General Public License as published by
       
    10  * the Free Software Foundation; either version 2 of the License, or
       
    11  * (at your option) any later version.
       
    12  *
       
    13  * This program is distributed in the hope that it will be useful,
       
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
       
    16  * GNU General Public License for more details.
       
    17  *
       
    18  * You should have received a copy of the GNU General Public License
       
    19  * along with this program; if not, write to the Free Software
       
    20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
       
    21  *
       
    22  * Copyrights
       
    23  *
       
    24  * Portions created by or assigned to Jabber.com, Inc. are
       
    25  * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
       
    26  * information for Jabber.com, Inc. is available at http://www.jabber.com/.
       
    27  *
       
    28  * Portions Copyright (c) 1998-1999 Jeremie Miller.
       
    29  *
       
    30  * Acknowledgements
       
    31  *
       
    32  * Special thanks to the Jabber Open Source Contributors for their
       
    33  * suggestions and support of Jabber.
       
    34  *
       
    35  */
       
    36 
       
    37 /**
       
    38  * @file pool.c
       
    39  * @brief Handling of memory pools
       
    40  *
       
    41  * Jabberd handles its memory allocations in pools. You create a pool, can
       
    42  * allocate memory from it and all allocations will be freed if you free
       
    43  * the pool. Therefore you don't have to care that each malloc is freed,
       
    44  * you only have to take care that the pool is freed.
       
    45  *
       
    46  * The normal call-flow for pools is:
       
    47  *
       
    48  * pool p = pool_new();
       
    49  * struct mystruct *allocation1 = pmalloc(sizeof(struct mystruct));
       
    50  * struct myotherstruct *allocation2 = pmalloc(sizeof(struct myotherstruct));
       
    51  * ...
       
    52  * pool_free(p);
       
    53  */
       
    54 
       
    55 #include "libxode.h"
       
    56 
       
    57 #define MAX_MALLOC_TRIES 10 /**< how many seconds we try to allocate memory */
       
    58 
       
    59 #ifdef POOL_DEBUG
       
    60 int pool__total = 0;		/**< how many memory blocks are allocated */
       
    61 int pool__ltotal = 0;
       
    62 xht pool__disturbed = NULL;
       
    63 
       
    64 inline void *_retried__malloc(size_t size);
       
    65 
       
    66 /**
       
    67  * create a new memory allocation and increment the pool__total counter
       
    68  *
       
    69  * only used if POOL_DEBUG is defined, else it is an alias for malloc
       
    70  *
       
    71  * @param size size of the memory to allocate
       
    72  * @return pointer to the allocated memory
       
    73  */
       
    74 void *_pool__malloc(size_t size)
       
    75 {
       
    76     pool__total++;
       
    77     return malloc(size);
       
    78 }
       
    79 
       
    80 /**
       
    81  * free memory and decrement the pool__total counter
       
    82  *
       
    83  * only used if POOL_DEBUG is defined, else it is an alias for free
       
    84  *
       
    85  * @param block pointer to the memory allocation that should be freed
       
    86  */
       
    87 void _pool__free(void *block)
       
    88 {
       
    89     pool__total--;
       
    90     free(block);
       
    91 }
       
    92 #else
       
    93 #define _pool__malloc malloc	/**< _pool__malloc updates pool__total counter if POOL_DEBUG is defined */
       
    94 #define _pool__free free	/**< _pool__free updates pool__total counter if POOL_DEBUG is defined */
       
    95 #endif
       
    96 
       
    97 /**
       
    98  * try to allocate memory
       
    99  *
       
   100  * If allocation fails, it will be retries for MAX_MALLOC_TRIES seconds.
       
   101  * If it still fails, we exit the process
       
   102  *
       
   103  * @param size how many bytes of memory we allocate
       
   104  * @return pointer to the allocated memory
       
   105  */
       
   106 void *_retried__malloc(size_t size) {
       
   107     void *allocated_memory;
       
   108     int malloc_tries = 0;
       
   109 
       
   110     while ((allocated_memory=_pool__malloc(size)) == NULL) {
       
   111 	if (malloc_tries++ > MAX_MALLOC_TRIES) {
       
   112 	    exit(999);
       
   113 	}
       
   114 
       
   115 	sleep(1); //pth_sleep(1);
       
   116     }
       
   117 
       
   118     return allocated_memory;
       
   119 }
       
   120 
       
   121 /**
       
   122  * make an empty pool
       
   123  *
       
   124  * Use the macro pool_new() instead of a direct call to this function. The
       
   125  * macro will create the parameters for you.
       
   126  *
       
   127  * @param zone the file in which the pool_new macro is called
       
   128  * @param line the line in the file in which the pool_new macro is called
       
   129  * @return the new allocated memory pool
       
   130  */
       
   131 pool _pool_new(char *zone, int line)
       
   132 {
       
   133     // int malloc_tries = 0;
       
   134 #ifdef POOL_DEBUG
       
   135     int old__pool__total;
       
   136 #endif
       
   137 
       
   138     pool p = _retried__malloc(sizeof(_pool));
       
   139 
       
   140     p->cleanup = NULL;
       
   141     p->heap = NULL;
       
   142     p->size = 0;
       
   143 
       
   144 #ifdef POOL_DEBUG
       
   145     p->lsize = -1;
       
   146     p->zone[0] = '\0';
       
   147     strcat(p->zone,zone);
       
   148     snprintf(p->zone, sizeof(p->zone), "%s:%i", zone, line);
       
   149     snprintf(p->name, sizeof(p->name), "%X", p);
       
   150 
       
   151     if(pool__disturbed == NULL)
       
   152     {
       
   153         pool__disturbed = (xht)1; /* reentrancy flag! */
       
   154         pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
       
   155     }
       
   156     if(pool__disturbed != (xht)1)
       
   157         ghash_put(pool__disturbed,p->name,p);
       
   158 #endif
       
   159 
       
   160     return p;
       
   161 }
       
   162 
       
   163 /**
       
   164  * free a memory heap (struct pheap)
       
   165  *
       
   166  * @param arg which heep should be freed
       
   167  */
       
   168 void _pool_heap_free(void *arg)
       
   169 {
       
   170     struct pheap *h = (struct pheap *)arg;
       
   171 
       
   172     _pool__free(h->block);
       
   173     _pool__free(h);
       
   174 }
       
   175 
       
   176 /**
       
   177  * append a pool_cleaner function (callback) to a pool
       
   178  *
       
   179  * mem should always be freed last
       
   180  *
       
   181  * All appended pool_cleaner functions will be called if a pool is freed.
       
   182  * This might be used to clean logically subpools.
       
   183  *
       
   184  * @param p to which pool the pool_cleaner should be added
       
   185  * @param pf structure containing the reference to the pool_cleaner and links for the list
       
   186  */
       
   187 void _pool_cleanup_append(pool p, struct pfree *pf)
       
   188 {
       
   189     struct pfree *cur;
       
   190 
       
   191     if(p->cleanup == NULL)
       
   192     {
       
   193         p->cleanup = pf;
       
   194         return;
       
   195     }
       
   196 
       
   197     /* fast forward to end of list */
       
   198     for(cur = p->cleanup; cur->next != NULL; cur = cur->next);
       
   199 
       
   200     cur->next = pf;
       
   201 }
       
   202 
       
   203 /**
       
   204  * create a cleanup tracker
       
   205  *
       
   206  * this function is used to create a pfree structure that can be passed to _pool_cleanup_append()
       
   207  *
       
   208  * @param p the pool to which the pool_cleaner should be added
       
   209  * @param f the function that should be called if the pool is freed
       
   210  * @param arg the parameter that should be passed to the pool_cleaner function
       
   211  * @return pointer to the new pfree structure
       
   212  */
       
   213 struct pfree *_pool_free(pool p, pool_cleaner f, void *arg)
       
   214 {
       
   215     struct pfree *ret;
       
   216 
       
   217     /* make the storage for the tracker */
       
   218     ret = _retried__malloc(sizeof(struct pfree));
       
   219     ret->f = f;
       
   220     ret->arg = arg;
       
   221     ret->next = NULL;
       
   222 
       
   223     return ret;
       
   224 }
       
   225 
       
   226 /**
       
   227  * create a heap and make sure it get's cleaned up
       
   228  *
       
   229  * pheaps are used by memory pools internally to handle the memory allocations
       
   230  *
       
   231  * @note the macro pool_heap calls _pool_new_heap and NOT _pool_heap
       
   232  *
       
   233  * @param p for which pool the heap should be created
       
   234  * @param size how big the pool should be
       
   235  * @return pointer to the new pheap
       
   236  */
       
   237 struct pheap *_pool_heap(pool p, int size)
       
   238 {
       
   239     struct pheap *ret;
       
   240     struct pfree *clean;
       
   241 
       
   242     /* make the return heap */
       
   243     ret = _retried__malloc(sizeof(struct pheap));
       
   244     ret->block = _retried__malloc(size);
       
   245     ret->size = size;
       
   246     p->size += size;
       
   247     ret->used = 0;
       
   248 
       
   249     /* append to the cleanup list */
       
   250     clean = _pool_free(p, _pool_heap_free, (void *)ret);
       
   251     clean->heap = ret; /* for future use in finding used mem for pstrdup */
       
   252     _pool_cleanup_append(p, clean);
       
   253 
       
   254     return ret;
       
   255 }
       
   256 
       
   257 /**
       
   258  * create a new memory pool and set the initial heap size
       
   259  *
       
   260  * @note you should not call this function but use the macro pool_heap instead which fills zone and line automatically
       
   261  *
       
   262  * @param size the initial size of the memory pool
       
   263  * @param zone the file where this function is called (for debugging)
       
   264  * @param line the line in the file where this function is called
       
   265  * @return the new memory pool
       
   266  */
       
   267 pool _pool_new_heap(int size, char *zone, int line)
       
   268 {
       
   269     pool p;
       
   270     p = _pool_new(zone, line);
       
   271     p->heap = _pool_heap(p,size);
       
   272     return p;
       
   273 }
       
   274 
       
   275 /**
       
   276  * allocate memory from a memory pool
       
   277  *
       
   278  * @param p the pool to use
       
   279  * @param size how much memory to allocate
       
   280  * @return pointer to the allocated memory
       
   281  */
       
   282 void *pmalloc(pool p, int size)
       
   283 {
       
   284     void *block;
       
   285 
       
   286     if(p == NULL)
       
   287     {
       
   288         fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n");
       
   289         abort();
       
   290     }
       
   291 
       
   292     /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */
       
   293     if(p->heap == NULL || size > (p->heap->size / 2))
       
   294     {
       
   295 	block = _retried__malloc(size);
       
   296         p->size += size;
       
   297         _pool_cleanup_append(p, _pool_free(p, _pool__free, block));
       
   298         return block;
       
   299     }
       
   300 
       
   301     /* we have to preserve boundaries, long story :) */
       
   302     if(size >= 4)
       
   303         while(p->heap->used&7) p->heap->used++;
       
   304 
       
   305     /* if we don't fit in the old heap, replace it */
       
   306     if(size > (p->heap->size - p->heap->used))
       
   307         p->heap = _pool_heap(p, p->heap->size);
       
   308 
       
   309     /* the current heap has room */
       
   310     block = (char *)p->heap->block + p->heap->used;
       
   311     p->heap->used += size;
       
   312     return block;
       
   313 }
       
   314 
       
   315 /**
       
   316  * allocate memory and initialize the memory with the given char c
       
   317  *
       
   318  * @deprecated jabberd does use pmalloco instead, this function will be removed
       
   319  *
       
   320  * @param p which pool to use
       
   321  * @param size the size of the allocation
       
   322  * @param c the initialization character
       
   323  * @return pointer to the allocated memory
       
   324  */
       
   325 void *pmalloc_x(pool p, int size, char c)
       
   326 {
       
   327    void* result = pmalloc(p, size);
       
   328    if (result != NULL)
       
   329            memset(result, c, size);
       
   330    return result;
       
   331 }
       
   332 
       
   333 /**
       
   334  * allocate memory and initialize the memory with zero bytes
       
   335  *
       
   336  * easy safety utility (for creating blank mem for structs, etc)
       
   337  *
       
   338  * @param p which pool to use
       
   339  * @param size the size of the allocation
       
   340  * @return pointer to the allocated memory
       
   341  */
       
   342 void *pmalloco(pool p, int size)
       
   343 {
       
   344     void *block = pmalloc(p, size);
       
   345     memset(block, 0, size);
       
   346     return block;
       
   347 }
       
   348 
       
   349 /**
       
   350  * duplicate a string and allocate memory for it
       
   351  *
       
   352  * @todo efficient: move this to const char* and then loop through the existing heaps to see if src is within a block in this pool
       
   353  *
       
   354  * @param p the pool to use
       
   355  * @param src the string that should be duplicated
       
   356  * @return the duplicated string
       
   357  */
       
   358 char *pstrdup(pool p, const char *src)
       
   359 {
       
   360     char *ret;
       
   361 
       
   362     if(src == NULL)
       
   363         return NULL;
       
   364 
       
   365     ret = pmalloc(p,strlen(src) + 1);
       
   366     strcpy(ret,src);
       
   367 
       
   368     return ret;
       
   369 }
       
   370 
       
   371 /**
       
   372  * when pstrdup() is moved to "const char*", this one would actually return a new block
       
   373  */
       
   374 char *pstrdupx(pool p, const char *src)
       
   375 {
       
   376     return pstrdup(p, src);
       
   377 }
       
   378 
       
   379 /**
       
   380  * get the size of a memory pool
       
   381  *
       
   382  * @param p the pool
       
   383  * @return the size
       
   384  */
       
   385 int pool_size(pool p)
       
   386 {
       
   387     if(p == NULL) return 0;
       
   388 
       
   389     return p->size;
       
   390 }
       
   391 
       
   392 /**
       
   393  * free a pool (and all memory that is allocated in it)
       
   394  *
       
   395  * @param p which pool to free
       
   396  */
       
   397 void pool_free(pool p)
       
   398 {
       
   399     struct pfree *cur, *stub;
       
   400 
       
   401     if(p == NULL) return;
       
   402 
       
   403     cur = p->cleanup;
       
   404     while(cur != NULL)
       
   405     {
       
   406         (*cur->f)(cur->arg);
       
   407         stub = cur->next;
       
   408         _pool__free(cur);
       
   409         cur = stub;
       
   410     }
       
   411 
       
   412 #ifdef POOL_DEBUG
       
   413     ghash_remove(pool__disturbed,p->name);
       
   414 #endif
       
   415 
       
   416     _pool__free(p);
       
   417 
       
   418 }
       
   419 
       
   420 /**
       
   421  * public cleanup utils, insert in a way that they are run FIFO, before mem frees
       
   422  */
       
   423 void pool_cleanup(pool p, pool_cleaner f, void *arg)
       
   424 {
       
   425     struct pfree *clean;
       
   426 
       
   427     clean = _pool_free(p, f, arg);
       
   428     clean->next = p->cleanup;
       
   429     p->cleanup = clean;
       
   430 }
       
   431 
       
   432 #ifdef POOL_DEBUG
       
   433 void debug_log(char *zone, const char *msgfmt, ...);
       
   434 void _pool_stat(xht h, const char *key, void *data, void *arg)
       
   435 {
       
   436     pool p = (pool)data;
       
   437 
       
   438     if(p->lsize == -1)
       
   439         debug_log("pool_debug","%s: %s is a new pool",p->zone, p->name);
       
   440     else if(p->size > p->lsize)
       
   441         debug_log("pool_debug","%s: %s grew %d",p->zone, p->name, p->size - p->lsize);
       
   442     else if((int)arg)
       
   443         debug_log("pool_debug","%s: %s exists %d",p->zone,p->name, p->size);
       
   444     p->lsize = p->size;
       
   445 }
       
   446 
       
   447 /**
       
   448  * print memory pool statistics (for debugging purposes)
       
   449  *
       
   450  * @param full make a full report? (0 = no, 1 = yes)
       
   451  */
       
   452 void pool_stat(int full)
       
   453 {
       
   454     if (pool__disturbed == NULL || pool__disturbed == (xht)1)
       
   455 	return;
       
   456 
       
   457     ghash_walk(pool__disturbed,_pool_stat,(void *)full);
       
   458     if(pool__total != pool__ltotal)
       
   459         debug_log("pool_debug","%d\ttotal missed mallocs",pool__total);
       
   460     pool__ltotal = pool__total;
       
   461 
       
   462     return;
       
   463 }
       
   464 #else
       
   465 /**
       
   466  * dummy implementation: print memory pool statistics (for debugging purposes, real implementation if POOL_DEBUG is defined)
       
   467  *
       
   468  * @param full make a full report? (0 = no, 1 = yes)
       
   469  */
       
   470 void pool_stat(int full)
       
   471 {
       
   472     return;
       
   473 }
       
   474 #endif