diff options
-rw-r--r-- | server/Makefile.am | 16 | ||||
-rw-r--r-- | server/tests/refcount-tests.c | 231 | ||||
-rw-r--r-- | server/util/refcount.c | 88 | ||||
-rw-r--r-- | server/util/refcount.h | 39 |
4 files changed, 373 insertions, 1 deletions
diff --git a/server/Makefile.am b/server/Makefile.am index 583baa98a..ca793394c 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 = \ @@ -185,6 +186,7 @@ SSSD_UTIL_OBJ = \ util/backup_file.c \ util/strtonum.c \ util/check_and_open.c \ + util/refcount.c \ $(SSSD_DEBUG_OBJ) SSSD_RESPONDER_OBJ = \ @@ -258,6 +260,7 @@ dist_noinst_HEADERS = \ util/strtonum.h \ util/sss_ldap.h \ util/sss_krb5.h \ + util/refcount.h \ config.h \ monitor/monitor.h \ monitor/monitor_interfaces.h \ @@ -464,6 +467,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 000000000..ff82b4ca8 --- /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 000000000..735170d1b --- /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 000000000..cc99a1731 --- /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__ */ |