Seriously revisit locking required for antithread.
authorRusty Russell <rusty@rustcorp.com.au>
Sun, 11 Jan 2009 13:51:39 +0000 (00:21 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Sun, 11 Jan 2009 13:51:39 +0000 (00:21 +1030)
Remove test that never ran properly anyway (still bug in current talloc
testsuite).

ccan/talloc/talloc.c
ccan/talloc/talloc.h
ccan/talloc/test/run-external-alloc.c
ccan/talloc/test/run.c

index 815f255f7f2ef4c11f905502fdbab0f7d05c445b..1424748eef7fa9a5b49caabd9fb095914b51cd50 100644 (file)
@@ -83,9 +83,8 @@ static void *null_context;
 static pid_t *autofree_context;
 
 static void *(*tc_external_realloc)(const void *parent, void *ptr, size_t size);
-static void (*tc_lock)(void *);
-static void (*tc_unlock)(void *);
-static void *tc_lock_data;
+static void (*tc_lock)(const void *ctx);
+static void (*tc_unlock)(void);
 
 struct talloc_reference_handle {
        struct talloc_reference_handle *next, *prev;
@@ -150,16 +149,27 @@ do { \
        if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
 } while (0)
 
-static inline void lock(void)
+static int locked;
+static inline void lock(const void *p)
 {
-       if (tc_lock)
-               tc_lock(tc_lock_data);
+       if (tc_lock && p) {
+               struct talloc_chunk *tc = talloc_chunk_from_ptr(p);
+
+               if (tc->flags & TALLOC_FLAG_EXT_ALLOC) {
+                       if (locked)
+                               TALLOC_ABORT("nested locking");
+                       tc_lock(tc);
+                       locked = 1;
+               }
+       }
 }
 
 static inline void unlock(void)
 {
-       if (tc_lock)
-               tc_unlock(tc_lock_data);
+       if (locked) {
+               tc_unlock();
+               locked = 0;
+       }
 }
 
 /*
@@ -179,14 +189,23 @@ static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
        return tc->parent;
 }
 
-void *talloc_parent(const void *ptr)
+/* This version doesn't do locking, so you must already have it. */
+static void *talloc_parent_nolock(const void *ptr)
 {
        struct talloc_chunk *tc;
 
-       lock();
        tc = talloc_parent_chunk(ptr);
+       return tc ? TC_PTR_FROM_CHUNK(tc) : NULL;
+}
+
+void *talloc_parent(const void *ptr)
+{
+       void *parent;
+
+       lock(ptr);
+       parent = talloc_parent_nolock(ptr);
        unlock();
-       return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
+       return parent;
 }
 
 /*
@@ -196,7 +215,7 @@ const char *talloc_parent_name(const void *ptr)
 {
        struct talloc_chunk *tc;
 
-       lock();
+       lock(ptr);
        tc = talloc_parent_chunk(ptr);
        unlock();
 
@@ -346,7 +365,7 @@ void *_talloc_reference(const void *context, const void *ptr)
        struct talloc_reference_handle *handle;
        if (unlikely(ptr == NULL)) return NULL;
 
-       lock();
+       lock(context);
        tc = talloc_chunk_from_ptr(ptr);
        handle = (struct talloc_reference_handle *)_talloc_named_const(context,
                                                   sizeof(struct talloc_reference_handle),
@@ -497,7 +516,7 @@ static inline int _talloc_free(void *ptr)
        }
 
        if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC))
-               oldparent = talloc_parent(ptr);
+               oldparent = talloc_parent_nolock(ptr);
 
        if (tc->parent) {
                _TLIST_REMOVE(tc->parent->child, tc);
@@ -546,7 +565,7 @@ void *_talloc_steal(const void *new_ctx, const void *ptr)
 {
        void *p;
 
-       lock();
+       lock(new_ctx);
        p = __talloc_steal(new_ctx, ptr);
        unlock();
        return p;
@@ -598,7 +617,7 @@ int talloc_unlink(const void *context, void *ptr)
                context = null_context;
        }
 
-       lock();
+       lock(context);
        if (talloc_unreference(context, ptr) == 0) {
                unlock();
                return 0;
@@ -682,7 +701,7 @@ void *talloc_named(const void *context, size_t size, const char *fmt, ...)
        void *ptr;
        const char *name;
 
-       lock();
+       lock(context);
        ptr = __talloc(context, size);
        unlock();
        if (unlikely(ptr == NULL)) return NULL;
@@ -747,9 +766,7 @@ void *talloc_init(const char *fmt, ...)
         */
        talloc_enable_null_tracking();
 
-       lock();
        ptr = __talloc(NULL, 0);
-       unlock();
        if (unlikely(ptr == NULL)) return NULL;
 
        va_start(ap, fmt);
@@ -788,7 +805,7 @@ void talloc_set_name_const(const void *ptr, const char *name)
 void *talloc_named_const(const void *context, size_t size, const char *name)
 {
        void *p;
-       lock();
+       lock(context);
        p = _talloc_named_const(context, size, name);
        unlock();
        return p;
@@ -805,7 +822,8 @@ void *talloc_named_const(const void *context, size_t size, const char *name)
 int talloc_free(void *ptr)
 {
        int saved_errno = errno, ret;
-       lock();
+
+       lock(ptr);
        ret = _talloc_free(ptr);
        unlock();
        if (ret == 0)
@@ -846,10 +864,10 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
                return NULL;
        }
 
-       lock();
+       lock(ptr);
        if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC)) {
                /* need to get parent before setting free flag. */
-               void *parent = talloc_parent(ptr);
+               void *parent = talloc_parent_nolock(ptr);
                tc->flags |= TALLOC_FLAG_FREE;
                new_ptr = tc_external_realloc(parent, tc, size + TC_HDR_SIZE);
        } else {
@@ -945,7 +963,7 @@ size_t talloc_total_size(const void *ptr)
                return 0;
        }
 
-       lock();
+       lock(ptr);
        total = _talloc_total_size(ptr);
        unlock();
        return total;
@@ -979,26 +997,34 @@ size_t talloc_total_blocks(const void *ptr)
 {
        size_t total;
 
-       lock();
+       lock(ptr);
        total = _talloc_total_blocks(ptr);
        unlock();
 
        return total;
 }
 
-/*
-  return the number of external references to a pointer
-*/
-size_t talloc_reference_count(const void *ptr)
+static size_t _talloc_reference_count(const void *ptr)
 {
        struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
        struct talloc_reference_handle *h;
        size_t ret = 0;
 
-       lock();
        for (h=tc->refs;h;h=h->next) {
                ret++;
        }
+       return ret;
+}
+
+/*
+  return the number of external references to a pointer
+*/
+size_t talloc_reference_count(const void *ptr)
+{
+       size_t ret;
+
+       lock(talloc_chunk_from_ptr(ptr));
+       ret = _talloc_reference_count(ptr);
        unlock();
        return ret;
 }
@@ -1051,7 +1077,7 @@ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
        }
        if (ptr == NULL) return;
 
-       lock();
+       lock(ptr);
        _talloc_report_depth_cb(ptr, depth, max_depth, callback, private_data);
        unlock();
 }
@@ -1069,17 +1095,17 @@ static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_
        if (depth == 0) {
                fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
                        (max_depth < 0 ? "full " :""), name,
-                       (unsigned long)talloc_total_size(ptr),
-                       (unsigned long)talloc_total_blocks(ptr));
+                       (unsigned long)_talloc_total_size(ptr),
+                       (unsigned long)_talloc_total_blocks(ptr));
                return;
        }
 
        fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
                depth*4, "",
                name,
-               (unsigned long)talloc_total_size(ptr),
-               (unsigned long)talloc_total_blocks(ptr),
-               (int)talloc_reference_count(ptr), ptr);
+               (unsigned long)_talloc_total_size(ptr),
+               (unsigned long)_talloc_total_blocks(ptr),
+               (int)_talloc_reference_count(ptr), ptr);
 
 #if 0
        fprintf(f, "content: ");
@@ -1149,11 +1175,9 @@ static void talloc_report_null_full(void)
 */
 void talloc_enable_null_tracking(void)
 {
-       lock();
        if (null_context == NULL) {
                null_context = _talloc_named_const(NULL, 0, "null_context");
        }
-       unlock();
 }
 
 /*
@@ -1161,10 +1185,8 @@ void talloc_enable_null_tracking(void)
 */
 void talloc_disable_null_tracking(void)
 {
-       lock();
        _talloc_free(null_context);
        null_context = NULL;
-       unlock();
 }
 
 /*
@@ -1192,7 +1214,7 @@ void *_talloc_zero(const void *ctx, size_t size, const char *name)
 {
        void *p;
 
-       lock();
+       lock(ctx);
        p = _talloc_named_const(ctx, size, name);
        unlock();
 
@@ -1210,7 +1232,7 @@ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name
 {
        void *newp;
 
-       lock();
+       lock(t);
        newp = _talloc_named_const(t, size, name);
        unlock();
 
@@ -1273,7 +1295,7 @@ char *talloc_strndup(const void *t, const char *p, size_t n)
 
        for (len=0; len<n && p[len]; len++) ;
 
-       lock();
+       lock(t);
        ret = (char *)__talloc(t, len + 1);
        unlock();
        if (!ret) { return NULL; }
@@ -1298,7 +1320,7 @@ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
                return NULL;
        }
 
-       lock();
+       lock(t);
        ret = (char *)__talloc(t, len+1);
        unlock();
        if (ret) {
@@ -1398,7 +1420,7 @@ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char
        if (count >= MAX_TALLOC_SIZE/el_size) {
                return NULL;
        }
-       lock();
+       lock(ctx);
        p = _talloc_named_const(ctx, el_size * count, name);
        unlock();
        return p;
@@ -1414,9 +1436,7 @@ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const
        if (count >= MAX_TALLOC_SIZE/el_size) {
                return NULL;
        }
-       lock();
        p = _talloc_zero(ctx, el_size * count, name);
-       unlock();
        return p;
 }
 
@@ -1494,7 +1514,7 @@ void *talloc_find_parent_byname(const void *context, const char *name)
                return NULL;
        }
 
-       lock();
+       lock(context);
        tc = talloc_chunk_from_ptr(context);
        while (tc) {
                if (tc->name && strcmp(tc->name, name) == 0) {
@@ -1522,7 +1542,7 @@ void talloc_show_parents(const void *context, FILE *file)
                return;
        }
 
-       lock();
+       lock(context);
        tc = talloc_chunk_from_ptr(context);
        fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
        while (tc) {
@@ -1539,19 +1559,20 @@ void talloc_show_parents(const void *context, FILE *file)
 int talloc_is_parent(const void *context, const void *ptr)
 {
        int ret;
-       lock();
+       lock(context);
        ret = _talloc_is_parent(context, ptr);
        unlock();
        return ret;
 }
 
 void *talloc_add_external(const void *ctx,
-                         void *(*realloc)(const void *, void *, size_t))
+                         void *(*realloc)(const void *, void *, size_t),
+                         void (*lock)(const void *p),
+                         void (*unlock)(void))
 {
        struct talloc_chunk *tc, *parent;
        void *p;
 
-       lock();
        if (tc_external_realloc && tc_external_realloc != realloc)
                TALLOC_ABORT("talloc_add_external realloc replaced");
        tc_external_realloc = realloc;
@@ -1564,14 +1585,8 @@ void *talloc_add_external(const void *ctx,
 
        tc = tc_external_realloc(ctx, NULL, TC_HDR_SIZE);
        p = init_talloc(parent, tc, 0, 1);
-       unlock();
-
-       return p;
-}
-
-void _talloc_locksafe(void (*lock)(void *), void (*unlock)(void *), void *data)
-{
        tc_lock = lock;
        tc_unlock = unlock;
-       tc_lock_data = data;
+
+       return p;
 }
index 4efe3b9bc1bf6463217d191b4d0cdff9b380b23f..791c98a2bbd88c6e0e0d5571f6af341f07e007a3 100644 (file)
@@ -917,6 +917,8 @@ void *talloc_find_parent_byname(const void *ctx, const char *name);
  * talloc_add_external - create an externally allocated node
  * @ctx: the parent
  * @realloc: the realloc() equivalent
+ * @lock: the call to lock before manipulation of external nodes
+ * @unlock: the call to unlock after manipulation of external nodes
  *
  * talloc_add_external() creates a node which uses a separate allocator.  All
  * children allocated from that node will also use that allocator.
@@ -924,29 +926,17 @@ void *talloc_find_parent_byname(const void *ctx, const char *name);
  * Note: Currently there is only one external allocator, not per-node,
  * and it is set with this function.
  *
+ * @lock is handed a pointer which was previous returned from your realloc
+ * function; you should use that to figure out which lock to get if you have
+ * multiple external pools.
+ *
  * The parent pointers in realloc is the talloc pointer of the parent, if any.
  */
 void *talloc_add_external(const void *ctx,
                          void *(*realloc)(const void *parent,
-                                          void *ptr, size_t));
-
-/**
- * talloc_locksafe - set locking for talloc on shared memory
- * @lock: function to use to lock memory
- * @unlock: function to use to unlock memory
- * @data: pointer to hand to @lock and @unlock
- *
- * If talloc is actually dealing with shared memory (threads or shared
- * memory using talloc_add_external()) then locking is required on
- * allocation and free to avoid corruption.
- *
- * These hooks allow a very course-grained locking scheme: @lock is
- * called before any internal alloc or free, and @unlock is called
- * after. */
-#define talloc_locksafe(lock, unlock, data)                    \
-       _talloc_locksafe(typesafe_cb(void, lock, data),         \
-                        typesafe_cb(void, unlock, data),       \
-                        data)
+                                          void *ptr, size_t),
+                         void (*lock)(const void *p),
+                         void (*unlock)(void));
 
 /* The following definitions come from talloc.c  */
 void *_talloc(const void *context, size_t size);
@@ -967,6 +957,5 @@ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned
 void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
 void talloc_show_parents(const void *context, FILE *file);
 int talloc_is_parent(const void *context, const void *ptr);
-void _talloc_locksafe(void (*lock)(void *), void (*unlock)(void *), void *);
 
 #endif /* CCAN_TALLOC_H */
index fb1e17e95f1e14301b2d132b34dd0f0672fff67d..70f3dacda5588f0fe6f1fddf9f2893675f5558dc 100644 (file)
@@ -2,7 +2,9 @@
 #include "tap/tap.h"
 #include <assert.h>
 
-static int ext_alloc_count, ext_free_count, ext_realloc_count;
+/* Much testing already done in run.c */
+
+static int ext_alloc_count, ext_free_count, ext_realloc_count, lock_count, unlock_count;
 static void *expected_parent;
 
 static void *ext_realloc(const void *parent, void *ptr, size_t size)
@@ -17,13 +19,23 @@ static void *ext_realloc(const void *parent, void *ptr, size_t size)
        return realloc(ptr, size);
 }
 
+static void ext_lock(const void *ctx)
+{
+       lock_count++;
+}
+
+static void ext_unlock(void)
+{
+       unlock_count++;
+}
+
 int main(void)
 {
        char *p, *p2, *head;
-       plan_tests(12);
+       plan_tests(13);
 
        expected_parent = NULL;
-       head = talloc_add_external(NULL, ext_realloc);
+       head = talloc_add_external(NULL, ext_realloc, ext_lock, ext_unlock);
        assert(head);
        ok1(ext_alloc_count == 1);
 
@@ -50,5 +62,7 @@ int main(void)
        talloc_free(p);
        ok1(ext_free_count == 2);
 
+       ok1(lock_count == unlock_count);
+
        return exit_status();
 }
index 32fe47b554cdc78ca75b11c5f8be8855c9d905eb..ad70a51ec77761059470347acbbdaa4cbf5d3e37 100644 (file)
 #include "tap/tap.h"
 
 #define torture_assert(test, expr, str)                                        \
-       ok(expr, "failure: %s [\n%s: Expression %s failed: %s\n]\n",    \
+       ok(expr, "%s [\n%s: Expression %s failed: %s\n]\n",             \
           test, __location__, #expr, str)
 
 #define torture_assert_str_equal(test, arg1, arg2, desc)       \
        ok(strcmp(arg1, arg2) == 0,                             \
-          "failure: %s [\n%s: Expected %s, got %s: %s\n]\n",   \
+          "%s [\n%s: Expected %s, got %s: %s\n]\n",            \
           test, __location__, arg1, arg2, desc)
 
 #define CHECK_SIZE(test, ptr, tsize)                                   \
        ok(talloc_total_size(ptr) == (tsize),                           \
-          "failed: %s [\nwrong '%s' tree size: got %u  expected %u\n]\n", \
+          "%s [\nwrong '%s' tree size: got %u  expected %u\n]\n",      \
           test, #ptr,                                                  \
           (unsigned)talloc_total_size(ptr),                            \
           (unsigned)tsize)
 
 #define CHECK_BLOCKS(test, ptr, tblocks)                               \
        ok(talloc_total_blocks(ptr) == (tblocks),                       \
-          "failed: %s [\nwrong '%s' tree blocks: got %u  expected %u\n]\n", \
+          "%s [\nwrong '%s' tree blocks: got %u  expected %u\n]\n",    \
           test, #ptr,                                                  \
           (unsigned)talloc_total_blocks(ptr),                          \
           (unsigned)tblocks)
 
 #define CHECK_PARENT(test, ptr, parent)                                        \
        ok(talloc_parent(ptr) == (parent),                              \
-          "failed: %s [\n'%s' has wrong parent: got %p  expected %p\n]\n", \
+          "%s [\n'%s' has wrong parent: got %p  expected %p\n]\n",     \
           test, #ptr,                                                  \
           talloc_parent(ptr),                                          \
           (parent))
 
+struct torture_context;
+
 /*
   test references 
 */
-static bool test_ref1(void)
+static bool test_ref1(const struct torture_context *ctx)
 {
        void *root, *p1, *p2, *ref, *r1;
 
-       root = talloc_named_const(NULL, 0, "root");
+       root = talloc_named_const(ctx, 0, "root");
        p1 = talloc_named_const(root, 1, "p1");
        p2 = talloc_named_const(p1, 1, "p2");
        talloc_named_const(p1, 1, "x1");
@@ -107,11 +109,11 @@ static bool test_ref1(void)
 /*
   test references 
 */
-static bool test_ref2(void)
+static bool test_ref2(const struct torture_context *ctx)
 {
        void *root, *p1, *p2, *ref, *r1;
 
-       root = talloc_named_const(NULL, 0, "root");
+       root = talloc_named_const(ctx, 0, "root");
        p1 = talloc_named_const(root, 1, "p1");
        talloc_named_const(p1, 1, "x1");
        talloc_named_const(p1, 1, "x2");
@@ -151,11 +153,11 @@ static bool test_ref2(void)
 /*
   test references 
 */
-static bool test_ref3(void)
+static bool test_ref3(const struct torture_context *ctx)
 {
        void *root, *p1, *p2, *ref, *r1;
 
-       root = talloc_named_const(NULL, 0, "root");
+       root = talloc_named_const(ctx, 0, "root");
        p1 = talloc_named_const(root, 1, "p1");
        p2 = talloc_named_const(root, 1, "p2");
        r1 = talloc_named_const(p1, 1, "r1");
@@ -182,11 +184,11 @@ static bool test_ref3(void)
 /*
   test references 
 */
-static bool test_ref4(void)
+static bool test_ref4(const struct torture_context *ctx)
 {
        void *root, *p1, *p2, *ref, *r1;
 
-       root = talloc_named_const(NULL, 0, "root");
+       root = talloc_named_const(ctx, 0, "root");
        p1 = talloc_named_const(root, 1, "p1");
        talloc_named_const(p1, 1, "x1");
        talloc_named_const(p1, 1, "x2");
@@ -222,11 +224,11 @@ static bool test_ref4(void)
 /*
   test references 
 */
-static bool test_unlink1(void)
+static bool test_unlink1(const struct torture_context *ctx)
 {
        void *root, *p1, *p2, *ref, *r1;
 
-       root = talloc_named_const(NULL, 0, "root");
+       root = talloc_named_const(ctx, 0, "root");
        p1 = talloc_named_const(root, 1, "p1");
        talloc_named_const(p1, 1, "x1");
        talloc_named_const(p1, 1, "x2");
@@ -263,14 +265,14 @@ static int fail_destructor(void *ptr)
 /*
   miscellaneous tests to try to get a higher test coverage percentage
 */
-static bool test_misc(void)
+static bool test_misc(const struct torture_context *ctx)
 {
        void *root, *p1;
        char *p2;
        double *d;
        const char *name;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        p1 = talloc_size(root, 0x7fffffff);
        torture_assert("misc", !p1, "failed: large talloc allowed\n");
@@ -404,11 +406,11 @@ static bool test_misc(void)
 /*
   test realloc
 */
-static bool test_realloc(void)
+static bool test_realloc(const struct torture_context *ctx)
 {
        void *root, *p1, *p2;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        p1 = talloc_size(root, 10);
        CHECK_SIZE("realloc", p1, 10);
@@ -456,7 +458,7 @@ static bool test_realloc(void)
 /*
   test realloc with a child
 */
-static bool test_realloc_child(void)
+static bool test_realloc_child(const struct torture_context *ctx)
 {
        void *root;
        struct el2 {
@@ -467,7 +469,7 @@ static bool test_realloc_child(void)
                struct el2 **list, **list2, **list3;
        } *el1;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        el1 = talloc(root, struct el1);
        el1->list = talloc(el1, struct el2 *);
@@ -498,7 +500,7 @@ static bool test_realloc_child(void)
 /*
   test type checking
 */
-static bool test_type(void)
+static bool test_type(const struct torture_context *ctx)
 {
        void *root;
        struct el1 {
@@ -509,7 +511,7 @@ static bool test_type(void)
        };
        struct el1 *el1;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        el1 = talloc(root, struct el1);
 
@@ -531,11 +533,11 @@ static bool test_type(void)
 /*
   test steal
 */
-static bool test_steal(void)
+static bool test_steal(const struct torture_context *ctx)
 {
        void *root, *p1, *p2;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        p1 = talloc_array(root, char, 10);
        CHECK_SIZE("steal", p1, 10);
@@ -579,7 +581,7 @@ static bool test_steal(void)
 /*
   test move
 */
-static bool test_move(void)
+static bool test_move(const struct torture_context *ctx)
 {
        void *root;
        struct t_move {
@@ -587,7 +589,7 @@ static bool test_move(void)
                int *x;
        } *t1, *t2;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        t1 = talloc(root, struct t_move);
        t2 = talloc(root, struct t_move);
@@ -609,11 +611,11 @@ static bool test_move(void)
 /*
   test talloc_realloc_fn
 */
-static bool test_realloc_fn(void)
+static bool test_realloc_fn(const struct torture_context *ctx)
 {
        void *root, *p1;
 
-       root = talloc_new(NULL);
+       root = talloc_new(ctx);
 
        p1 = talloc_realloc_fn(root, NULL, 10);
        CHECK_BLOCKS("realloc_fn", root, 2);
@@ -631,11 +633,11 @@ static bool test_realloc_fn(void)
 }
 
 
-static bool test_unref_reparent(void)
+static bool test_unref_reparent(const struct torture_context *ctx)
 {
        void *root, *p1, *p2, *c1;
 
-       root = talloc_named_const(NULL, 0, "root");
+       root = talloc_named_const(ctx, 0, "root");
        p1 = talloc_named_const(root, 1, "orig parent");
        p2 = talloc_named_const(root, 1, "parent by reference");
 
@@ -658,11 +660,11 @@ static bool test_unref_reparent(void)
        return true;
 }
 
-static bool test_lifeless(void)
+static bool test_lifeless(const struct torture_context *ctx)
 {
-       void *top = talloc_new(NULL);
+       void *top = talloc_new(ctx);
        char *parent, *child; 
-       void *child_owner = talloc_new(NULL);
+       void *child_owner = talloc_new(ctx);
 
        parent = talloc_strdup(top, "parent");
        child = talloc_strdup(parent, "child");  
@@ -685,9 +687,9 @@ static int test_loop_destructor(char *ptr)
        return 0;
 }
 
-static bool test_loop(void)
+static bool test_loop(const struct torture_context *ctx)
 {
-       void *top = talloc_new(NULL);
+       void *top = talloc_new(ctx);
        char *parent;
        struct req1 {
                char *req2, *req3;
@@ -714,9 +716,9 @@ static int fail_destructor_str(char *ptr)
        return -1;
 }
 
-static bool test_free_parent_deny_child(void)
+static bool test_free_parent_deny_child(const struct torture_context *ctx)
 {
-       void *top = talloc_new(NULL);
+       void *top = talloc_new(ctx);
        char *level1;
        char *level2;
        char *level3;
@@ -736,9 +738,9 @@ static bool test_free_parent_deny_child(void)
        return true;
 }
 
-static bool test_talloc_ptrtype(void)
+static bool test_talloc_ptrtype(const struct torture_context *ctx)
 {
-       void *top = talloc_new(NULL);
+       void *top = talloc_new(ctx);
        struct struct1 {
                int foo;
                int bar;
@@ -779,13 +781,15 @@ static bool test_talloc_ptrtype(void)
        return true;
 }
 
+static bool test_talloc_free_in_destructor_run;
 static int _test_talloc_free_in_destructor(void **ptr)
 {
        talloc_free(*ptr);
+       test_talloc_free_in_destructor_run = true;
        return 0;
 }
 
-static bool test_talloc_free_in_destructor(void)
+static bool test_talloc_free_in_destructor(const struct torture_context *ctx)
 {
        void *level0;
        void *level1;
@@ -794,28 +798,32 @@ static bool test_talloc_free_in_destructor(void)
        void *level4;
        void **level5;
 
-       level0 = talloc_new(NULL);
+       /* FIXME: Can't do nested destruction with locking, sorry. */
+       if (ctx)
+               return true;
+
+       level0 = talloc_new(ctx);
        level1 = talloc_new(level0);
        level2 = talloc_new(level1);
        level3 = talloc_new(level2);
        level4 = talloc_new(level3);
        level5 = talloc(level4, void *);
 
-       *level5 = level3;
-       (void)talloc_reference(level0, level3);
-       (void)talloc_reference(level3, level3);
-       (void)talloc_reference(level5, level3);
+       *level5 = talloc_reference(NULL, level3);
 
+       test_talloc_free_in_destructor_run = false;
        talloc_set_destructor(level5, _test_talloc_free_in_destructor);
 
        talloc_free(level1);
 
        talloc_free(level0);
 
+       ok1(test_talloc_free_in_destructor_run);
+
        return true;
 }
 
-static bool test_autofree(void)
+static bool test_autofree(const struct torture_context *ctx)
 {
        /* autofree test would kill smbtorture */
        void *p;
@@ -828,8 +836,7 @@ static bool test_autofree(void)
        return true;
 }
 
-struct torture_context;
-static bool torture_local_talloc(struct torture_context *tctx)
+static bool torture_local_talloc(const struct torture_context *tctx)
 {
        bool ret = true;
 
@@ -838,54 +845,140 @@ static bool torture_local_talloc(struct torture_context *tctx)
        talloc_disable_null_tracking();
        talloc_enable_null_tracking();
 
-       ret &= test_ref1();
-       ret &= test_ref2();
-       ret &= test_ref3();
-       ret &= test_ref4();
-       ret &= test_unlink1(); 
-       ret &= test_misc();
-       ret &= test_realloc();
-       ret &= test_realloc_child(); 
-       ret &= test_steal(); 
-       ret &= test_move(); 
-       ret &= test_unref_reparent();
-       ret &= test_realloc_fn(); 
-       ret &= test_type();
-       ret &= test_lifeless(); 
-       ret &= test_loop();
-       ret &= test_free_parent_deny_child(); 
-       ret &= test_talloc_ptrtype();
-       ret &= test_talloc_free_in_destructor();
-       ret &= test_autofree();
+       ret &= test_ref1(tctx);
+       ret &= test_ref2(tctx);
+       ret &= test_ref3(tctx);
+       ret &= test_ref4(tctx);
+       ret &= test_unlink1(tctx); 
+       ret &= test_misc(tctx);
+       ret &= test_realloc(tctx);
+       ret &= test_realloc_child(tctx); 
+       ret &= test_steal(tctx);
+       ret &= test_move(tctx);
+       ret &= test_unref_reparent(tctx);
+       ret &= test_realloc_fn(tctx);
+       ret &= test_type(tctx);
+       ret &= test_lifeless(tctx);
+       ret &= test_loop(tctx);
+       ret &= test_free_parent_deny_child(tctx);
+       ret &= test_talloc_ptrtype(tctx);
+       ret &= test_talloc_free_in_destructor(tctx);
+       ret &= test_autofree(tctx);
 
        return ret;
 }
 
-static int lock_failed = 0, unlock_failed = 0;
-static void test_lock(int *locked)
+static int lock_failed = 0, unlock_failed = 0, lock_bad = 0;
+static int locked;
+
+#define MAX_ALLOCATIONS 100
+static void *allocations[MAX_ALLOCATIONS];
+static int num_allocs, num_frees, num_reallocs;
+
+static unsigned int find_ptr(const void *p)
 {
-       if (*locked)
+       unsigned int i;
+
+       for (i = 0; i < MAX_ALLOCATIONS; i++)
+               if (allocations[i] == p)
+                       break;
+       return i;
+}
+
+static unsigned int allocations_used(void)
+{
+       unsigned int i, ret = 0;
+
+       for (i = 0; i < MAX_ALLOCATIONS; i++)
+               if (allocations[i])
+                       ret++;
+       return ret;
+}
+
+static void test_lock(const void *ctx)
+{
+       if (find_ptr(ctx) == MAX_ALLOCATIONS)
+               lock_bad++;
+
+       if (locked)
                lock_failed++;
-       *locked = 1;
+       locked = 1;
 }
 
-static void test_unlock(int *locked)
+static void test_unlock(void)
 {
-       if (!*locked)
+       if (!locked)
                unlock_failed++;
-       *locked = 0;
+       locked = 0;
+}
+
+static int realloc_called, realloc_bad;
+
+static void *normal_realloc(const void *parent, void *ptr, size_t size)
+{
+       unsigned int i = find_ptr(ptr);
+
+       realloc_called++;
+       if (ptr && size)
+               num_reallocs++;
+       else if (ptr)
+               num_frees++;
+       else if (size)
+               num_allocs++;
+       else
+               abort();
+
+       if (i == MAX_ALLOCATIONS) {
+               if (ptr) {
+                       realloc_bad++;
+                       i = find_ptr(NULL);
+               } else
+                       abort();
+       }
+
+       allocations[i] = realloc(ptr, size);
+       /* Not guarenteed by realloc. */
+       if (!size)
+               allocations[i] = NULL;
+
+       return allocations[i];
 }
 
 int main(void)
 {
-       int locked = 0;
+       struct torture_context *ctx;
 
-       plan_tests(136);
-       talloc_locksafe(test_lock, test_unlock, &locked);
+       plan_tests(284);
+       ctx = talloc_add_external(NULL, normal_realloc, test_lock, test_unlock);
 
        torture_local_talloc(NULL);
+       ok(!lock_bad, "%u locks on bad pointer", lock_bad);
+       ok(!lock_failed, "lock_failed count %u should be zero", lock_failed);
+       ok(!unlock_failed, "unlock_failed count %u should be zero",
+          unlock_failed);
+       ok(realloc_called == 1, "our realloc should not be called again");
+
+       torture_local_talloc(ctx);
+       ok(!lock_bad, "%u locks on bad pointer", lock_bad);
        ok(!lock_failed, "lock_failed count %u should be zero", lock_failed);
-       ok(!unlock_failed, "unlock_failed count %u should be zero", unlock_failed);
+       ok(!unlock_failed, "unlock_failed count %u should be zero",
+          unlock_failed);
+       ok(realloc_called, "our realloc should be called");
+       ok(!realloc_bad, "our realloc given unknown pointer %u times",
+          realloc_bad);
+
+       talloc_free(ctx);
+       ok(!lock_bad, "%u locks on bad pointer", lock_bad);
+       ok(!lock_failed, "lock_failed count %u should be zero", lock_failed);
+       ok(!unlock_failed, "unlock_failed count %u should be zero",
+          unlock_failed);
+       ok(realloc_called, "our realloc should be called");
+       ok(!realloc_bad, "our realloc given unknown pointer %u times",
+          realloc_bad);
+
+       ok(allocations_used() == 0, "%u allocations still used?",
+          allocations_used());
+
        return exit_status();
 }