*/
static void *null_context;
static void *autofree_context;
+static void *no_owner_context;
+void *talloc_no_owner_context(void);
+static inline int _talloc_free(void *ptr);
struct talloc_reference_handle {
struct talloc_reference_handle *next, *prev;
{
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->refs == NULL &&
+ no_owner_context && talloc_parent(handle->ptr) == no_owner_context) {
+ _talloc_free(handle->ptr);
+ }
return 0;
}
tc = talloc_chunk_from_ptr(ptr);
if (unlikely(tc->refs)) {
+ /*TODO: Fix this loop detection. The code here breaks top reference if
+ it is also one of our children. It is insufficient and can lead
+ to dangling references */
+
int is_child;
/* check this is a reference from a child or grantchild
* back to it's parent or grantparent
_talloc_free(tc->refs);
return _talloc_free(ptr);
} else {
- /* the first reference becomes the owner */
- _talloc_steal(talloc_parent(tc->refs), ptr);
- _talloc_free(tc->refs);
+ /* we can't free if we have refs, so no_owner_context becomes the owner */
+ _talloc_steal(talloc_no_owner_context(), ptr);
}
return -1;
}
tc->flags |= TALLOC_FLAG_LOOP;
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 = null_context;
- if (unlikely(tc->child->refs)) {
- struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
- }
+ 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)) {
- 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);
}
}
return _talloc_free(ptr);
}
- new_p = talloc_parent_chunk(tc_p->refs);
- if (new_p) {
- new_parent = TC_PTR_FROM_CHUNK(new_p);
- } else {
- new_parent = NULL;
- }
-
- if (talloc_unreference(new_parent, ptr) != 0) {
- return -1;
- }
-
- talloc_steal(new_parent, ptr);
-
+ talloc_steal(talloc_no_owner_context(), ptr);
return 0;
}
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 = null_context;
+ const void *new_parent = talloc_no_owner_context();
+
if (unlikely(tc->child->refs)) {
- struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
- if (p) new_parent = TC_PTR_FROM_CHUNK(p);
+ new_parent = talloc_no_owner_context();
}
if (unlikely(_talloc_free(child) == -1)) {
if (new_parent == null_context) {
*/
int talloc_free(void *ptr)
{
+ struct talloc_chunk *tc;
+
+ if (unlikely(ptr == NULL)) {
+ return -1;
+ }
+
+ tc = talloc_chunk_from_ptr(ptr);
+
+ if (no_owner_context && tc->parent == no_owner_context) {
+ talloc_abort_double_free();
+ }
+
return _talloc_free(ptr);
}
FILE *f = (FILE *)_f;
if (is_ref) {
- fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+ if (talloc_parent(ptr) == no_owner_context && no_owner_context) {
+ fprintf(f, "%*sreference to: %s [no owner]\n", depth*4, "", name);
+ } else {
+ fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
+ }
return;
}
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;