#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */
#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */
#define TALLOC_MAGIC_REFERENCE ((const char *)1)
+#define TALLOC_MAGIC_NO_OWNER ((void *)1)
+#define TALLOC_MAGIC_NO_OWNER_TC ((struct talloc_chunk *)1)
/* by default we abort when given a bad pointer (such as when talloc_free() is called
on a pointer that came from malloc() */
*/
static void *null_context;
static void *autofree_context;
-static void *no_owner_context;
-void *talloc_no_owner_context(void);
+static void *no_owner_context = TALLOC_MAGIC_NO_OWNER;
static inline int _talloc_free(void *ptr);
struct talloc_reference_handle {
talloc_abort("Bad talloc magic value - double free");
}
+static void talloc_abort_no_owner_free(void)
+{
+ talloc_abort("Bad talloc parent - no_owner free");
+}
+
static void talloc_abort_unknown_value(void)
{
talloc_abort("Bad talloc magic value - unknown value");
if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) {
if (tc->flags & TALLOC_FLAG_FREE) {
talloc_abort_double_free();
+ return NULL;
} else {
talloc_abort_unknown_value();
+ return NULL;
}
}
return tc;
if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
} while (0)
+static inline bool talloc_parent_is_no_owner(const void *ptr)
+{
+ struct talloc_chunk *tc;
+ struct talloc_chunk *no_owner_tc = TALLOC_MAGIC_NO_OWNER_TC;
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (no_owner_context != TALLOC_MAGIC_NO_OWNER) {
+ no_owner_tc = talloc_chunk_from_ptr(no_owner_context);
+ }
+
+ if (tc->parent == no_owner_tc) {
+ return true;
+ }
+
+ return false;
+}
+
+#define TALLOC_INVALID_PARENT(tc) \
+ ((((tc)->parent == NULL)||((tc)->parent == TALLOC_MAGIC_NO_OWNER_TC)))
/*
return the parent chunk of a pointer
tc = talloc_chunk_from_ptr(ptr);
while (tc->prev) tc=tc->prev;
+ if (unlikely(TALLOC_INVALID_PARENT(tc))) {
+ return NULL;
+ }
+
return tc->parent;
}
return tc? tc->name : NULL;
}
+static inline void talloc_remove_from_parent(struct talloc_chunk *tc)
+{
+ if (!TALLOC_INVALID_PARENT(tc)) {
+ _TLIST_REMOVE(tc->parent->child, tc);
+ if (tc->parent->child) {
+ tc->parent->child->parent = tc->parent;
+ }
+ } else {
+ if (tc->prev) tc->prev->next = tc->next;
+ if (tc->next) tc->next->prev = tc->prev;
+ }
+// tc->parent = tc->prec = tc->next = NULL;
+}
+
/*
A pool carries an in-pool object count count in the first 16 bytes.
bytes. This is done to support talloc_steal() to a parent outside of the
{
struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
_TLIST_REMOVE(ptr_tc->refs, handle);
- /* If ptr_tc has no refs left and is owner by no_owner_context then we free it */
+ /*
+ * If ptr_tc has no refs left and is owner by
+ * no_owner_context then we free it
+ */
if (ptr_tc->refs == NULL &&
- no_owner_context && talloc_parent(handle->ptr) == no_owner_context) {
- _talloc_free(handle->ptr);
+ talloc_parent_is_no_owner(handle->ptr)) {
+ int ret = _talloc_free(handle->ptr);
+ if (ret != 0) {
+ _TLIST_ADD(ptr_tc->refs, handle);
+ return ret;
+ }
}
return 0;
}
return handle->ptr;
}
+static inline void _talloc_free_children(struct talloc_chunk *tc)
+{
+ void *ptr = TC_PTR_FROM_CHUNK(tc);
+
+ tc->flags |= TALLOC_FLAG_LOOP;
+
+ while (tc->child) {
+ void *child = TC_PTR_FROM_CHUNK(tc->child);
+ if (unlikely(_talloc_free(child) == -1)) {
+ const void *new_parent = no_owner_context;
+ struct talloc_chunk *p = talloc_parent_chunk(ptr);
+
+ /*
+ * we need to work out who will own an abandoned child
+ * if it cannot be freed. In priority order, the first
+ * choice is our parent, and the final choice is the
+ * no owner context.
+ */
+ if (p) {
+ new_parent = TC_PTR_FROM_CHUNK(p);
+ }
+
+ /*
+ * if talloc_free fails because of refs and
+ * not due to the destructor, it will already
+ * have a new owner no_owner_context
+ */
+ if (!talloc_parent_is_no_owner(child)) {
+ talloc_steal(new_parent, child);
+ }
+ }
+ }
+
+ tc->flags &= ~TALLOC_FLAG_LOOP;
+}
/*
internal talloc_free call
_talloc_free(tc->refs);
return _talloc_free(ptr);
} else {
- /* we can't free if we have refs, so no_owner_context becomes the owner */
- _talloc_steal(talloc_no_owner_context(), ptr);
+ /*
+ * we can't free if we have refs,
+ * so no_owner_context becomes the owner
+ */
+ _talloc_steal(no_owner_context, ptr);
}
return -1;
}
tc->destructor = NULL;
}
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
+ talloc_remove_from_parent(tc);
- tc->flags |= TALLOC_FLAG_LOOP;
-
- while (tc->child) {
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = talloc_no_owner_context();
- /* if talloc_free fails because of refs and not due to the destructor,
- it will already have a new owner no_owner_context */
- if (unlikely(_talloc_free(child) == -1)) {
- talloc_steal(new_parent, child);
- }
- }
+ _talloc_free_children(tc);
tc->flags |= TALLOC_FLAG_FREE;
if (*pool_object_count == 0) {
talloc_abort("Pool object count zero!");
+ return 0;
}
*pool_object_count -= 1;
void *_talloc_steal(const void *new_ctx, const void *ptr)
{
struct talloc_chunk *tc, *new_tc;
+ bool no_owner_null = false;
if (unlikely(!ptr)) {
return NULL;
}
+ if (unlikely(talloc_parent_is_no_owner(ptr) &&
+ new_ctx != no_owner_context)) {
+// talloc_abort_no_owner_free();
+// return NULL;
+ }
+
+ if (unlikely(new_ctx == TALLOC_MAGIC_NO_OWNER)) {
+ no_owner_null = true;
+ new_ctx = NULL;
+ }
+
if (unlikely(new_ctx == NULL)) {
new_ctx = null_context;
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(new_ctx == NULL)) {
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
-
+ talloc_remove_from_parent(tc);
+
tc->parent = tc->next = tc->prev = NULL;
+ if (unlikely(no_owner_null)) {
+ tc->parent = TALLOC_MAGIC_NO_OWNER_TC;
+ }
return discard_const_p(void, ptr);
}
return discard_const_p(void, ptr);
}
- if (tc->parent) {
- _TLIST_REMOVE(tc->parent->child, tc);
- if (tc->parent->child) {
- tc->parent->child->parent = tc->parent;
- }
- } else {
- if (tc->prev) tc->prev->next = tc->next;
- if (tc->next) tc->next->prev = tc->prev;
- }
+ talloc_remove_from_parent(tc);
tc->parent = new_tc;
if (new_tc->child) new_tc->child->parent = NULL;
*/
int talloc_unlink(const void *context, void *ptr)
{
- struct talloc_chunk *tc_p, *new_p;
- void *new_parent;
+ struct talloc_chunk *tc_p;
if (ptr == NULL) {
return -1;
return _talloc_free(ptr);
}
- talloc_steal(talloc_no_owner_context(), ptr);
+ talloc_steal(no_owner_context, ptr);
return 0;
}
tc = talloc_chunk_from_ptr(ptr);
- while (tc->child) {
- /* we need to work out who will own an abandoned child
- if it cannot be freed. In priority order, the first
- choice is owner of any remaining reference to this
- pointer, the second choice is our parent, and the
- final choice is the null context. */
- void *child = TC_PTR_FROM_CHUNK(tc->child);
- const void *new_parent = talloc_no_owner_context();
-
- if (unlikely(tc->child->refs)) {
- new_parent = talloc_no_owner_context();
- }
- if (unlikely(_talloc_free(child) == -1)) {
- if (new_parent == null_context) {
- struct talloc_chunk *p = talloc_parent_chunk(ptr);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
- talloc_steal(new_parent, child);
- }
- }
+ _talloc_free_children(tc);
if ((tc->flags & TALLOC_FLAG_POOL)
&& (*talloc_pool_objectcount(tc) == 1)) {
tc = talloc_chunk_from_ptr(ptr);
- if (no_owner_context && tc->parent == no_owner_context) {
- talloc_abort_double_free();
+ if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
+ /* we have a free loop - stop looping */
+ return 0;
+ }
+
+ if (unlikely(talloc_parent_is_no_owner(ptr))) {
+ talloc_abort_no_owner_free();
+ return -1;
}
return _talloc_free(ptr);
if (malloced) {
tc->flags &= ~TALLOC_FLAG_POOLMEM;
}
- if (tc->parent) {
+ if (!TALLOC_INVALID_PARENT(tc)) {
tc->parent->child = tc;
}
if (tc->child) {
FILE *f = (FILE *)_f;
if (is_ref) {
- if (talloc_parent(ptr) == no_owner_context && no_owner_context) {
+ if (talloc_parent_is_no_owner(ptr)) {
fprintf(f, "%*sreference to: %s [no owner]\n", depth*4, "", name);
} else {
fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
{
if (null_context == NULL) {
null_context = _talloc_named_const(NULL, 0, "null_context");
+ no_owner_context = _talloc_named_const(null_context, 0,
+ "no_owner_context");
}
}
{
_talloc_free(null_context);
null_context = NULL;
+ no_owner_context = TALLOC_MAGIC_NO_OWNER;
}
/*
return autofree_context;
}
-/* this is only used by the test suite so that failures from on test don't
- confuse the results of the next test */
-void talloc_erase_no_owner_context(void)
-{
- /* force free all children */
- if (no_owner_context) {
- struct talloc_chunk *tc;
- tc = talloc_chunk_from_ptr(no_owner_context);
- tc->child=NULL;
- tc->refs=NULL;
- _talloc_free(no_owner_context);
- no_owner_context=NULL;
- }
-}
-
-void *talloc_no_owner_context(void)
-{
- if (no_owner_context == NULL) {
- no_owner_context = _talloc_named_const(NULL, 0, "no_owner_context");
- }
- return no_owner_context;
-}
-
size_t talloc_get_size(const void *context)
{
struct talloc_chunk *tc;
}
while (tc && tc->prev) tc = tc->prev;
if (tc) {
+ if (TALLOC_INVALID_PARENT(tc)) {
+ break;
+ }
tc = tc->parent;
}
}
fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
while (tc && tc->prev) tc = tc->prev;
if (tc) {
+ if (tc->parent == TALLOC_MAGIC_NO_OWNER_TC) {
+ fprintf(file, "\t'no_owner_context'\n");
+ break;
+ }
tc = tc->parent;
}
}
if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
while (tc && tc->prev) tc = tc->prev;
if (tc) {
+ if (TALLOC_INVALID_PARENT(tc)) {
+ break;
+ }
tc = tc->parent;
}
}