📄 su_alloc.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@ingroup su_alloc * @CFILE su_alloc.c Home-based memory management. * * @author Pekka Pessi <Pekka.Pessi@nokia.com>. * * @date Created: Thu Aug 19 01:12:25 1999 ppessi */#include "config.h"/**@defgroup su_alloc Memory Management Tutorial * * This page gives a short overview of home-based memory management used * with Sofia. Such home-based memory management is useful when a lot of * memory blocks are allocated for given task. The allocations are done via * the @e memory @e home, which keeps a reference to each block. When the * memory home is then freed, it will free all blocks to which it has * reference. * * Typically, there is a @e home @e object which contains a su_home_t * structure in the beginning of the object (sort of inheritance from * su_home_t): * @code * struct context { * su_home_t ctx_home[1]; * other_t *ctx_stuff; * ... * } * @endcode * * A new home memory pool can be created with su_home_new(): * @code * struct context *ctx = su_home_new(sizeof (struct context)); * @endcode * * It is also possible to create a secondary memory pool that can be * released separately: * * @code * struct context *ctx = su_home_clone(tophome, sizeof (struct context)); * @endcode * * Note that the tophome has a reference to @a ctx structure; whenever * tophome is freed, the @a ctx is also freed. * * You can also create an independent home object by passing NULL as @a * tophome argument. This is identical to the call to su_home_new(). * * The memory allocations using @a ctx proceed then as follows: * @code * zeroblock = su_zalloc(ctx->ctx_home, sizeof (*zeroblock)); * @endcode * * The home memory pool - the home object and all the memory blocks * allocated using it - are freed when su_home_unref() is called: * * @code * su_home_unref(ctx->ctx_home). * @endcode * * @note For historical reasons, su_home_unref() is also known as * su_home_zap(). * * As you might have guessed, it is also possible to use reference counting * with home objects. The function su_home_ref() increases the reference * count, su_home_unref() decreases. A newly allocated home object has * reference count of 1. * * @note Please note that while it is possible to create new references to * secondary home objects which have a parent home, the secondary home * objects will always be destroyed when the parent home is destroyed even * if there are other references left to them. * * The memory blocks in a cloned home object are freed when the object with * home itself is freed: * @code * su_free(tophome, ctx); * @endcode * * @note * The su_home_create() and su_home_destroy() are deprecated. The * function su_home_create() creates a home object with infinite reference * count. Likewise, su_home_init() does the same. * * @section su_home_desctructor_usage Destructors * * It is possible to give a destructor function to a home object. The * destructor releases other resources associated with the home object * besides memory. The destructor function will be called when the reference * count of home reaches zero (upon calling su_home_unref()) or the home * object is otherwise deinitialized (calling su_home_deinit() on * stack-derived objects). * * @section su_home_move_example Combining Allocations * * In some cases, an operation that makes multiple memory allocations may * fail, making those allocations redundant. If the allocations are made * through a temporary home, they can be conveniently freed by calling * su_home_deinit(), for instance. If, however, the operation is successful, * and one wants to keep the allocations, the allocations can be combined * into an existing home with su_home_move(). For example, * @code * int example(su_home_t *home, ...) * { * su_home_t temphome[1] = { SU_HOME_INIT(temphome) }; * * ... do lot of allocations with temphome ... * * if (success) * su_home_move(home, temphome); * su_home_deinit(temphome); * * return success; * } * @endcode * * Note that the @a temphome is deinitialized in every case, but when * operation is successful, the allocations are moved from @a temphome to @a * home. * * @section su_alloc_threadsafe Threadsafe Operation * * If multiple threads need to access same home object, it must be marked as * @c threadsafe by calling su_home_threadsafe() with the home pointer as * argument. The threadsafeness is not inherited by clones. * * The threadsafe home objects can be locked and unlocked with * su_home_mutex_lock() and su_home_mutex_unlock(). * * @section su_alloc_preloading Preloading a Memory Home * * In some situations there is quite heavy overhead when using the global * heap allocator. The overhead caused by the large number of small * allocations can be reduced by using su_home_preload(): it allocates or * preloads some a memory to home to be used as a kind of private heap. The * preloaded memory area is then used to satisfy small enough allocations. * For instance, the SIP parser typically preloads some 2K of memory when it * starts to parse the message. * * @section su_alloc_stack Using Stack * * In some situation, it is sensible to use memory allocated from stack for * some operations. The su_home_auto() function can be used for that * purpose. The memory area from stack is used to satisfy the allocations as * far as possible; if it is not enough, allocation is made from heap. * * The word @e auto refers to the automatic scope; however, the home object * initialized with su_home_auto() must be explicitly deinitialized with * su_home_deinit() when the program exits the scope where the stack frame * used in su_home_auto() was allocate. * */#include <sofia-sip/su_config.h>#include "sofia-sip/su_alloc.h"#include "sofia-sip/su_alloc_stat.h"#include "sofia-sip/su_errno.h"#include <stdlib.h>#include <stddef.h>#include <memory.h>#include <limits.h>#include <assert.h>void (*su_home_locker)(void *mutex);void (*su_home_unlocker)(void *mutex);void (*su_home_mutex_locker)(void *mutex);void (*su_home_mutex_unlocker)(void *mutex);#define MEMLOCK(h) \ (((h) && (h)->suh_lock ? su_home_locker((h)->suh_lock) : (void)0), (h)->suh_blocks)#define UNLOCK(h) (((h) && (h)->suh_lock ? su_home_unlocker((h)->suh_lock) : (void)0), NULL)#ifdef NDEBUG#define MEMCHECK 0#define MEMCHECK_EXTRA 0#elif !defined(MEMCHECK)/* Default settings for valgrinding */#define MEMCHECK 1#define MEMCHECK_EXTRA 0#elif !defined(MEMCHECK_EXTRA)#define MEMCHECK_EXTRA sizeof (unsigned)#endifenum { SUB_N = 31, /**< Initial size */ SUB_N_AUTO = 7, /**< Initial size for autohome */ SUB_P = 29 /**< Secondary probe. * Secondary probe must be relative prime * with all sub_n values */}; #define ALIGNMENT (8)#define ALIGN(n) (((n) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))#define SIZEBITS (sizeof (unsigned) * 8 - 1)typedef struct { unsigned long sua_size:SIZEBITS; /**< Size of the block */ unsigned sua_home:1; /**< Is this another home? */ void *sua_data; /**< Data pointer */} su_alloc_t;struct su_block_s { su_home_t *sub_parent; /**< Parent home */ char *sub_preload; /**< Preload area */ su_home_stat_t *sub_stats; /**< Statistics.. */ void (*sub_destructor)(void *); /**< Destructor function */ unsigned sub_ref; /**< Reference count */ unsigned sub_used; /**< Number of blocks allocated */ unsigned sub_n; /**< Size of hash table */ unsigned sub_prsize:16; /**< Preload size */ unsigned sub_prused:16; /**< Used from preload */ unsigned sub_auto:1; /**< struct su_block_s is from stack! */ unsigned sub_preauto:1; /**< Preload is from stack! */ unsigned sub_auto_all:1; /**< Everything is from stack! */ unsigned :0; su_alloc_t sub_nodes[SUB_N]; /**< Pointers to data/lower blocks */};static void su_home_check_blocks(su_block_t const *b);static void su_home_stats_alloc(su_block_t *, void *p, void *preload, unsigned size, int zero);static void su_home_stats_free(su_block_t *sub, void *p, void *preload, unsigned size);static void _su_home_deinit(su_home_t *home);#define SU_ALLOC_STATS 1#if SU_ALLOC_STATSunsigned count_su_block_find, count_su_block_find_loop;unsigned size_su_block_find, used_su_block_find;unsigned max_size_su_block_find, max_used_su_block_find;unsigned su_block_find_collision, su_block_find_collision_used, su_block_find_collision_size;#endifstatic inline su_alloc_t *su_block_find(su_block_t *b, void *p){ unsigned h, h0, probe;#if SU_ALLOC_STATS unsigned collision = 0; count_su_block_find++; size_su_block_find += b->sub_n; used_su_block_find += b->sub_used; if (b->sub_n > max_size_su_block_find) max_size_su_block_find = b->sub_n; if (b->sub_used > max_used_su_block_find) max_used_su_block_find = b->sub_used;#endif assert(p != NULL); h = h0 = ((unsigned long)p) % b->sub_n; probe = (b->sub_n > SUB_P) ? SUB_P : 1; do { if (b->sub_nodes[h].sua_data == p) return &b->sub_nodes[h]; h += probe; if (h >= b->sub_n) h -= b->sub_n;#if SU_ALLOC_STATS if (++collision > su_block_find_collision) su_block_find_collision = collision, su_block_find_collision_used = b->sub_used, su_block_find_collision_size = b->sub_n; count_su_block_find_loop++;#endif } while (h != h0); return NULL;}static inline su_alloc_t *su_block_add(su_block_t *b, void *p){ unsigned h, probe; assert(p != NULL); h = ((unsigned long)p) % b->sub_n; probe = (b->sub_n > SUB_P) ? SUB_P : 1; while (b->sub_nodes[h].sua_data) { h += probe; if (h >= b->sub_n) h -= b->sub_n; } b->sub_used++; b->sub_nodes[h].sua_data = p; return &b->sub_nodes[h];}static inline int su_is_preloaded(su_block_t const *sub, char *data){ return sub->sub_preload && sub->sub_preload <= data && sub->sub_preload + sub->sub_prsize > data;}static inline int su_alloc_check(su_block_t const *sub, su_alloc_t const *sua){#if MEMCHECK_EXTRA int size, term; assert(sua); if (sua) { size = sua->sua_size; memcpy(&term, (char *)sua->sua_data + size, sizeof (term)); assert(-term == size); return -term == size; } else return 0;#endif return sua != NULL;}/** Allocate the block hash table. * * The function su_hash_alloc() allocates a block hash table of @a n * elements. * * @param home pointer to home object * @param n number of buckets in hash table * * @return * This function returns a pointer to the allocated hash table or * NULL if an error occurred. */static inline su_block_t *su_hash_alloc(int n){ su_block_t *b = calloc(1, offsetof(su_block_t, sub_nodes[n])); if (b) b->sub_n = n; return b;}enum sub_zero { do_malloc, do_calloc, do_clone };/** Allocate a memory block. * * Precondition: locked home * * @param home home to allocate * @param sub block structure used to allocate * @param size * @param zero if true, zero allocated block; * if > 1, allocate a subhome * */static void *sub_alloc(su_home_t *home, su_block_t *sub, long size, enum sub_zero zero){ void *data, *preload = NULL; assert (size >= 0); if (sub == NULL || 3 * sub->sub_used > 2 * sub->sub_n) { /* Resize the hash table */ int i, n, n2, used; su_block_t *b2; if (sub) n = home->suh_blocks->sub_n, n2 = 4 * n + 3, used = sub->sub_used; else n = 0, n2 = SUB_N, used = 0;#if 0 printf("su_alloc(home = %p): realloc block hash of size %d\n", home, n2);#endif if (!(b2 = su_hash_alloc(n2))) return NULL; for (i = 0; i < n; i++) { if (sub->sub_nodes[i].sua_data) su_block_add(b2, sub->sub_nodes[i].sua_data)[0] = sub->sub_nodes[i]; } if (sub) { b2->sub_parent = sub->sub_parent; b2->sub_ref = sub->sub_ref; b2->sub_preload = sub->sub_preload; b2->sub_prsize = sub->sub_prsize; b2->sub_prused = sub->sub_prused; b2->sub_preauto = sub->sub_preauto; b2->sub_destructor = sub->sub_destructor; /* auto_all is not copied! */ b2->sub_stats = sub->sub_stats; } home->suh_blocks = b2; if (sub && !sub->sub_auto) free(sub); sub = b2; } if (size && sub && zero < do_clone && sub->sub_preload && size <= sub->sub_prsize) { /* Use preloaded memory */ size_t prused = sub->sub_prused + size + MEMCHECK_EXTRA; prused = ALIGN(prused); if (prused <= sub->sub_prsize) { preload = (char *)sub->sub_preload + sub->sub_prused; sub->sub_prused = prused; } } if (preload && zero) data = memset(preload, 0, size); else if (preload) data = preload; else if (zero) data = calloc(1, size + MEMCHECK_EXTRA); else data = malloc(size + MEMCHECK_EXTRA); if (data) { su_alloc_t *sua;#if MEMCHECK_EXTRA int term = -size; memcpy((char *)data + size, &term, sizeof (term));#endif if (!preload) sub->sub_auto_all = 0; if (zero == do_clone) { /* Prepare cloned home */ su_home_t *subhome = data; assert(preload == 0); subhome->suh_blocks = su_hash_alloc(SUB_N); if (!subhome->suh_blocks) return (void)free(data), NULL; subhome->suh_size = size; subhome->suh_blocks->sub_parent = home; subhome->suh_blocks->sub_ref = 1; } /* OK, add the block to the hash table. */ sua = su_block_add(sub, data); assert(sua); sua->sua_size = size; sua->sua_home = zero > 1; if (sub->sub_stats) su_home_stats_alloc(sub, data, preload, size, zero); } return data;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -