r2718: - added a talloc_unreference() function as requested by metze.
authorAndrew Tridgell <tridge@samba.org>
Tue, 28 Sep 2004 11:54:17 +0000 (11:54 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:59:26 +0000 (12:59 -0500)
- added documentation for talloc_unreference()

- made the abandoned child logic in talloc_free() clearer and more consistent
(This used to be commit a87584c8e3fb06cd3ff29a918f681b5c6c32b9ff)

source4/lib/talloc.c
source4/torture/local/talloc.c
talloc_guide.txt

index d0056d7b2aa137e5f846243e6c7049b0c7a41bf9..bb2ed9449c2b73da12e6b58f6b2150a9bae11037 100644 (file)
@@ -170,6 +170,34 @@ void *talloc_reference(const void *context, const void *ptr)
        return handle->ptr;
 }
 
+/*
+  remove a secondary reference to a pointer. This undo's what
+  talloc_reference() has done. The context and pointer arguments
+  must match those given to a talloc_reference()
+*/
+void *talloc_unreference(const void *context, const void *ptr)
+{
+       struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
+       struct talloc_reference_handle *h;
+
+       if (context == NULL) {
+               context = null_context;
+       }
+
+       for (h=tc->refs;h;h=h->next) {
+               struct talloc_chunk *tc2 = talloc_chunk_from_ptr(h);
+               const void *parent = tc2->parent?tc2->parent+1:null_context;
+               if (parent == context) break;
+       }
+       if (h == NULL) {
+               return NULL;
+       }
+
+       talloc_set_destructor(h, NULL);
+       DLIST_REMOVE(tc->refs, h);
+       talloc_free(h);
+       return discard_const(ptr);
+}
 
 /*
   add a name to an existing pointer - va_list version
@@ -318,8 +346,21 @@ int talloc_free(void *ptr)
        }
 
        while (tc->child) {
-               talloc_free(talloc_steal(tc->parent?tc->parent+1:null_context, 
-                                        tc->child+1));
+               /* 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->child+1;
+               const void *new_parent = null_context;
+               if (tc->child->refs) {
+                       struct talloc_chunk *ref = talloc_chunk_from_ptr(tc->child->refs);
+                       if (ref->parent) new_parent = ref->parent+1;
+               }
+               if (new_parent == null_context && tc->parent) {
+                       new_parent = tc->parent+1;
+               }
+               talloc_free(talloc_steal(new_parent, child));
        }
 
        if (tc->parent) {
index 4c353ecbbdf32010454deee329715574534950ea..9f518eabf2ba9fd0bdab56247cf58a4d784b0280 100644 (file)
 
 #include "includes.h"
 
-static struct timeval tp1,tp2;
-
-static void start_timer(void)
-{
-       gettimeofday(&tp1,NULL);
-}
-
-static double end_timer(void)
-{
-       gettimeofday(&tp2,NULL);
-       return((tp2.tv_sec - tp1.tv_sec) + 
-              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
-}
-
 /*
   test references 
 */
@@ -68,7 +54,7 @@ static BOOL test_ref1(void)
        talloc_report_full(NULL, stdout);
 
        if (talloc_total_size(NULL) != 0) {
-               printf("non-zero total size\n");
+               printf("failed: non-zero total size\n");
                return False;
        }
 
@@ -93,21 +79,134 @@ static BOOL test_ref2(void)
        r1 = talloc_named_const(NULL, 1, "r1"); 
        ref = talloc_reference(r1, p2);
        talloc_report_full(NULL, stdout);
+
        printf("Freeing ref\n");
        talloc_free(ref);
        talloc_report_full(NULL, stdout);
+
        printf("Freeing p2\n");
        talloc_free(p2);
        talloc_report_full(NULL, stdout);
+
        printf("Freeing p1\n");
        talloc_free(p1);
        talloc_report_full(NULL, stdout);
+
        printf("Freeing r1\n");
        talloc_free(r1);
        talloc_report_full(NULL, stdout);
 
        if (talloc_total_size(NULL) != 0) {
-               printf("non-zero total size\n");
+               printf("failed: non-zero total size\n");
+               return False;
+       }
+
+       return True;
+}
+
+/*
+  test references 
+*/
+static BOOL test_ref3(void)
+{
+       void *p1, *p2, *ref, *r1;
+
+       printf("TESTING PARENT REFERENCE FREE\n");
+
+       p1 = talloc_named_const(NULL, 1, "p1");
+       p2 = talloc_named_const(NULL, 1, "p2");
+
+       r1 = talloc_named_const(p1, 1, "r1");
+
+       ref = talloc_reference(p2, r1);
+
+       talloc_report_full(NULL, stdout);
+
+       printf("Freeing p1\n");
+       talloc_free(p1);
+       talloc_report_full(NULL, stdout);
+
+       printf("Freeing p2\n");
+       talloc_free(p2);
+       talloc_report_full(NULL, stdout);
+
+       if (talloc_total_size(NULL) != 0) {
+               printf("failed: non-zero total size\n");
+               return False;
+       }
+
+       return True;
+}
+
+/*
+  test references 
+*/
+static BOOL test_ref4(void)
+{
+       void *p1, *p2, *ref, *r1;
+
+       printf("TESTING REFERRER REFERENCE FREE\n");
+
+       p1 = talloc_named_const(NULL, 1, "p1");
+       talloc_named_const(p1, 1, "x1");
+       talloc_named_const(p1, 1, "x2");
+       talloc_named_const(p1, 1, "x3");
+       p2 = talloc_named_const(p1, 1, "p2");
+
+       r1 = talloc_named_const(NULL, 1, "r1"); 
+       ref = talloc_reference(r1, p2);
+       talloc_report_full(NULL, stdout);
+
+       printf("Freeing r1\n");
+       talloc_free(r1);
+       talloc_report_full(NULL, stdout);
+
+       printf("Freeing p2\n");
+       talloc_free(p2);
+       talloc_report_full(NULL, stdout);
+
+       printf("Freeing p1\n");
+       talloc_free(p1);
+       talloc_report_full(NULL, stdout);
+
+       if (talloc_total_size(NULL) != 0) {
+               printf("failed: non-zero total size\n");
+               return False;
+       }
+
+       return True;
+}
+
+
+/*
+  test references 
+*/
+static BOOL test_unref1(void)
+{
+       void *p1, *p2, *ref, *r1;
+
+       printf("TESTING UNREFERENCE\n");
+
+       p1 = talloc_named_const(NULL, 1, "p1");
+       talloc_named_const(p1, 1, "x1");
+       talloc_named_const(p1, 1, "x2");
+       talloc_named_const(p1, 1, "x3");
+       p2 = talloc_named_const(p1, 1, "p2");
+
+       r1 = talloc_named_const(p1, 1, "r1");   
+       ref = talloc_reference(r1, p2);
+       talloc_report_full(NULL, stdout);
+
+       printf("Unreferencing r1\n");
+       talloc_unreference(r1, p2);
+       talloc_report_full(NULL, stdout);
+
+       printf("Freeing p1\n");
+       talloc_free(p1);
+       talloc_report_full(NULL, stdout);
+
+       if (talloc_total_size(NULL) != 0) {
+               printf("failed: non-zero total size\n");
                return False;
        }
 
@@ -137,6 +236,8 @@ static BOOL test_speed(void)
 
        printf("talloc: %.0f ops/sec\n", count/end_timer());
 
+       talloc_free(ctx);
+
        start_timer();
        count = 0;
        do {
@@ -164,7 +265,10 @@ BOOL torture_local_talloc(int dummy)
 
        ret &= test_ref1();
        ret &= test_ref2();
+       ret &= test_ref3();
+       ret &= test_ref4();
+       ret &= test_unref1();
        ret &= test_speed();
 
-       return True;
+       return ret;
 }
index ed3f7713aa7c64790d058face042160c0d965539..6920ea16f1acf16ab24995d65815affbf01b22f5 100644 (file)
@@ -124,6 +124,17 @@ ways:
     where it is.
 
 
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+void *talloc_unreference(const void *context, const void *ptr);
+
+The talloc_unreference() function removes a reference added by
+talloc_reference(). It must be called with exactly the same arguments
+as talloc_reference().
+
+Note that if the reference has already been removed using
+talloc_free() then this function will fail and will return NULL.
+
+
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 void talloc_set_destructor(const void *ptr, int (*destructor)(void *));