]> git.ozlabs.org Git - ccan/blobdiff - ccan/talloc/talloc.c
ttxml: removed cruft from tests
[ccan] / ccan / talloc / talloc.c
index 1b96f90fd08baf614dc6da16e3b2fd3e73071327..0c6bcda955165e5cb088ec9dadd9da5863c95676 100644 (file)
 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;
 }
@@ -664,7 +683,7 @@ int talloc_unlink(const void *context, void *ptr)
 /*
   add a name to an existing pointer - va_list version
 */
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
+static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_FMT(2,0);
 
 static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
 {
@@ -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),