]> git.ozlabs.org Git - ccan/blobdiff - ccan/talloc/talloc.c
Simplify external allocation (realloc only from Tridge)
[ccan] / ccan / talloc / talloc.c
index d624b9174dde42b8e892fb5764cb9544bd249671..8ec5b4b150006d909d9b7f65cabd582d8c09329f 100644 (file)
@@ -35,6 +35,8 @@
 #include <string.h>
 #include <stdint.h>
 #include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 /* use this to force every realloc to change the pointer, to stress test
    code that might not cope */
@@ -45,6 +47,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 
@@ -77,7 +80,9 @@
    NULL
 */
 static void *null_context;
-static void *autofree_context;
+static pid_t *autofree_context;
+
+static void *(*tc_external_realloc)(const void *parent, void *ptr, size_t size);
 
 struct talloc_reference_handle {
        struct talloc_reference_handle *next, *prev;
@@ -175,34 +180,23 @@ const char *talloc_parent_name(const void *ptr)
        return tc? tc->name : NULL;
 }
 
-/* 
-   Allocate a bit of memory as a child of an existing pointer
-*/
-static inline void *__talloc(const void *context, size_t size)
+static void *init_talloc(struct talloc_chunk *parent,
+                        struct talloc_chunk *tc,
+                        size_t size, int external)
 {
-       struct talloc_chunk *tc;
-
-       if (unlikely(context == NULL)) {
-               context = null_context;
-       }
-
-       if (unlikely(size >= MAX_TALLOC_SIZE)) {
+       if (unlikely(tc == NULL))
                return NULL;
-       }
-
-       tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
-       if (unlikely(tc == NULL)) return NULL;
 
        tc->size = size;
        tc->flags = TALLOC_MAGIC;
+       if (external)
+               tc->flags |= TALLOC_FLAG_EXT_ALLOC;
        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 (likely(parent)) {
                if (parent->child) {
                        parent->child->parent = NULL;
                        tc->next = parent->child;
@@ -220,6 +214,38 @@ static inline void *__talloc(const void *context, size_t size)
        return TC_PTR_FROM_CHUNK(tc);
 }
 
+/* 
+   Allocate a bit of memory as a child of an existing pointer
+*/
+static inline void *__talloc(const void *context, size_t size)
+{
+       struct talloc_chunk *tc;
+       struct talloc_chunk *parent = NULL;
+       int external = 0;
+
+       if (unlikely(context == NULL)) {
+               context = null_context;
+       }
+
+       if (unlikely(size >= MAX_TALLOC_SIZE)) {
+               return NULL;
+       }
+
+       if (likely(context)) {
+               parent = talloc_chunk_from_ptr(context);
+               if (unlikely(parent->flags & TALLOC_FLAG_EXT_ALLOC)) {
+                       tc = tc_external_realloc(context, NULL,
+                                                TC_HDR_SIZE+size);
+                       external = 1;
+                       goto alloc_done;
+               }
+       }
+
+       tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
+alloc_done:
+       return init_talloc(parent, tc, size, external);
+}
+
 /*
   setup a destructor to be called on free of a pointer
   the destructor should return 0 on success, or -1 on failure.
@@ -319,6 +345,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;
@@ -361,6 +388,9 @@ static inline int _talloc_free(void *ptr)
                tc->destructor = NULL;
        }
 
+       if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC))
+               oldparent = talloc_parent(ptr);
+
        if (tc->parent) {
                _TLIST_REMOVE(tc->parent->child, tc);
                if (tc->parent->child) {
@@ -395,7 +425,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_realloc(oldparent, tc, 0);
+       else
+               free(tc);
+
        return 0;
 }
 
@@ -771,18 +806,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(parent, tc, 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; 
@@ -1303,7 +1346,8 @@ static int talloc_autofree_destructor(void *ptr)
 
 static void talloc_autofree(void)
 {
-       _talloc_free(autofree_context);
+       if (autofree_context && *autofree_context == getpid())
+               _talloc_free(autofree_context);
 }
 
 /*
@@ -1312,8 +1356,11 @@ static void talloc_autofree(void)
 */
 void *talloc_autofree_context(void)
 {
-       if (autofree_context == NULL) {
-               autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
+       if (autofree_context == NULL || *autofree_context != getpid()) {
+               autofree_context = talloc(NULL, pid_t);
+               *autofree_context = getpid();
+               talloc_set_name_const(autofree_context, "autofree_context");
+               
                talloc_set_destructor(autofree_context, talloc_autofree_destructor);
                atexit(talloc_autofree);
        }
@@ -1401,3 +1448,22 @@ int talloc_is_parent(const void *context, const void *ptr)
        }
        return 0;
 }
+
+void *talloc_add_external(const void *ctx,
+                         void *(*realloc)(const void *, void *, size_t))
+{
+       struct talloc_chunk *tc, *parent;
+
+       if (tc_external_realloc && tc_external_realloc != realloc)
+               TALLOC_ABORT("talloc_add_external realloc replaced");
+       tc_external_realloc = realloc;
+
+       if (unlikely(ctx == NULL)) {
+               ctx = null_context;
+               parent = NULL;
+       } else
+               parent = talloc_chunk_from_ptr(ctx);    
+
+       tc = tc_external_realloc(ctx, NULL, TC_HDR_SIZE);
+       return init_talloc(parent, tc, 0, 1);
+}