123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- #ifndef MPOOL_H_
- #define MPOOL_H_
- #include <assert.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include "ctassert.h"
- /**
- * Memory allocator cache. Memory allocations can be returned to the pool
- * and reused by a subsequent allocation without returning all the way to
- * free/malloc. In effect, this is an optimization for the case where we
- * know we will want another allocation of the same size soon, at the expense
- * of keeping memory allocated (and thus preventing any other code from
- * allocating the same memory).
- */
- /* Internal data. */
- struct mpool {
- size_t stacklen;
- size_t allocsize;
- void ** allocs;
- uint64_t nallocs;
- uint64_t nempties;
- int state;
- void ** allocs_static;
- void (* atexitfunc)(void);
- };
- static inline void
- mpool_atexit(struct mpool * M)
- {
- while (M->stacklen)
- free(M->allocs[--M->stacklen]);
- if (M->allocs != M->allocs_static)
- free(M->allocs);
- }
- static inline void *
- mpool_malloc(struct mpool * M, size_t len)
- {
- M->nallocs++;
- if (M->stacklen)
- return (M->allocs[--(M->stacklen)]);
- M->nempties++;
- if (M->state == 0) {
- atexit(M->atexitfunc);
- M->state = 1;
- }
- return (malloc(len));
- }
- static inline void
- mpool_free(struct mpool * M, void * p)
- {
- void ** allocs_new;
- if (p == NULL)
- return;
- if (M->stacklen < M->allocsize) {
- M->allocs[M->stacklen++] = p;
- return;
- }
- if (M->nempties > (M->nallocs >> 8)) {
- /* Sanity check. */
- assert(M->allocsize > 0);
- allocs_new = (void **)malloc(M->allocsize * 2 * sizeof(void *));
- if (allocs_new) {
- memcpy(allocs_new, M->allocs,
- M->allocsize * sizeof(void *));
- if (M->allocs != M->allocs_static)
- free(M->allocs);
- M->allocs = allocs_new;
- M->allocsize = M->allocsize * 2;
- M->allocs[M->stacklen++] = p;
- } else
- free(p);
- } else
- free(p);
- M->nempties = 0;
- M->nallocs = 0;
- }
- /**
- * MPOOL(name, type, size):
- * Define the functions
- *
- * ${type} * mpool_${name}_malloc(void);
- * void mpool_${name}_free(${type} *);
- *
- * which allocate and free structures of type ${type}. A minimum of ${size}
- * such structures are kept cached after _free is called in order to allow
- * future _malloc calls to be rapidly serviced; this limit will be autotuned
- * upwards depending on the allocation/free pattern.
- *
- * Cached structures will be freed at program exit time in order to aid
- * in the detection of memory leaks.
- */
- #define MPOOL(name, type, size) \
- static void mpool_##name##_atexit(void); \
- static void * mpool_##name##_static[size]; \
- static struct mpool mpool_##name##_rec = \
- {0, size, mpool_##name##_static, 0, 0, 0, \
- mpool_##name##_static, mpool_##name##_atexit}; \
- \
- CTASSERT(size > 0); \
- \
- static void \
- mpool_##name##_atexit(void) \
- { \
- \
- mpool_atexit(&mpool_##name##_rec); \
- } \
- \
- static inline type * \
- mpool_##name##_malloc(void) \
- { \
- \
- return (mpool_malloc(&mpool_##name##_rec, sizeof(type))); \
- } \
- \
- static inline void \
- mpool_##name##_free(type * p) \
- { \
- \
- mpool_free(&mpool_##name##_rec, p); \
- } \
- \
- static void (* mpool_##name##_dummyptr)(void); \
- static inline void \
- mpool_##name##_dummyfunc(void) \
- { \
- \
- (void)mpool_##name##_malloc; \
- (void)mpool_##name##_free; \
- (void)mpool_##name##_dummyptr; \
- } \
- static void (* mpool_##name##_dummyptr)(void) = mpool_##name##_dummyfunc; \
- struct mpool_##name##_dummy
- #endif /* !MPOOL_H_ */
|