} \
} while (0)
+static unsigned int test_abort_count;
+
+static void test_abort_fn(const char *reason)
+{
+ printf("# test_abort_fn(%s)\n", reason);
+ test_abort_count++;
+}
+
+static void test_abort_start(void)
+{
+ test_abort_count = 0;
+ talloc_set_abort_fn(test_abort_fn);
+}
+
+static void test_abort_stop(void)
+{
+ test_abort_count = 0;
+ talloc_set_abort_fn(NULL);
+}
/*
test references
return true;
}
+/* If a referenced allocation is explicitly freed the new owner
+ should be the same as if the same allocation is implicitly freed
+ (because it's owner was freed).
+ Traditionally in talloc an explicit free will free the top non-child reference
+ but an implicit free will move the top (any) reference to be the new owner */
+static bool test_implicit_explicit_free(void)
+{
+ void *root, *p1, *p2, *p3, *ref, *r1;
+ int e, i;
+
+ printf("test: test_implicit_explicit_free\n# SINGLE REFERENCE IMPLICIT FREE\n");
+
+ 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);
+ /* how many blocks is r1 taking against p2 ? */
+ e=talloc_total_blocks(r1);
+
+ talloc_report_full(root, stderr);
+ talloc_free(root);
+
+ /* now repeat, but this time free p1 */
+ printf("# SINGLE REFERENCE EXPLICIT FREE\n");
+
+ 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(NULL, stderr);
+
+ CHECK_BLOCKS(__FUNCTION__, p1, 2);
+ CHECK_BLOCKS(__FUNCTION__, p2, 1);
+ CHECK_BLOCKS(__FUNCTION__, r1, 2);
+
+ fprintf(stderr, "Freeing p1\n");
+ talloc_free(p1);
+ /* how many blocks is r1 taking against p2 ? */
+ i=talloc_total_blocks(r1);
+ talloc_report_full(NULL, stderr);
+
+ CHECK_BLOCKS(__FUNCTION__,r1, e);
+
+ talloc_free(root);
+
+ printf("success: ref1\n");
+ 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.
+ In current talloc p2 will be freed despite the reference.
+ In proposed talloc r1 will be the reference with no parent for p2*/
+static bool test_ref_free_steal(void)
+{
+ void *root, *p1, *p2, *p3, *ref, *r1;
+
+ printf("test: ref_free_self\n# SINGLE REFERENCE FREE SELF FREE\n");
+
+ 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 p1\n");
+ talloc_free(p1);
+ /* r1 should have ref reference to p2 still */
+ talloc_report_full(NULL, stderr);
+ CHECK_BLOCKS(__FUNCTION__, r1, 2);
+ CHECK_BLOCKS(__FUNCTION__, p3, 1);
+
+ /* if we talloc_steal p2 to p3, we should get an abort() */
+ fprintf(stderr, "Steal p2 to p3\n");
+ test_abort_start();
+ talloc_steal(p3, p2);
+ torture_assert(__FUNCTION__,
+ test_abort_count == 1,
+ "talloc_steal() didn't abort");
+ test_abort_stop();
+
+ talloc_report_full(NULL, stderr);
+ CHECK_BLOCKS(__FUNCTION__, r1, 2);
+ CHECK_BLOCKS(__FUNCTION__, p3, 1);
+
+ /* but we can reference p2 to p3 */
+ fprintf(stderr, "Steal p2 to p3\n");
+ talloc_reference(p3, p2);
+
+ talloc_report_full(NULL, stderr);
+ CHECK_BLOCKS(__FUNCTION__, r1, 2);
+ CHECK_BLOCKS(__FUNCTION__, p3, 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__, p2, 1);
+ CHECK_BLOCKS(__FUNCTION__, r1, 2);
+
+ /* if we free r1 then p2 should also vanish */
+ fprintf(stderr, "Freeing r1\n");
+ talloc_free(r1);
+
+ talloc_report_full(NULL, stderr);
+ CHECK_BLOCKS(__FUNCTION__, NULL, 3);
+ CHECK_BLOCKS(__FUNCTION__, root, 1);
+
+ talloc_free(root);
+ CHECK_BLOCKS(__FUNCTION__, NULL, 2);
+ printf("success: ref1\n");
+ return true;
+}
+
/*
measure the speed of talloc versus malloc
*/
static void test_reset(void)
{
+ test_abort_stop();
talloc_disable_null_tracking();
talloc_enable_null_tracking();
}
test_reset();
ret &= test_talloc_free_in_destructor();
test_reset();
+ ret &= test_implicit_explicit_free();
+ test_reset();
+ ret &= test_ref_free_steal();
+ test_reset();
ret &= test_pool();
if (ret) {