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.
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) {