>From a14571ab429533e9d9d3f83c6a46a0e09489d330 Mon Sep 17 00:00:00 2001
From: Martin Nagy <mnagy@redhat.com>
Date: Tue, 6 Oct 2009 01:34:55 +0200
Subject: [PATCH 2/3] Add simple reference counting wrappers for talloc

---
 server/Makefile.am            |   16 +++-
 server/tests/refcount-tests.c |  231 +++++++++++++++++++++++++++++++++++++++++
 server/util/refcount.c        |   88 ++++++++++++++++
 server/util/refcount.h        |   39 +++++++
 4 files changed, 373 insertions(+), 1 deletions(-)
 create mode 100644 server/tests/refcount-tests.c
 create mode 100644 server/util/refcount.c
 create mode 100644 server/util/refcount.h

diff --git a/server/Makefile.am b/server/Makefile.am
index 81223f4..7a94563 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -68,7 +68,8 @@ if HAVE_CHECK
         resolv-tests \
         krb5-utils-tests \
         check_and_open-tests \
-        files-tests
+        files-tests \
+        refcount-tests
 endif
 
 check_PROGRAMS = \
@@ -184,6 +185,7 @@ SSSD_UTIL_OBJ = \
     util/strtonum.c \
     util/check_and_open.c \
     util/files.c \
+    util/refcount.c \
     $(SSSD_DEBUG_OBJ)
 
 SSSD_RESPONDER_OBJ = \
@@ -247,6 +249,7 @@ dist_noinst_HEADERS = \
     util/util.h \
     util/strtonum.h \
     util/sss_ldap.h \
+    util/refcount.h \
     config.h \
     monitor/monitor.h \
     monitor/monitor_interfaces.h \
@@ -451,6 +454,17 @@ resolv_tests_LDADD = \
     $(SSSD_LIBS) \
     $(CHECK_LIBS) \
     $(CARES_LIBS)
+
+refcount_tests_SOURCES = \
+    tests/refcount-tests.c \
+    tests/common.c \
+    $(CHECK_OBJ) \
+    $(SSSD_UTIL_OBJ)
+refcount_tests_CFLAGS = \
+    $(CHECK_CFLAGS)
+refcount_tests_LDADD = \
+    $(SSSD_LIBS) \
+    $(CHECK_LIBS)
 endif
 
 stress_tests_SOURCES = \
diff --git a/server/tests/refcount-tests.c b/server/tests/refcount-tests.c
new file mode 100644
index 0000000..ff82b4c
--- /dev/null
+++ b/server/tests/refcount-tests.c
@@ -0,0 +1,231 @@
+/*
+   SSSD
+
+   Reference counting tests.
+
+   Authors:
+        Martin Nagy <mnagy@redhat.com>
+
+   Copyright (C) Red Hat, Inc 2009
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <check.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <popt.h>
+
+#include "tests/common.h"
+#include "util/util.h"
+
+/* Interface under test */
+#include "util/refcount.h"
+
+/* Fail the test if object 'obj' does not have 'num' references. */
+#define REF_ASSERT(obj, num) \
+    fail_unless(((obj)->DO_NOT_TOUCH_THIS_MEMBER_refcount == (num)), \
+                "Reference count of " #obj " should be %d but is %d", \
+                (num), (obj)->DO_NOT_TOUCH_THIS_MEMBER_refcount)
+
+#define FILLER_SIZE 32
+
+struct foo {
+    REFCOUNT_COMMON;
+    char a[FILLER_SIZE];
+    char b[FILLER_SIZE];
+};
+
+struct bar {
+    char a[FILLER_SIZE];
+    REFCOUNT_COMMON;
+    char b[FILLER_SIZE];
+};
+
+struct baz {
+    char a[FILLER_SIZE];
+    char b[FILLER_SIZE];
+    REFCOUNT_COMMON;
+};
+
+#define SET_FILLER(target) do { \
+    memset((target)->a, 'a', FILLER_SIZE); \
+    memset((target)->b, 'b', FILLER_SIZE); \
+} while (0)
+
+#define CHECK_FILLER(target) do { \
+    int _counter; \
+    for (_counter = 0; _counter < FILLER_SIZE; _counter++) { \
+        fail_unless((target)->a[_counter] == 'a', "Corrupted memory in "  \
+                    #target "->a[%d] of size %d", _counter, FILLER_SIZE); \
+        fail_unless((target)->b[_counter] == 'b', "Corrupted memory in "  \
+                    #target "->b[%d] of size %d", _counter, FILLER_SIZE); \
+    } \
+} while (0)
+
+struct container {
+    struct foo *foo;
+    struct bar *bar;
+    struct baz *baz;
+};
+
+static struct container *global;
+
+START_TEST(test_refcount_basic)
+{
+    struct container *containers;
+    int i;
+
+    /* First allocate our global storage place. */
+    global = talloc(NULL, struct container);
+    fail_if(global == NULL);
+
+    /* Allocate foo. */
+    global->foo = rc_alloc(global, struct foo);
+    fail_if(global->foo == NULL);
+    SET_FILLER(global->foo);
+    REF_ASSERT(global->foo, 1);
+
+    /* Allocate bar. */
+    global->bar = rc_alloc(global, struct bar);
+    fail_if(global->bar == NULL);
+    SET_FILLER(global->bar);
+    REF_ASSERT(global->bar, 1);
+
+    /* Allocate baz. */
+    global->baz = rc_alloc(global, struct baz);
+    fail_if(global->baz == NULL);
+    SET_FILLER(global->baz);
+    REF_ASSERT(global->baz, 1);
+
+    /* Try multiple attaches. */
+    containers = talloc_array(NULL, struct container, 100);
+    fail_if(containers == NULL);
+    for (i = 0; i < 100; i++) {
+        containers[i].foo = rc_reference(containers, struct foo, global->foo);
+        containers[i].bar = rc_reference(containers, struct bar, global->bar);
+        containers[i].baz = rc_reference(containers, struct baz, global->baz);
+        REF_ASSERT(containers[i].foo, i + 2);
+        REF_ASSERT(global->foo, i + 2);
+        REF_ASSERT(containers[i].bar, i + 2);
+        REF_ASSERT(global->bar, i + 2);
+        REF_ASSERT(containers[i].baz, i + 2);
+        REF_ASSERT(global->baz, i + 2);
+    }
+    talloc_free(containers);
+
+    CHECK_FILLER(global->foo);
+    CHECK_FILLER(global->bar);
+    CHECK_FILLER(global->baz);
+
+    REF_ASSERT(global->foo, 1);
+    REF_ASSERT(global->bar, 1);
+    REF_ASSERT(global->baz, 1);
+
+    talloc_free(global);
+}
+END_TEST
+
+START_TEST(test_refcount_swap)
+{
+    void *tmp_ctx;
+    struct container *container1;
+    struct container *container2;
+
+    tmp_ctx = talloc_new(NULL);
+
+    check_leaks_push(tmp_ctx);
+
+    container1 = talloc(tmp_ctx, struct container);
+    container2 = talloc(tmp_ctx, struct container);
+
+    /* Allocate. */
+    container1->foo = rc_alloc(container1, struct foo);
+    fail_if(container1->foo == NULL);
+    SET_FILLER(container1->foo);
+
+    /* Reference. */
+    container2->foo = rc_reference(container2, struct foo, container1->foo);
+    fail_if(container2->foo == NULL);
+
+    /* Make sure everything is as it should be. */
+    fail_unless(container1->foo == container2->foo);
+    REF_ASSERT(container1->foo, 2);
+
+    /* Free in reverse order. */
+    talloc_free(container1);
+    REF_ASSERT(container2->foo, 1);
+    CHECK_FILLER(container2->foo);
+    talloc_free(container2);
+
+    check_leaks_pop(tmp_ctx);
+    talloc_free(tmp_ctx);
+}
+END_TEST
+
+Suite *create_suite(void)
+{
+    Suite *s = suite_create("refcount");
+
+    TCase *tc = tcase_create("REFCOUNT Tests");
+
+    /* Do some testing */
+    tcase_add_checked_fixture(tc, leak_check_setup, leak_check_teardown);
+    tcase_add_test(tc, test_refcount_basic);
+    tcase_add_test(tc, test_refcount_swap);
+
+    /* Add all test cases to the test suite */
+    suite_add_tcase(s, tc);
+
+    return s;
+}
+
+int main(int argc, const char *argv[])
+{
+    int opt;
+    poptContext pc;
+    int failure_count;
+    Suite *suite;
+    SRunner *sr;
+    int debug = 0;
+
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL },
+        POPT_TABLEEND
+    };
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+    debug_level = debug;
+
+    suite = create_suite();
+    sr = srunner_create(suite);
+    srunner_set_fork_status(sr, CK_FORK);
+    srunner_run_all(sr, CK_VERBOSE);
+    failure_count = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (failure_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
diff --git a/server/util/refcount.c b/server/util/refcount.c
new file mode 100644
index 0000000..735170d
--- /dev/null
+++ b/server/util/refcount.c
@@ -0,0 +1,88 @@
+/*
+   SSSD
+
+   Simple reference counting wrappers for talloc.
+
+   Authors:
+        Martin Nagy <mnagy@redhat.com>
+
+   Copyright (C) Red Hat, Inc 2009
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+
+#include "refcount.h"
+#include "util/util.h"
+
+struct wrapper {
+    int *refcount;
+    void *ptr;
+};
+
+static int
+refcount_destructor(struct wrapper *wrapper)
+{
+    (*wrapper->refcount)--;
+    if (*wrapper->refcount == 0) {
+        talloc_free(wrapper->ptr);
+    };
+
+    return 0;
+}
+
+void *
+_rc_alloc(const void *context, size_t size, size_t refcount_offset,
+          const char *type_name)
+{
+    struct wrapper *wrapper;
+
+    wrapper = talloc(context, struct wrapper);
+    if (wrapper == NULL) {
+        return NULL;
+    }
+
+    wrapper->ptr = talloc_named_const(NULL, size, type_name);
+    if (wrapper->ptr == NULL) {
+        talloc_free(wrapper);
+        return NULL;
+    };
+
+    wrapper->refcount = (int *)((char *)wrapper->ptr + refcount_offset);
+    *wrapper->refcount = 1;
+
+    talloc_set_destructor(wrapper, refcount_destructor);
+
+    return wrapper->ptr;
+}
+
+void *
+_rc_reference(const void *context, size_t refcount_offset, void *source)
+{
+    struct wrapper *wrapper;
+
+    wrapper = talloc(context, struct wrapper);
+    if (wrapper == NULL) {
+        return NULL;
+    }
+
+    wrapper->ptr = source;
+    wrapper->refcount = (int *)((char *)wrapper->ptr + refcount_offset);
+    (*wrapper->refcount)++;
+
+    talloc_set_destructor(wrapper, refcount_destructor);
+
+    return wrapper->ptr;
+}
diff --git a/server/util/refcount.h b/server/util/refcount.h
new file mode 100644
index 0000000..cc99a17
--- /dev/null
+++ b/server/util/refcount.h
@@ -0,0 +1,39 @@
+#ifndef __REFCOUNT_H__
+#define __REFCOUNT_H__
+
+#include <stddef.h>
+
+#define REFCOUNT_MEMBER_NAME DO_NOT_TOUCH_THIS_MEMBER_refcount
+
+/*
+ * Include this member in your structure in order to be able to use it with
+ * the refcount_* functions.
+ */
+#define REFCOUNT_COMMON int REFCOUNT_MEMBER_NAME
+
+/*
+ * Allocate a new structure that uses reference counting. The resulting pointer
+ * returned. You must not free the returned pointer manually. It will be freed
+ * when 'ctx' is freed with talloc_free() and no other references are left.
+ */
+#define rc_alloc(ctx, type) \
+ (type *)_rc_alloc(ctx, sizeof(type), offsetof(type, REFCOUNT_MEMBER_NAME), \
+                   #type)
+
+/*
+ * Increment the reference count of 'src' and return it back if we are
+ * successful. The reference count will be decremented after 'ctx' has been
+ * released by talloc_free(). The function will return NULL in case of failure.
+ */
+#define rc_reference(ctx, type, src) \
+ (type *)_rc_reference(ctx, offsetof(type, REFCOUNT_MEMBER_NAME), src)
+
+/*
+ * These functions should not be used directly. Use the above macros instead.
+ */
+void *_rc_alloc(const void *context, size_t size, size_t refcount_offset,
+                 const char *type_name);
+void *_rc_reference(const void *context, size_t refcount_offset, void *source);
+
+
+#endif /* !__REFCOUNT_H__ */
-- 
1.6.2.5

