From 5b3d619eb53abee7b47110088dd0ee00fc57961a Mon Sep 17 00:00:00 2001 From: Sam Liddicott Date: Fri, 10 Jul 2009 11:45:09 +0100 Subject: [PATCH] talloc add more tests more new talloc model test_dangling_loop test_ref_free_owner test_ref_free_self test_ref_free Signed-off-by: Sam Liddicott --- lib/talloc/testsuite.c | 236 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/lib/talloc/testsuite.c b/lib/talloc/testsuite.c index 9b79bfacac6d..03afd2702e0e 100644 --- a/lib/talloc/testsuite.c +++ b/lib/talloc/testsuite.c @@ -903,6 +903,234 @@ static bool test_implicit_explicit_free(void) return true; } +/* If take r1 reference to p2 and then free p2's parent + p2 should still be around by virtue of the reference. + In current talloc r1 will be the parent + In proposed talloc r1 will be the reference with no parent */ +static bool test_ref_free_owner(void) +{ + void *root, *p1, *p2, *p3, *ref, *r1; + + printf("test: ref_free_owner\n# SINGLE REFERENCE FREE OWNER FREE\n"); + talloc_erase_no_owner_context(); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + p3 = talloc_named_const(root, 1, "p3"); + /* Now root owns p1 ,and p2 owns p2 */ + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + /* now r1 has ref reference to p2 */ + talloc_report_full(root, stderr); + + CHECK_BLOCKS(__FUNCTION__, p1, 2); + CHECK_BLOCKS(__FUNCTION__, p2, 1); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + /* r1 should have ref reference to p2 still */ + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* if we talloc_steal p2 to p3, r1 should still have a reference + (unless it had been made the parent like in old talloc */ + fprintf(stderr, "Stealing p2 to p3\n"); + talloc_steal(p3, p2); + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* now we free p3 and r1 should still have a reference */ + fprintf(stderr, "free p3\n"); + talloc_free(p3); + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* if we free r1 then p2 should vanish */ + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, root, 1); + + talloc_free(root); + printf("success: ref1\n"); + return true; +} + +/* If take r1 reference to p2 and then free p2 + p2 should still be around by virtue of the reference. + In current talloc r1 will be the parent + In proposed talloc r1 will be the reference with no parent */ +static bool test_ref_free_self(void) +{ + void *root, *p1, *p2, *p3, *ref, *r1; + + printf("test: ref_free_self\n# SINGLE REFERENCE FREE SELF FREE\n"); + talloc_erase_no_owner_context(); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + p3 = talloc_named_const(root, 1, "p3"); + /* Now root owns p1, and p1 owns p2 */ + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + /* now r1 has ref reference to p2 */ + talloc_report_full(NULL, stderr); + + CHECK_BLOCKS(__FUNCTION__, p1, 2); + CHECK_BLOCKS(__FUNCTION__, p2, 1); + CHECK_BLOCKS(__FUNCTION__, p3, 1); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + fprintf(stderr, "Freeing p2\n"); + talloc_free(p2); + /* r1 should have ref reference to p2 still */ + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* if we talloc_steal p2 to p3, r1 should still have a reference + (unless it had been made the parent like in old talloc */ + fprintf(stderr, "Steal p2 to p3\n"); + talloc_steal(p3, p2); + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* now we free p3 and r1 should still have a reference */ + fprintf(stderr, "free p3\n"); + talloc_free(p3); + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* if we free r1 then p2 should also vanish */ + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + + fprintf(stderr, "Checking that p1 is empty and freeing p1\n"); + CHECK_BLOCKS(__FUNCTION__, p1, 1); + talloc_free(p1); + + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, root, 1); + + talloc_free(root); + printf("success: ref1\n"); + return true; +} + +/* check that an allocation that is freed while also referenced finally goes + away when the reference is released */ +static bool test_ref_free(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("test: test_ref_free\n# FREE ON SINGLE REFERENCE FREE\n"); + talloc_erase_no_owner_context(); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + /* Now root owns p1, and p1 owns p2 */ + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + /* now r1 has ref reference to p2 */ + talloc_report_full(root, stderr); + + CHECK_BLOCKS(__FUNCTION__, p1, 2); + CHECK_BLOCKS(__FUNCTION__, p2, 1); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + fprintf(stderr, "Freeing p2\n"); + talloc_free(p2); + /* r1 should have ref reference to p2 still */ + talloc_report_full(root, stderr); + + CHECK_BLOCKS(__FUNCTION__, p1, 1); + CHECK_BLOCKS(__FUNCTION__, root, 4); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* if we free r1 then p2 should also vanish */ + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + /* p2 should have gone away */ + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, root, 2); + CHECK_BLOCKS(__FUNCTION__, p1, 1); + + talloc_free(root); + printf("success: ref1\n"); + return true; +} + +/* If an object having references from children that are also referenced is + freed, the child reference will be removed, but the child will survive + (because of it's reference) and the object will still be freed leaving + a dangling reference */ +static bool test_dangling_loop(void) +{ + void *root, *p1, *p2, *ref, *r1, *r2; + + printf("test: %s\n# FREE ON SINGLE REFERENCE FREE\n",__FUNCTION__); + talloc_erase_no_owner_context(); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + /* Now root owns p1, and p1 owns p2 */ + + /* someone takes a ref on p2 */ + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + + /* p2 takes a ref on p1 */ + talloc_reference(p2, p1); + + talloc_report_full(NULL, stderr); + + CHECK_BLOCKS(__FUNCTION__, root, 6); + CHECK_BLOCKS(__FUNCTION__, p1, 3); + CHECK_BLOCKS(__FUNCTION__, p2, 2); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* talloc will wrongly spot a loop and free p2's ref, and then p1 + leaving a dangling pointer */ + fprintf(stderr, "Freeing p1\n"); + talloc_free(p1); + /* p1 should not get freed as it is referenced from something (a child) that won't free */ + /* r1 should have ref reference to p2 still */ + talloc_report_full(NULL, stderr); + + CHECK_BLOCKS(__FUNCTION__, root, 3); + /* The ugly talloc de-child-looping code will delete p2's reference + leaving p2 having a dangling pointer. p2's reference should remain */ + /* TODO change this back to 2 when the loop detection is working */ + CHECK_BLOCKS(__FUNCTION__, p2, 1); + CHECK_BLOCKS(__FUNCTION__, r1, 2); + + /* if we free r1 then p2 should also vanish as it is owned by something that + is not owned, but we can't track that yet. Once p2 vanishes it's reference + to p1 should vanish letting p1 vanish. + We can often make sub-tree's from no-owner-context, by checking when references + of things child-of no-owner-context die + */ + fprintf(stderr, "Freeing r1\n"); + talloc_free(r1); + talloc_report_full(NULL, stderr); + CHECK_BLOCKS(__FUNCTION__, root, 1); + + talloc_free(root); + printf("success: ref1\n"); + + talloc_erase_no_owner_context(); + talloc_report_full(NULL, stderr); + return true; +} + /* If take r1 reference to p2 and then free p2's owner p1 p2 should still be around by virtue of the reference. steal p2 to p3 and free p3. @@ -1368,6 +1596,14 @@ bool torture_local_talloc(struct torture_context *tctx) test_reset(); ret &= test_ref_free_steal(); test_reset(); + ret &= test_dangling_loop(); + test_reset(); + ret &= test_ref_free_owner(); + test_reset(); + ret &= test_ref_free_self(); + test_reset(); + ret &= test_ref_free(); + test_reset(); ret &= test_pool(); if (ret) { -- 2.34.1