From 6502a8728975624437e172ea6b034b24295ceda5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 29 Jun 2008 19:05:47 +1000 Subject: [PATCH] External allocator support. --- ccan/talloc/talloc.c | 76 ++++++++++++++++++++++----- ccan/talloc/talloc.h | 26 +++++++++ ccan/talloc/test/run-external-alloc.c | 64 ++++++++++++++++++++++ 3 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 ccan/talloc/test/run-external-alloc.c diff --git a/ccan/talloc/talloc.c b/ccan/talloc/talloc.c index d624b917..59a981b4 100644 --- a/ccan/talloc/talloc.c +++ b/ccan/talloc/talloc.c @@ -45,6 +45,7 @@ #define TALLOC_MAGIC 0xe814ec70 #define TALLOC_FLAG_FREE 0x01 #define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_FLAG_EXT_ALLOC 0x04 #define TALLOC_MAGIC_REFERENCE ((const char *)1) /* by default we abort when given a bad pointer (such as when talloc_free() is called @@ -79,6 +80,10 @@ static void *null_context; static void *autofree_context; +static void *(*tc_external_alloc)(void *parent, size_t size); +static void (*tc_external_free)(void *ptr, void *parent); +static void *(*tc_external_realloc)(void *ptr, void *parent, size_t size); + struct talloc_reference_handle { struct talloc_reference_handle *next, *prev; void *ptr; @@ -181,6 +186,8 @@ const char *talloc_parent_name(const void *ptr) static inline void *__talloc(const void *context, size_t size) { struct talloc_chunk *tc; + struct talloc_chunk *parent = NULL; /* Prevent spurious gcc warning */ + unsigned flags = TALLOC_MAGIC; if (unlikely(context == NULL)) { context = null_context; @@ -190,19 +197,28 @@ static inline void *__talloc(const void *context, size_t size) return NULL; } + if (likely(context)) { + parent = talloc_chunk_from_ptr(context); + if (unlikely(parent->flags & TALLOC_FLAG_EXT_ALLOC)) { + tc = tc_external_alloc(TC_PTR_FROM_CHUNK(parent), + TC_HDR_SIZE+size); + flags |= TALLOC_FLAG_EXT_ALLOC; + goto alloc_done; + } + } + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); +alloc_done: if (unlikely(tc == NULL)) return NULL; tc->size = size; - tc->flags = TALLOC_MAGIC; + tc->flags = flags; tc->destructor = NULL; tc->child = NULL; tc->name = NULL; tc->refs = NULL; if (likely(context)) { - struct talloc_chunk *parent = talloc_chunk_from_ptr(context); - if (parent->child) { parent->child->parent = NULL; tc->next = parent->child; @@ -319,6 +335,7 @@ void *_talloc_reference(const void *context, const void *ptr) static inline int _talloc_free(void *ptr) { struct talloc_chunk *tc; + void *oldparent = NULL; if (unlikely(ptr == NULL)) { return -1; @@ -362,6 +379,7 @@ static inline int _talloc_free(void *ptr) } if (tc->parent) { + oldparent = TC_PTR_FROM_CHUNK(tc->parent); _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; @@ -395,7 +413,12 @@ static inline int _talloc_free(void *ptr) } tc->flags |= TALLOC_FLAG_FREE; - free(tc); + + if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC)) + tc_external_free(tc, oldparent); + else + free(tc); + return 0; } @@ -771,18 +794,26 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n return NULL; } - /* by resetting magic we catch users of the old memory */ - tc->flags |= TALLOC_FLAG_FREE; + if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC)) { + /* need to get parent before setting free flag. */ + void *parent = talloc_parent(ptr); + tc->flags |= TALLOC_FLAG_FREE; + new_ptr = tc_external_realloc(tc, parent, size + TC_HDR_SIZE); + } else { + /* by resetting magic we catch users of the old memory */ + tc->flags |= TALLOC_FLAG_FREE; #if ALWAYS_REALLOC - new_ptr = malloc(size + TC_HDR_SIZE); - if (new_ptr) { - memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); - free(tc); - } + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); + free(tc); + } #else - new_ptr = realloc(tc, size + TC_HDR_SIZE); + new_ptr = realloc(tc, size + TC_HDR_SIZE); #endif + } + if (unlikely(!new_ptr)) { tc->flags &= ~TALLOC_FLAG_FREE; return NULL; @@ -1401,3 +1432,24 @@ int talloc_is_parent(const void *context, const void *ptr) } return 0; } + +void talloc_external_enable(void *(*alloc)(void *parent, size_t size), + void (*free)(void *ptr, void *parent), + void *(*realloc)(void *ptr, void *parent, size_t)) +{ + tc_external_alloc = alloc; + tc_external_free = free; + tc_external_realloc = realloc; +} + +void talloc_mark_external(void *context) +{ + struct talloc_chunk *tc; + + if (unlikely(context == NULL)) { + context = null_context; + } + + tc = talloc_chunk_from_ptr(context); + tc->flags |= TALLOC_FLAG_EXT_ALLOC; +} diff --git a/ccan/talloc/talloc.h b/ccan/talloc/talloc.h index 7da8d975..da416a4b 100644 --- a/ccan/talloc/talloc.h +++ b/ccan/talloc/talloc.h @@ -928,6 +928,32 @@ size_t talloc_get_size(const void *ctx); */ void *talloc_find_parent_byname(const void *ctx, const char *name); +/** + * talloc_external_enable - set external allocators for some nodes + * @alloc: the malloc() equivalent + * @free: the free() equivalent + * @realloc: the realloc() equivalent + * + * talloc_mark_external() can be used to mark nodes whose children should + * use separate allocators. Currently the set of allocators is global, not + * per-node, and is set with this function. + * + * The parent pointers is the talloc pointer of the parent. + */ +void talloc_external_enable(void *(*alloc)(void *parent, size_t size), + void (*free)(void *ptr, void *parent), + void *(*realloc)(void *ptr, void *parent, size_t)); + +/** + * talloc_mark_external - children of this note must use external allocators + * @p: the talloc pointer + * + * This function indicates that all children (and children's children etc) + * should use the allocators set up wth talloc_external_enable() rather than + * normal malloc/free. + */ +void talloc_mark_external(void *ptr); + /* The following definitions come from talloc.c */ void *_talloc(const void *context, size_t size); void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); diff --git a/ccan/talloc/test/run-external-alloc.c b/ccan/talloc/test/run-external-alloc.c new file mode 100644 index 00000000..85b3b014 --- /dev/null +++ b/ccan/talloc/test/run-external-alloc.c @@ -0,0 +1,64 @@ +#include "talloc/talloc.c" +#include "tap/tap.h" +#include + +static int ext_alloc_count, ext_free_count, ext_realloc_count; +static void *expected_parent; + +static void *ext_alloc(void *parent, size_t size) +{ + ok1(parent == expected_parent); + ext_alloc_count++; + return malloc(size); +} + +static void ext_free(void *ptr, void *parent) +{ + ok1(parent == expected_parent); + ext_free_count++; + free(ptr); +} + +static void *ext_realloc(void *ptr, void *parent, size_t size) +{ + ok1(parent == expected_parent); + ext_realloc_count++; + return realloc(ptr, size); +} + +int main(void) +{ + char *p, *p2, *head; + plan_tests(10); + + talloc_external_enable(ext_alloc, ext_free, ext_realloc); + head = talloc(NULL, char); + assert(head); + expected_parent = head; + + talloc_mark_external(head); + + p = talloc_array(head, char, 1); + ok1(ext_alloc_count == 1); + assert(p); + + /* Child is also externally allocated */ + expected_parent = p; + p2 = talloc(p, char); + ok1(ext_alloc_count == 2); + + expected_parent = head; + p = talloc_realloc(NULL, p, char, 1000); + ok1(ext_realloc_count == 1); + assert(p); + + expected_parent = p; + talloc_free(p2); + ok1(ext_free_count == 1); + + expected_parent = head; + talloc_free(p); + ok1(ext_free_count == 2); + + return exit_status(); +} -- 2.39.2