diff options
author | Nick Mathewson <nickm@torproject.org> | 2007-04-11 00:30:22 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2007-04-11 00:30:22 +0000 |
commit | d7359eb996218f4492a5583c34ab8ee7d9315e7d (patch) | |
tree | ee45fedecacdfd83bea07a57bbf89cd8fa2c1463 /src/common/mempool.c | |
parent | f95d232483a0e9fd78563bfdc061016baab6ef97 (diff) | |
download | tor-d7359eb996218f4492a5583c34ab8ee7d9315e7d.tar.gz tor-d7359eb996218f4492a5583c34ab8ee7d9315e7d.zip |
r12335@catbus: nickm | 2007-04-10 16:53:48 -0400
Initial version of memory pool logic. Needs unit tests. Made to be easily separable from Tor.
svn:r9937
Diffstat (limited to 'src/common/mempool.c')
-rw-r--r-- | src/common/mempool.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/common/mempool.c b/src/common/mempool.c new file mode 100644 index 0000000000..f62821ba7c --- /dev/null +++ b/src/common/mempool.c @@ -0,0 +1,293 @@ +/* Copyright 2007 Nick Mathewson */ +/* See LICENSE for licensing information */ +/* $Id: /tor/trunk/src/common/util.c 12153 2007-03-12T03:11:12.797278Z nickm $ */ +#include <stdlib.h> +#include <string.h> + +#define MEMPOOL_PRIVATE +#include "mempool.h" + +/* DRAWBACKS: + * - Not even slightly threadsafe. + * - Likes to have lots of items per chunks. + * - One pointer overhead per allocated thing. (The alternative is + * something like glib's use of an RB-tree to keep track of what + * chunk any given piece of memory is in.) + * - Only aligns allocated things to void* level: redefign ALIGNMENT_TYPE + * if you need doubles. + * - Could probably be optimized a bit; the representation contains + * a bit more info than it really needs to have. + */ + +/* NOTES: + * - The algorithm is similar to the one used by Python, but assumes that + * we'll know in advance which objects we want to pool, and doesn't + * try to handle a zillion objects of weird different sizes. + */ + +#if 1 +/* Tor dependencies */ +#include "orconfig.h" +#include "util.h" +#include "compat.h" +#include "log.h" +#define ALLOC(x) tor_malloc(x) +#define FREE(x) tor_free(x) +#define ASSERT(x) tor_assert(x) +/* End Tor dependencies */ +#else +#include <assert.h> +#define PREDICT_UNLIKELY(x) (x) +#define PREDICT_LIKELY(x) (x) +#define ALLOC(x) malloc(x) +#define FREE(x) free(x) +#define STRUCT_OFFSET(tp, member) \ + ((off_t) (((char*)&((tp*)0)->member)-(char*)0)) +#define ASSERT(x) assert(x) +#endif + +/* Tuning parameters */ +/** DOCDOC */ +#define ALIGNMENT_TYPE void * +/** DOCDOC */ +#define ALIGNMENT sizeof(void*) +/** DOCDOC */ +#define MAX_CHUNK (8*(1L<<20)) +/** DOCDOC */ +#define MIN_CHUNK 4096 + +/** DOCDOC */ +struct mp_allocated_t { + mp_chunk_t *in_chunk; + union { + mp_allocated_t *next_free; + char mem[1]; + ALIGNMENT_TYPE _dummy; + }; +}; + +/** DOCDOC */ +#define MP_CHUNK_MAGIC 0x09870123 + +/** DOCDOC */ +#define CHUNK_OVERHEAD (sizeof(mp_chunk_t)-1) + +/** DOCDOC */ +#define A2M(a) (&(a)->mem) +/** DOCDOC */ +#define M2A(p) ( ((char*)p) - STRUCT_OFFSET(mp_chunk_t, mem) ) + +/* INVARIANT: every chunk can hold 2 or more items. */ + +/** DOCDOC */ +static mp_chunk_t * +mp_chunk_new(mp_pool_t *pool) +{ + size_t sz = pool->new_chunk_capacity * pool->item_alloc_size; + mp_chunk_t *chunk = ALLOC(CHUNK_OVERHEAD + sz); + memset(chunk, 0, sizeof(mp_chunk_t)); /* Doesn't clear the whole thing. */ + chunk->magic = MP_CHUNK_MAGIC; + chunk->capacity = pool->new_chunk_capacity; + chunk->mem_size = sz; + chunk->next_mem = chunk->mem; + chunk->pool = pool; + return chunk; +} + +/** DOCDOC */ +void * +mp_pool_get(mp_pool_t *pool) +{ + mp_chunk_t *chunk; + mp_allocated_t *allocated; + if (PREDICT_LIKELY(pool->used_chunks != NULL)) { + chunk = pool->used_chunks; + } else if (pool->empty_chunks) { + /* Put the most recently emptied chunk on the used list. */ + chunk = pool->empty_chunks; + pool->empty_chunks = chunk->next; + if (chunk->next) + chunk->next->prev = NULL; + chunk->next = pool->used_chunks; + if (chunk->next) + chunk->next->prev = chunk; + pool->used_chunks = chunk; + ASSERT(!chunk->prev); + } else { + /* Allocate a new chunk and add it to the used list. */ + chunk = mp_chunk_new(pool); + chunk->next = pool->used_chunks; + if (chunk->next) + chunk->next->prev = chunk; + pool->used_chunks = chunk; + ASSERT(!chunk->prev); + } + + ASSERT(chunk->n_allocated < chunk->capacity); + + if (chunk->first_free) { + allocated = chunk->first_free; + chunk->first_free = allocated->next_free; + allocated->next_free = NULL; /* debugging */ + } else { + ASSERT(chunk->next_mem + pool->item_alloc_size < + chunk->mem + chunk->mem_size); + allocated = (void*)chunk->next_mem; + chunk->next_mem += pool->item_alloc_size; + allocated->in_chunk = chunk; + } + + ++chunk->n_allocated; + if (PREDICT_UNLIKELY(chunk->n_allocated == chunk->capacity)) { + /* This is now a full chunk. */ + ASSERT(chunk == pool->used_chunks); + ASSERT(chunk->prev == NULL); + pool->used_chunks = chunk->next; + if (chunk->next) + chunk->next->prev = NULL; + + chunk->next = pool->full_chunks; + pool->full_chunks->prev = chunk; + pool->full_chunks = chunk; + } + + return A2M(allocated); +} + +/** DOCDOC */ +void +mp_pool_release(void *_item) +{ + mp_allocated_t *allocated = (void*) M2A(_item); + mp_chunk_t *chunk = allocated->in_chunk; + + ASSERT(chunk); + ASSERT(chunk->magic == MP_CHUNK_MAGIC); + ASSERT(chunk->n_allocated > 0); + + allocated->next_free = chunk->first_free; + chunk->first_free = allocated; + + if (PREDICT_UNLIKELY(chunk->n_allocated == chunk->capacity)) { + /* This chunk was full and is about to be used. */ + mp_pool_t *pool = chunk->pool; + /* unlink from full */ + if (chunk->prev) + chunk->prev->next = chunk->next; + if (chunk->next) + chunk->next->prev = chunk->prev; + if (chunk == pool->full_chunks) + pool->full_chunks = chunk->next; + + /* link to used */ + chunk->next = pool->used_chunks; + chunk->prev = NULL; + if (chunk->next) + chunk->next->prev = chunk; + pool->used_chunks = chunk; + } else if (PREDICT_UNLIKELY(chunk->n_allocated == 1)) { + /* This was used and is about to be empty. */ + mp_pool_t *pool = chunk->pool; + /* unlink from used */ + if (chunk->prev) + chunk->prev->next = chunk->next; + if (chunk->next) + chunk->next->prev = chunk->prev; + if (chunk == pool->used_chunks) + pool->used_chunks = chunk->next; + + /* link to empty */ + chunk->next = pool->empty_chunks; + chunk->prev = NULL; + if (chunk->next) + chunk->next->prev = chunk; + pool->empty_chunks = chunk; + + /* reset guts to defragment this chunk. */ + chunk->first_free = NULL; + chunk->next_mem = chunk->mem; + + ++pool->n_empty_chunks; + } + --chunk->n_allocated; +} + +/** DOCDOC */ +mp_pool_t * +mp_pool_new(size_t item_size, size_t chunk_capacity) +{ + mp_pool_t *pool; + size_t alloc_size; + + pool = ALLOC(sizeof(mp_pool_t)); + memset(pool, 0, sizeof(mp_pool_t)); + + /* First, minimal size with overhead. */ + alloc_size = STRUCT_OFFSET(mp_allocated_t, mem) + item_size; + if (alloc_size < sizeof(mp_allocated_t)) + alloc_size = sizeof(mp_allocated_t); + + /* Then, round up to alignment. */ + if (alloc_size % ALIGNMENT) { + alloc_size = alloc_size + ALIGNMENT - (alloc_size % ALIGNMENT); + } + if (alloc_size < ALIGNMENT) + alloc_size = ALIGNMENT; + + ASSERT((alloc_size % ALIGNMENT) == 0); + + if (chunk_capacity > MAX_CHUNK) + chunk_capacity = MAX_CHUNK; + + if (chunk_capacity < alloc_size * 2 + CHUNK_OVERHEAD) + chunk_capacity = alloc_size * 2 + CHUNK_OVERHEAD; + + if (chunk_capacity < MIN_CHUNK) /* Guess system page size. */ + chunk_capacity = MIN_CHUNK; + + pool->new_chunk_capacity = (chunk_capacity-CHUNK_OVERHEAD / alloc_size); + pool->item_alloc_size = alloc_size; + + return pool; +} + +/** DOCDOC */ +void +mp_pool_clean(mp_pool_t *pool) +{ + if (pool->empty_chunks) { + mp_chunk_t *next, *chunk = pool->empty_chunks->next; + while (chunk) { + next = chunk->next; + FREE(chunk); + chunk = next; + } + pool->empty_chunks->next = NULL; + pool->n_empty_chunks = 1; + } +} + +/** DOCDOC */ +static void +destroy_chunks(mp_chunk_t *chunk) +{ + mp_chunk_t *next; + while (chunk) { + chunk->magic = 0xd3adb33f; + next = chunk->next; + FREE(chunk); + chunk = next; + } +} + +/** DOCDOC */ +void +mp_pool_destroy(mp_pool_t *pool) +{ + destroy_chunks(pool->empty_chunks); + destroy_chunks(pool->used_chunks); + destroy_chunks(pool->full_chunks); + memset(pool, 0xe0, sizeof(mp_pool_t)); + FREE(pool); +} + |