X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftalloc%2Ftalloc.c;h=d02eee772784a07a4afe84ec1270bc0da163f599;hp=fdf8eebed297db56d26abdca812b515702ffdf02;hb=d1a951b82386391b82e48b32403891f85e253565;hpb=1c005e1223d2f8f664ce4b3be6cd7eb4dd449844 diff --git a/ccan/talloc/talloc.c b/ccan/talloc/talloc.c index fdf8eebe..d02eee77 100644 --- a/ccan/talloc/talloc.c +++ b/ccan/talloc/talloc.c @@ -82,6 +82,10 @@ static void *null_context; static pid_t *autofree_context; +static void *(*tc_malloc)(size_t size) = malloc; +static void (*tc_free)(void *ptr) = free; +static void *(*tc_realloc)(void *ptr, size_t size) = realloc; + static void *(*tc_external_realloc)(const void *parent, void *ptr, size_t size); static void (*tc_lock)(const void *ctx); static void (*tc_unlock)(void); @@ -283,7 +287,7 @@ static inline void *__talloc(const void *context, size_t size) } } - tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + tc = (struct talloc_chunk *)tc_malloc(TC_HDR_SIZE+size); alloc_done: return init_talloc(parent, tc, size, external); } @@ -538,13 +542,28 @@ static inline int _talloc_free(const void *ptr) final choice is the null context. */ void *child = TC_PTR_FROM_CHUNK(tc->child); const void *new_parent = null_context; + struct talloc_chunk *old_parent = NULL; if (unlikely(tc->child->refs)) { struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } + /* finding the parent here is potentially quite + expensive, but the alternative, which is to change + talloc to always have a valid tc->parent pointer, + makes realloc more expensive where there are a + large number of children. + + The reason we need the parent pointer here is that + if _talloc_free_internal() fails due to references + or a failing destructor we need to re-parent, but + the free call can invalidate the prev pointer. + */ + if (new_parent == null_context && (tc->child->refs || tc->child->destructor)) { + old_parent = talloc_parent_chunk(ptr); + } if (unlikely(_talloc_free(child) == -1)) { if (new_parent == null_context) { - struct talloc_chunk *p = talloc_parent_chunk(ptr); + struct talloc_chunk *p = old_parent; if (p) new_parent = TC_PTR_FROM_CHUNK(p); } __talloc_steal(new_parent, child); @@ -556,7 +575,7 @@ static inline int _talloc_free(const void *ptr) if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC)) tc_external_realloc(oldparent, tc, 0); else - free(tc); + tc_free(tc); return 0; } @@ -602,7 +621,7 @@ static inline int talloc_unreference(const void *context, const void *ptr) /* remove a specific parent context from a pointer. This is a more - controlled varient of talloc_free() + controlled variant of talloc_free() */ int talloc_unlink(const void *context, void *ptr) { @@ -791,7 +810,7 @@ void *_talloc(const void *context, size_t size) static int talloc_destroy_pointer(void ***pptr) { - if ((uintptr_t)**pptr < getpagesize()) + if ((uintptr_t)**pptr < (uintptr_t)sysconf(_SC_PAGESIZE)) TALLOC_ABORT("Double free or invalid talloc_set?"); /* Invalidate pointer so it can't be used again. */ **pptr = (void *)1; @@ -908,13 +927,13 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n tc->flags |= TALLOC_FLAG_FREE; #if ALWAYS_REALLOC - new_ptr = malloc(size + TC_HDR_SIZE); + new_ptr = tc_malloc(size + TC_HDR_SIZE); if (new_ptr) { memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); - free(tc); + tc_free(tc); } #else - new_ptr = realloc(tc, size + TC_HDR_SIZE); + new_ptr = tc_realloc(tc, size + TC_HDR_SIZE); #endif } @@ -1598,6 +1617,15 @@ int talloc_is_parent(const void *context, const void *ptr) return ret; } +void talloc_set_allocator(void *(*malloc)(size_t size), + void (*free)(void *ptr), + void *(*realloc)(void *ptr, size_t size)) +{ + tc_malloc = malloc; + tc_free = free; + tc_realloc = realloc; +} + void *talloc_add_external(const void *ctx, void *(*realloc)(const void *, void *, size_t), void (*lock)(const void *p),