#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 */
#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
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;
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;
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.
static inline int _talloc_free(void *ptr)
{
struct talloc_chunk *tc;
+ void *oldparent = NULL;
if (unlikely(ptr == NULL)) {
return -1;
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) {
}
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;
}
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;
static void talloc_autofree(void)
{
- _talloc_free(autofree_context);
+ if (autofree_context && *autofree_context == getpid())
+ _talloc_free(autofree_context);
}
/*
*/
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);
}
}
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);
+}