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;
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;
+ }
}
/*
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;
}
/*
{
struct talloc_chunk *tc;
- lock();
+ lock(ptr);
tc = talloc_parent_chunk(ptr);
unlock();
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),
}
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);
{
void *p;
- lock();
+ lock(new_ctx);
p = __talloc_steal(new_ctx, ptr);
unlock();
return p;
context = null_context;
}
- lock();
+ lock(context);
if (talloc_unreference(context, ptr) == 0) {
unlock();
return 0;
void *ptr;
const char *name;
- lock();
+ lock(context);
ptr = __talloc(context, size);
unlock();
if (unlikely(ptr == NULL)) return NULL;
*/
talloc_enable_null_tracking();
- lock();
ptr = __talloc(NULL, 0);
- unlock();
if (unlikely(ptr == NULL)) return NULL;
va_start(ap, fmt);
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;
int talloc_free(void *ptr)
{
int saved_errno = errno, ret;
- lock();
+
+ lock(ptr);
ret = _talloc_free(ptr);
unlock();
if (ret == 0)
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 {
return 0;
}
- lock();
+ lock(ptr);
total = _talloc_total_size(ptr);
unlock();
return total;
{
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;
}
}
if (ptr == NULL) return;
- lock();
+ lock(ptr);
_talloc_report_depth_cb(ptr, depth, max_depth, callback, private_data);
unlock();
}
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: ");
*/
void talloc_enable_null_tracking(void)
{
- lock();
if (null_context == NULL) {
null_context = _talloc_named_const(NULL, 0, "null_context");
}
- unlock();
}
/*
*/
void talloc_disable_null_tracking(void)
{
- lock();
_talloc_free(null_context);
null_context = NULL;
- unlock();
}
/*
{
void *p;
- lock();
+ lock(ctx);
p = _talloc_named_const(ctx, size, name);
unlock();
{
void *newp;
- lock();
+ lock(t);
newp = _talloc_named_const(t, size, name);
unlock();
for (len=0; len<n && p[len]; len++) ;
- lock();
+ lock(t);
ret = (char *)__talloc(t, len + 1);
unlock();
if (!ret) { return NULL; }
return NULL;
}
- lock();
+ lock(t);
ret = (char *)__talloc(t, len+1);
unlock();
if (ret) {
if (count >= MAX_TALLOC_SIZE/el_size) {
return NULL;
}
- lock();
+ lock(ctx);
p = _talloc_named_const(ctx, el_size * count, name);
unlock();
return p;
if (count >= MAX_TALLOC_SIZE/el_size) {
return NULL;
}
- lock();
p = _talloc_zero(ctx, el_size * count, name);
- unlock();
return p;
}
return NULL;
}
- lock();
+ lock(context);
tc = talloc_chunk_from_ptr(context);
while (tc) {
if (tc->name && strcmp(tc->name, name) == 0) {
return;
}
- lock();
+ lock(context);
tc = talloc_chunk_from_ptr(context);
fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
while (tc) {
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;
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;
}
#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");
/*
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");
/*
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");
/*
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");
/*
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");
/*
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");
/*
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);
/*
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 {
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 *);
/*
test type checking
*/
-static bool test_type(void)
+static bool test_type(const struct torture_context *ctx)
{
void *root;
struct el1 {
};
struct el1 *el1;
- root = talloc_new(NULL);
+ root = talloc_new(ctx);
el1 = talloc(root, struct el1);
/*
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);
/*
test move
*/
-static bool test_move(void)
+static bool test_move(const struct torture_context *ctx)
{
void *root;
struct t_move {
int *x;
} *t1, *t2;
- root = talloc_new(NULL);
+ root = talloc_new(ctx);
t1 = talloc(root, struct t_move);
t2 = talloc(root, struct t_move);
/*
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);
}
-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");
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");
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;
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;
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;
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;
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;
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;
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();
}