Browse Source

Seriously revisit locking required for antithread.
Remove test that never ran properly anyway (still bug in current talloc
testsuite).

Rusty Russell 17 years ago
parent
commit
f933b8c324
4 changed files with 273 additions and 162 deletions
  1. 75 60
      ccan/talloc/talloc.c
  2. 9 20
      ccan/talloc/talloc.h
  3. 17 3
      ccan/talloc/test/run-external-alloc.c
  4. 172 79
      ccan/talloc/test/run.c

+ 75 - 60
ccan/talloc/talloc.c

@@ -83,9 +83,8 @@ static void *null_context;
 static pid_t *autofree_context;
 static pid_t *autofree_context;
 
 
 static void *(*tc_external_realloc)(const void *parent, void *ptr, size_t size);
 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 {
 	struct talloc_reference_handle *next, *prev;
 	struct talloc_reference_handle *next, *prev;
@@ -150,16 +149,27 @@ do { \
 	if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
 	if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
 } while (0)
 } 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)
 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;
 	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;
 	struct talloc_chunk *tc;
 
 
-	lock();
 	tc = talloc_parent_chunk(ptr);
 	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();
 	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;
 	struct talloc_chunk *tc;
 
 
-	lock();
+	lock(ptr);
 	tc = talloc_parent_chunk(ptr);
 	tc = talloc_parent_chunk(ptr);
 	unlock();
 	unlock();
 
 
@@ -346,7 +365,7 @@ void *_talloc_reference(const void *context, const void *ptr)
 	struct talloc_reference_handle *handle;
 	struct talloc_reference_handle *handle;
 	if (unlikely(ptr == NULL)) return NULL;
 	if (unlikely(ptr == NULL)) return NULL;
 
 
-	lock();
+	lock(context);
 	tc = talloc_chunk_from_ptr(ptr);
 	tc = talloc_chunk_from_ptr(ptr);
 	handle = (struct talloc_reference_handle *)_talloc_named_const(context,
 	handle = (struct talloc_reference_handle *)_talloc_named_const(context,
 						   sizeof(struct talloc_reference_handle),
 						   sizeof(struct talloc_reference_handle),
@@ -497,7 +516,7 @@ static inline int _talloc_free(void *ptr)
 	}
 	}
 
 
 	if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC))
 	if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC))
-		oldparent = talloc_parent(ptr);
+		oldparent = talloc_parent_nolock(ptr);
 
 
 	if (tc->parent) {
 	if (tc->parent) {
 		_TLIST_REMOVE(tc->parent->child, tc);
 		_TLIST_REMOVE(tc->parent->child, tc);
@@ -546,7 +565,7 @@ void *_talloc_steal(const void *new_ctx, const void *ptr)
 {
 {
 	void *p;
 	void *p;
 
 
-	lock();
+	lock(new_ctx);
 	p = __talloc_steal(new_ctx, ptr);
 	p = __talloc_steal(new_ctx, ptr);
 	unlock();
 	unlock();
 	return p;
 	return p;
@@ -598,7 +617,7 @@ int talloc_unlink(const void *context, void *ptr)
 		context = null_context;
 		context = null_context;
 	}
 	}
 
 
-	lock();
+	lock(context);
 	if (talloc_unreference(context, ptr) == 0) {
 	if (talloc_unreference(context, ptr) == 0) {
 		unlock();
 		unlock();
 		return 0;
 		return 0;
@@ -682,7 +701,7 @@ void *talloc_named(const void *context, size_t size, const char *fmt, ...)
 	void *ptr;
 	void *ptr;
 	const char *name;
 	const char *name;
 
 
-	lock();
+	lock(context);
 	ptr = __talloc(context, size);
 	ptr = __talloc(context, size);
 	unlock();
 	unlock();
 	if (unlikely(ptr == NULL)) return NULL;
 	if (unlikely(ptr == NULL)) return NULL;
@@ -747,9 +766,7 @@ void *talloc_init(const char *fmt, ...)
 	 */
 	 */
 	talloc_enable_null_tracking();
 	talloc_enable_null_tracking();
 
 
-	lock();
 	ptr = __talloc(NULL, 0);
 	ptr = __talloc(NULL, 0);
-	unlock();
 	if (unlikely(ptr == NULL)) return NULL;
 	if (unlikely(ptr == NULL)) return NULL;
 
 
 	va_start(ap, fmt);
 	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 *talloc_named_const(const void *context, size_t size, const char *name)
 {
 {
 	void *p;
 	void *p;
-	lock();
+	lock(context);
 	p = _talloc_named_const(context, size, name);
 	p = _talloc_named_const(context, size, name);
 	unlock();
 	unlock();
 	return p;
 	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 talloc_free(void *ptr)
 {
 {
 	int saved_errno = errno, ret;
 	int saved_errno = errno, ret;
-	lock();
+
+	lock(ptr);
 	ret = _talloc_free(ptr);
 	ret = _talloc_free(ptr);
 	unlock();
 	unlock();
 	if (ret == 0)
 	if (ret == 0)
@@ -846,10 +864,10 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	lock();
+	lock(ptr);
 	if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC)) {
 	if (unlikely(tc->flags & TALLOC_FLAG_EXT_ALLOC)) {
 		/* need to get parent before setting free flag. */
 		/* need to get parent before setting free flag. */
-		void *parent = talloc_parent(ptr);
+		void *parent = talloc_parent_nolock(ptr);
 		tc->flags |= TALLOC_FLAG_FREE;
 		tc->flags |= TALLOC_FLAG_FREE;
 		new_ptr = tc_external_realloc(parent, tc, size + TC_HDR_SIZE);
 		new_ptr = tc_external_realloc(parent, tc, size + TC_HDR_SIZE);
 	} else {
 	} else {
@@ -945,7 +963,7 @@ size_t talloc_total_size(const void *ptr)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	lock();
+	lock(ptr);
 	total = _talloc_total_size(ptr);
 	total = _talloc_total_size(ptr);
 	unlock();
 	unlock();
 	return total;
 	return total;
@@ -979,26 +997,34 @@ size_t talloc_total_blocks(const void *ptr)
 {
 {
 	size_t total;
 	size_t total;
 
 
-	lock();
+	lock(ptr);
 	total = _talloc_total_blocks(ptr);
 	total = _talloc_total_blocks(ptr);
 	unlock();
 	unlock();
 
 
 	return total;
 	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_chunk *tc = talloc_chunk_from_ptr(ptr);
 	struct talloc_reference_handle *h;
 	struct talloc_reference_handle *h;
 	size_t ret = 0;
 	size_t ret = 0;
 
 
-	lock();
 	for (h=tc->refs;h;h=h->next) {
 	for (h=tc->refs;h;h=h->next) {
 		ret++;
 		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();
 	unlock();
 	return ret;
 	return ret;
 }
 }
@@ -1051,7 +1077,7 @@ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
 	}
 	}
 	if (ptr == NULL) return;
 	if (ptr == NULL) return;
 
 
-	lock();
+	lock(ptr);
 	_talloc_report_depth_cb(ptr, depth, max_depth, callback, private_data);
 	_talloc_report_depth_cb(ptr, depth, max_depth, callback, private_data);
 	unlock();
 	unlock();
 }
 }
@@ -1069,17 +1095,17 @@ static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_
 	if (depth == 0) {
 	if (depth == 0) {
 		fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
 		fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
 			(max_depth < 0 ? "full " :""), name,
 			(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;
 		return;
 	}
 	}
 
 
 	fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
 	fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
 		depth*4, "",
 		depth*4, "",
 		name,
 		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
 #if 0
 	fprintf(f, "content: ");
 	fprintf(f, "content: ");
@@ -1149,11 +1175,9 @@ static void talloc_report_null_full(void)
 */
 */
 void talloc_enable_null_tracking(void)
 void talloc_enable_null_tracking(void)
 {
 {
-	lock();
 	if (null_context == NULL) {
 	if (null_context == NULL) {
 		null_context = _talloc_named_const(NULL, 0, "null_context");
 		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)
 void talloc_disable_null_tracking(void)
 {
 {
-	lock();
 	_talloc_free(null_context);
 	_talloc_free(null_context);
 	null_context = NULL;
 	null_context = NULL;
-	unlock();
 }
 }
 
 
 /*
 /*
@@ -1192,7 +1214,7 @@ void *_talloc_zero(const void *ctx, size_t size, const char *name)
 {
 {
 	void *p;
 	void *p;
 
 
-	lock();
+	lock(ctx);
 	p = _talloc_named_const(ctx, size, name);
 	p = _talloc_named_const(ctx, size, name);
 	unlock();
 	unlock();
 
 
@@ -1210,7 +1232,7 @@ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name
 {
 {
 	void *newp;
 	void *newp;
 
 
-	lock();
+	lock(t);
 	newp = _talloc_named_const(t, size, name);
 	newp = _talloc_named_const(t, size, name);
 	unlock();
 	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++) ;
 	for (len=0; len<n && p[len]; len++) ;
 
 
-	lock();
+	lock(t);
 	ret = (char *)__talloc(t, len + 1);
 	ret = (char *)__talloc(t, len + 1);
 	unlock();
 	unlock();
 	if (!ret) { return NULL; }
 	if (!ret) { return NULL; }
@@ -1298,7 +1320,7 @@ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	lock();
+	lock(t);
 	ret = (char *)__talloc(t, len+1);
 	ret = (char *)__talloc(t, len+1);
 	unlock();
 	unlock();
 	if (ret) {
 	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) {
 	if (count >= MAX_TALLOC_SIZE/el_size) {
 		return NULL;
 		return NULL;
 	}
 	}
-	lock();
+	lock(ctx);
 	p = _talloc_named_const(ctx, el_size * count, name);
 	p = _talloc_named_const(ctx, el_size * count, name);
 	unlock();
 	unlock();
 	return p;
 	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) {
 	if (count >= MAX_TALLOC_SIZE/el_size) {
 		return NULL;
 		return NULL;
 	}
 	}
-	lock();
 	p = _talloc_zero(ctx, el_size * count, name);
 	p = _talloc_zero(ctx, el_size * count, name);
-	unlock();
 	return p;
 	return p;
 }
 }
 
 
@@ -1494,7 +1514,7 @@ void *talloc_find_parent_byname(const void *context, const char *name)
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	lock();
+	lock(context);
 	tc = talloc_chunk_from_ptr(context);
 	tc = talloc_chunk_from_ptr(context);
 	while (tc) {
 	while (tc) {
 		if (tc->name && strcmp(tc->name, name) == 0) {
 		if (tc->name && strcmp(tc->name, name) == 0) {
@@ -1522,7 +1542,7 @@ void talloc_show_parents(const void *context, FILE *file)
 		return;
 		return;
 	}
 	}
 
 
-	lock();
+	lock(context);
 	tc = talloc_chunk_from_ptr(context);
 	tc = talloc_chunk_from_ptr(context);
 	fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
 	fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
 	while (tc) {
 	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 talloc_is_parent(const void *context, const void *ptr)
 {
 {
 	int ret;
 	int ret;
-	lock();
+	lock(context);
 	ret = _talloc_is_parent(context, ptr);
 	ret = _talloc_is_parent(context, ptr);
 	unlock();
 	unlock();
 	return ret;
 	return ret;
 }
 }
 
 
 void *talloc_add_external(const void *ctx,
 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;
 	struct talloc_chunk *tc, *parent;
 	void *p;
 	void *p;
 
 
-	lock();
 	if (tc_external_realloc && tc_external_realloc != realloc)
 	if (tc_external_realloc && tc_external_realloc != realloc)
 		TALLOC_ABORT("talloc_add_external realloc replaced");
 		TALLOC_ABORT("talloc_add_external realloc replaced");
 	tc_external_realloc = realloc;
 	tc_external_realloc = realloc;
@@ -1564,14 +1585,8 @@ void *talloc_add_external(const void *ctx,
 
 
 	tc = tc_external_realloc(ctx, NULL, TC_HDR_SIZE);
 	tc = tc_external_realloc(ctx, NULL, TC_HDR_SIZE);
 	p = init_talloc(parent, tc, 0, 1);
 	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_lock = lock;
 	tc_unlock = unlock;
 	tc_unlock = unlock;
-	tc_lock_data = data;
+
+	return p;
 }
 }

+ 9 - 20
ccan/talloc/talloc.h

@@ -917,6 +917,8 @@ void *talloc_find_parent_byname(const void *ctx, const char *name);
  * talloc_add_external - create an externally allocated node
  * talloc_add_external - create an externally allocated node
  * @ctx: the parent
  * @ctx: the parent
  * @realloc: the realloc() equivalent
  * @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
  * talloc_add_external() creates a node which uses a separate allocator.  All
  * children allocated from that node will also use that allocator.
  * 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,
  * Note: Currently there is only one external allocator, not per-node,
  * and it is set with this function.
  * 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.
  * The parent pointers in realloc is the talloc pointer of the parent, if any.
  */
  */
 void *talloc_add_external(const void *ctx,
 void *talloc_add_external(const void *ctx,
 			  void *(*realloc)(const void *parent,
 			  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  */
 /* The following definitions come from talloc.c  */
 void *_talloc(const void *context, size_t size);
 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_realloc_fn(const void *context, void *ptr, size_t size);
 void talloc_show_parents(const void *context, FILE *file);
 void talloc_show_parents(const void *context, FILE *file);
 int talloc_is_parent(const void *context, const void *ptr);
 int talloc_is_parent(const void *context, const void *ptr);
-void _talloc_locksafe(void (*lock)(void *), void (*unlock)(void *), void *);
 
 
 #endif /* CCAN_TALLOC_H */
 #endif /* CCAN_TALLOC_H */

+ 17 - 3
ccan/talloc/test/run-external-alloc.c

@@ -2,7 +2,9 @@
 #include "tap/tap.h"
 #include "tap/tap.h"
 #include <assert.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 *expected_parent;
 
 
 static void *ext_realloc(const void *parent, void *ptr, size_t size)
 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);
 	return realloc(ptr, size);
 }
 }
 
 
+static void ext_lock(const void *ctx)
+{
+	lock_count++;
+}
+
+static void ext_unlock(void)
+{
+	unlock_count++;
+}
+
 int main(void)
 int main(void)
 {
 {
 	char *p, *p2, *head;
 	char *p, *p2, *head;
-	plan_tests(12);
+	plan_tests(13);
 
 
 	expected_parent = NULL;
 	expected_parent = NULL;
-	head = talloc_add_external(NULL, ext_realloc);
+	head = talloc_add_external(NULL, ext_realloc, ext_lock, ext_unlock);
 	assert(head);
 	assert(head);
 	ok1(ext_alloc_count == 1);
 	ok1(ext_alloc_count == 1);
 
 
@@ -50,5 +62,7 @@ int main(void)
 	talloc_free(p);
 	talloc_free(p);
 	ok1(ext_free_count == 2);
 	ok1(ext_free_count == 2);
 
 
+	ok1(lock_count == unlock_count);
+
 	return exit_status();
 	return exit_status();
 }
 }

+ 172 - 79
ccan/talloc/test/run.c

@@ -30,43 +30,45 @@
 #include "tap/tap.h"
 #include "tap/tap.h"
 
 
 #define torture_assert(test, expr, str)					\
 #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)
 	   test, __location__, #expr, str)
 
 
 #define torture_assert_str_equal(test, arg1, arg2, desc)	\
 #define torture_assert_str_equal(test, arg1, arg2, desc)	\
 	ok(strcmp(arg1, arg2) == 0,				\
 	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)
 	   test, __location__, arg1, arg2, desc)
 
 
 #define CHECK_SIZE(test, ptr, tsize)					\
 #define CHECK_SIZE(test, ptr, tsize)					\
 	ok(talloc_total_size(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,							\
 	   test, #ptr,							\
 	   (unsigned)talloc_total_size(ptr),				\
 	   (unsigned)talloc_total_size(ptr),				\
 	   (unsigned)tsize)
 	   (unsigned)tsize)
 
 
 #define CHECK_BLOCKS(test, ptr, tblocks)				\
 #define CHECK_BLOCKS(test, ptr, tblocks)				\
 	ok(talloc_total_blocks(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,							\
 	   test, #ptr,							\
 	   (unsigned)talloc_total_blocks(ptr),				\
 	   (unsigned)talloc_total_blocks(ptr),				\
 	   (unsigned)tblocks)
 	   (unsigned)tblocks)
 
 
 #define CHECK_PARENT(test, ptr, parent)					\
 #define CHECK_PARENT(test, ptr, parent)					\
 	ok(talloc_parent(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,							\
 	   test, #ptr,							\
 	   talloc_parent(ptr),						\
 	   talloc_parent(ptr),						\
 	   (parent))
 	   (parent))
 
 
+struct torture_context;
+
 /*
 /*
   test references 
   test references 
 */
 */
-static bool test_ref1(void)
+static bool test_ref1(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2, *ref, *r1;
 	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");
 	p1 = talloc_named_const(root, 1, "p1");
 	p2 = talloc_named_const(p1, 1, "p2");
 	p2 = talloc_named_const(p1, 1, "p2");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x1");
@@ -107,11 +109,11 @@ static bool test_ref1(void)
 /*
 /*
   test references 
   test references 
 */
 */
-static bool test_ref2(void)
+static bool test_ref2(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2, *ref, *r1;
 	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");
 	p1 = talloc_named_const(root, 1, "p1");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x2");
 	talloc_named_const(p1, 1, "x2");
@@ -151,11 +153,11 @@ static bool test_ref2(void)
 /*
 /*
   test references 
   test references 
 */
 */
-static bool test_ref3(void)
+static bool test_ref3(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2, *ref, *r1;
 	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");
 	p1 = talloc_named_const(root, 1, "p1");
 	p2 = talloc_named_const(root, 1, "p2");
 	p2 = talloc_named_const(root, 1, "p2");
 	r1 = talloc_named_const(p1, 1, "r1");
 	r1 = talloc_named_const(p1, 1, "r1");
@@ -182,11 +184,11 @@ static bool test_ref3(void)
 /*
 /*
   test references 
   test references 
 */
 */
-static bool test_ref4(void)
+static bool test_ref4(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2, *ref, *r1;
 	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");
 	p1 = talloc_named_const(root, 1, "p1");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x2");
 	talloc_named_const(p1, 1, "x2");
@@ -222,11 +224,11 @@ static bool test_ref4(void)
 /*
 /*
   test references 
   test references 
 */
 */
-static bool test_unlink1(void)
+static bool test_unlink1(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2, *ref, *r1;
 	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");
 	p1 = talloc_named_const(root, 1, "p1");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x1");
 	talloc_named_const(p1, 1, "x2");
 	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
   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;
 	void *root, *p1;
 	char *p2;
 	char *p2;
 	double *d;
 	double *d;
 	const char *name;
 	const char *name;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	p1 = talloc_size(root, 0x7fffffff);
 	p1 = talloc_size(root, 0x7fffffff);
 	torture_assert("misc", !p1, "failed: large talloc allowed\n");
 	torture_assert("misc", !p1, "failed: large talloc allowed\n");
@@ -404,11 +406,11 @@ static bool test_misc(void)
 /*
 /*
   test realloc
   test realloc
 */
 */
-static bool test_realloc(void)
+static bool test_realloc(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2;
 	void *root, *p1, *p2;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	p1 = talloc_size(root, 10);
 	p1 = talloc_size(root, 10);
 	CHECK_SIZE("realloc", p1, 10);
 	CHECK_SIZE("realloc", p1, 10);
@@ -456,7 +458,7 @@ static bool test_realloc(void)
 /*
 /*
   test realloc with a child
   test realloc with a child
 */
 */
-static bool test_realloc_child(void)
+static bool test_realloc_child(const struct torture_context *ctx)
 {
 {
 	void *root;
 	void *root;
 	struct el2 {
 	struct el2 {
@@ -467,7 +469,7 @@ static bool test_realloc_child(void)
 		struct el2 **list, **list2, **list3;
 		struct el2 **list, **list2, **list3;
 	} *el1;
 	} *el1;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	el1 = talloc(root, struct el1);
 	el1 = talloc(root, struct el1);
 	el1->list = talloc(el1, struct el2 *);
 	el1->list = talloc(el1, struct el2 *);
@@ -498,7 +500,7 @@ static bool test_realloc_child(void)
 /*
 /*
   test type checking
   test type checking
 */
 */
-static bool test_type(void)
+static bool test_type(const struct torture_context *ctx)
 {
 {
 	void *root;
 	void *root;
 	struct el1 {
 	struct el1 {
@@ -509,7 +511,7 @@ static bool test_type(void)
 	};
 	};
 	struct el1 *el1;
 	struct el1 *el1;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	el1 = talloc(root, struct el1);
 	el1 = talloc(root, struct el1);
 
 
@@ -531,11 +533,11 @@ static bool test_type(void)
 /*
 /*
   test steal
   test steal
 */
 */
-static bool test_steal(void)
+static bool test_steal(const struct torture_context *ctx)
 {
 {
 	void *root, *p1, *p2;
 	void *root, *p1, *p2;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	p1 = talloc_array(root, char, 10);
 	p1 = talloc_array(root, char, 10);
 	CHECK_SIZE("steal", p1, 10);
 	CHECK_SIZE("steal", p1, 10);
@@ -579,7 +581,7 @@ static bool test_steal(void)
 /*
 /*
   test move
   test move
 */
 */
-static bool test_move(void)
+static bool test_move(const struct torture_context *ctx)
 {
 {
 	void *root;
 	void *root;
 	struct t_move {
 	struct t_move {
@@ -587,7 +589,7 @@ static bool test_move(void)
 		int *x;
 		int *x;
 	} *t1, *t2;
 	} *t1, *t2;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	t1 = talloc(root, struct t_move);
 	t1 = talloc(root, struct t_move);
 	t2 = talloc(root, struct t_move);
 	t2 = talloc(root, struct t_move);
@@ -609,11 +611,11 @@ static bool test_move(void)
 /*
 /*
   test talloc_realloc_fn
   test talloc_realloc_fn
 */
 */
-static bool test_realloc_fn(void)
+static bool test_realloc_fn(const struct torture_context *ctx)
 {
 {
 	void *root, *p1;
 	void *root, *p1;
 
 
-	root = talloc_new(NULL);
+	root = talloc_new(ctx);
 
 
 	p1 = talloc_realloc_fn(root, NULL, 10);
 	p1 = talloc_realloc_fn(root, NULL, 10);
 	CHECK_BLOCKS("realloc_fn", root, 2);
 	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;
 	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");
 	p1 = talloc_named_const(root, 1, "orig parent");
 	p2 = talloc_named_const(root, 1, "parent by reference");
 	p2 = talloc_named_const(root, 1, "parent by reference");
 
 
@@ -658,11 +660,11 @@ static bool test_unref_reparent(void)
 	return true;
 	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; 
 	char *parent, *child; 
-	void *child_owner = talloc_new(NULL);
+	void *child_owner = talloc_new(ctx);
 
 
 	parent = talloc_strdup(top, "parent");
 	parent = talloc_strdup(top, "parent");
 	child = talloc_strdup(parent, "child");  
 	child = talloc_strdup(parent, "child");  
@@ -685,9 +687,9 @@ static int test_loop_destructor(char *ptr)
 	return 0;
 	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;
 	char *parent;
 	struct req1 {
 	struct req1 {
 		char *req2, *req3;
 		char *req2, *req3;
@@ -714,9 +716,9 @@ static int fail_destructor_str(char *ptr)
 	return -1;
 	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 *level1;
 	char *level2;
 	char *level2;
 	char *level3;
 	char *level3;
@@ -736,9 +738,9 @@ static bool test_free_parent_deny_child(void)
 	return true;
 	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 {
 	struct struct1 {
 		int foo;
 		int foo;
 		int bar;
 		int bar;
@@ -779,13 +781,15 @@ static bool test_talloc_ptrtype(void)
 	return true;
 	return true;
 }
 }
 
 
+static bool test_talloc_free_in_destructor_run;
 static int _test_talloc_free_in_destructor(void **ptr)
 static int _test_talloc_free_in_destructor(void **ptr)
 {
 {
 	talloc_free(*ptr);
 	talloc_free(*ptr);
+	test_talloc_free_in_destructor_run = true;
 	return 0;
 	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 *level0;
 	void *level1;
 	void *level1;
@@ -794,28 +798,32 @@ static bool test_talloc_free_in_destructor(void)
 	void *level4;
 	void *level4;
 	void **level5;
 	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);
 	level1 = talloc_new(level0);
 	level2 = talloc_new(level1);
 	level2 = talloc_new(level1);
 	level3 = talloc_new(level2);
 	level3 = talloc_new(level2);
 	level4 = talloc_new(level3);
 	level4 = talloc_new(level3);
 	level5 = talloc(level4, void *);
 	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_set_destructor(level5, _test_talloc_free_in_destructor);
 
 
 	talloc_free(level1);
 	talloc_free(level1);
 
 
 	talloc_free(level0);
 	talloc_free(level0);
 
 
+	ok1(test_talloc_free_in_destructor_run);
+
 	return true;
 	return true;
 }
 }
 
 
-static bool test_autofree(void)
+static bool test_autofree(const struct torture_context *ctx)
 {
 {
 	/* autofree test would kill smbtorture */
 	/* autofree test would kill smbtorture */
 	void *p;
 	void *p;
@@ -828,8 +836,7 @@ static bool test_autofree(void)
 	return true;
 	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;
 	bool ret = true;
 
 
@@ -838,54 +845,140 @@ static bool torture_local_talloc(struct torture_context *tctx)
 	talloc_disable_null_tracking();
 	talloc_disable_null_tracking();
 	talloc_enable_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;
 	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++;
 		lock_failed++;
-	*locked = 1;
+	locked = 1;
 }
 }
 
 
-static void test_unlock(int *locked)
+static void test_unlock(void)
 {
 {
-	if (!*locked)
+	if (!locked)
 		unlock_failed++;
 		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 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);
 	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(!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();
 	return exit_status();
 }
 }