From: David M. Lee Date: Thu, 15 Nov 2012 23:06:56 +0000 (+0000) Subject: Reverted r376309; merged to wrong branch X-Git-Tag: 10.12.0-digiumphones-rc1~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=27d03eb7a94a30abe30798c450d58adddf74edd3;p=thirdparty%2Fasterisk.git Reverted r376309; merged to wrong branch git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10-digiumphones@376311 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- diff --git a/include/asterisk/hashtab.h b/include/asterisk/hashtab.h index 17eff70482..89d293759a 100644 --- a/include/asterisk/hashtab.h +++ b/include/asterisk/hashtab.h @@ -23,9 +23,6 @@ * \brief Generic (perhaps overly so) hashtable implementation * \ref AstHash */ - -#include "asterisk/lock.h" - /*! \page AstHash Hash Table support in Asterisk A hash table is a structure that allows for an exact-match search diff --git a/tests/test_astobj2_thrash.c b/tests/test_astobj2_thrash.c deleted file mode 100644 index f43445a051..0000000000 --- a/tests/test_astobj2_thrash.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2012, David M. Lee, II - * - * David M. Lee, II - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/* - *! \file \brief Thrash a astobj2 container, for fun and profit. - * - * \author\verbatim David M. Lee, II \endverbatim - * - * Inspired by the original hashtest2.c by Steve Murphy . This test runs - * several threads manipulatings a concurrent astobj2 container to see if they maintain - * consistency. While the tests attempt to check consistency and error normally, threading - * errors often result in segfaults. - * \ingroup tests - */ - -/*** MODULEINFO - TEST_FRAMEWORK - core - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include -#include "asterisk/astobj2.h" -#include "asterisk/hashtab.h" -#include "asterisk/lock.h" -#include "asterisk/module.h" -#include "asterisk/test.h" -#include "asterisk/time.h" -#include "asterisk/utils.h" - -#define MAX_HASH_ENTRIES 15000 -#define MAX_TEST_SECONDS 60 - -struct hash_test { - /*! Unit under test */ - struct ao2_container *to_be_thrashed; - /*! Number of entries to insert in the grow thread. */ - int max_grow; - /*! Number of enteries added by the grow thread. */ - int grow_count; - /*! Entries preloaded into the hashtab; to be deleted by the shrink thread */ - int preload; - /*! When to give up on the tests */ - struct timeval deadline; -}; - -static int alloc_count = 0; - -static int is_timed_out(struct hash_test const *data) { - return ast_tvdiff_us(data->deadline, ast_tvnow()) < 0; -} - -/*! /brief Free test element */ -static void ht_delete(void *obj) -{ - ast_atomic_fetchadd_int(&alloc_count, -1); -} - -/*! /brief Create test element */ -static char *ht_new(int i) -{ - const int buflen = 12; - char *keybuf = ao2_alloc(buflen, ht_delete); - int needed; - if (keybuf == NULL) { - return NULL; - } - needed = snprintf(keybuf, buflen, "key%08x", i); - ast_atomic_fetchadd_int(&alloc_count, 1); - ast_assert(needed + 1 <= buflen); - return keybuf; -} - -/*! /brief Grow the hash data as specified */ -static void *hash_test_grow(void *d) -{ - struct hash_test *data = d; - int i; - - for (i = 0; i < data->max_grow; ++i) { - char *ht; - if (is_timed_out(data)) { - printf("Growth timed out at %d\n", i); - return "Growth timed out"; - } - ht = ht_new(i); - if (ht == NULL) { - return "Allocation failed"; - } - ao2_link(data->to_be_thrashed, ht); - ao2_ref(ht, -1); - ast_atomic_fetchadd_int(&data->grow_count, 1); - } - return NULL; -} - -/*! Randomly lookup data in the hash */ -static void *hash_test_lookup(void *d) -{ - struct hash_test *data = d; - int max; - unsigned seed = time(NULL); - - /* ast_atomic_fetchadd_int provide a memory fence so that the optimizer doesn't - * optimize away reads. - */ - while ((max = ast_atomic_fetchadd_int(&data->grow_count, 0)) < data->max_grow) { - int i; - char *obj; - char *from_ao2; - - if (is_timed_out(data)) { - return "Lookup timed out"; - } - - if (max == 0) { - /* No data yet; yield and try again */ - sched_yield(); - continue; - } - - /* Randomly lookup one object from the hash */ - i = rand_r(&seed) % max; - obj = ht_new(i); - if (obj == NULL) { - return "Allocation failed"; - } - from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_POINTER); - ao2_ref(obj, -1); - ao2_ref(from_ao2, -1); - if (from_ao2 == NULL) { - return "Key unexpectedly missing"; - } - } - - return NULL; -} - -/*! Delete entries from the hash */ -static void *hash_test_shrink(void *d) -{ - const struct hash_test *data = d; - int i; - - for (i = 1; i < data->preload; ++i) { - char *obj = ht_new(-i); - char *from_ao2; - - if (obj == NULL) { - return "Allocation failed"; - } - from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_UNLINK | OBJ_POINTER); - - ao2_ref(obj, -1); - if (from_ao2) { - ao2_ref(from_ao2, -1); - } else { - return "Could not find object to delete"; - } - - if (is_timed_out(data)) { - return "Shrink timed out"; - } - } - - return NULL; -} - -/*! ao2_callback for hash_test_count */ -static int increment_count(void *obj, void *arg, int flags) { - char *ht = obj; - int *count = arg; - if (strncmp(ht, "key0", 4) == 0) { - ++(*count); - } - return 0; -} - -/*! Continuously iterate through all the entries in the hash */ -static void *hash_test_count(void *d) -{ - const struct hash_test *data = d; - int count = 0; - int last_count = 0; - - while (count < data->max_grow) { - last_count = count; - count = 0; - ao2_callback(data->to_be_thrashed, OBJ_MULTIPLE, increment_count, &count); - - if (last_count == count) { - /* Allow other threads to run. */ - sched_yield(); - } else if (last_count > count) { - /* Make sure the ao2 container never shrinks */ - return "ao2 container unexpectedly shrank"; - } - - if (is_timed_out(data)) { - return "Count timed out"; - } - } - - /* Successfully iterated over all of the expected elements */ - return NULL; -} - -static int hash_string(const void *obj, const int flags) -{ - return ast_hashtab_hash_string_nocase(obj); -} - -static int compare_strings(void *lhs, void *rhs, int flags) -{ - const char *lhs_str = lhs; - const char *rhs_str = rhs; - if (strcasecmp(lhs_str, rhs_str) == 0) { - return CMP_MATCH | CMP_STOP; - } else { - return 0; - } -} - -AST_TEST_DEFINE(hash_test) -{ - enum ast_test_result_state res = AST_TEST_PASS; - struct hash_test data = {}; - pthread_t grow_thread, count_thread, lookup_thread, shrink_thread; - void *thread_results; - int i; - - switch (cmd) { - case TEST_INIT: - info->name = "thrash"; - info->category = "/main/astobj2/"; - info->summary = "Testing astobj2 container concurrency"; - info->description = "Test astobj2 container concurrency correctness."; - return AST_TEST_NOT_RUN; - case TEST_EXECUTE: - break; - } - - ast_test_status_update(test, "Executing hash concurrency test...\n"); - data.preload = MAX_HASH_ENTRIES / 2; - data.max_grow = MAX_HASH_ENTRIES - data.preload; - data.deadline = ast_tvadd(ast_tvnow(), ast_tv(MAX_TEST_SECONDS, 0)); - data.to_be_thrashed = ao2_container_alloc(MAX_HASH_ENTRIES / 100, hash_string, - compare_strings); - - if (data.to_be_thrashed == NULL) { - ast_test_status_update(test, "Allocation failed\n"); - /* Nothing needs to be freed; early return is fine */ - return AST_TEST_FAIL; - } - - /* preload with data to delete */ - for (i = 1; i < data.preload; ++i) { - char *ht = ht_new(-i); - if (ht == NULL) { - ast_test_status_update(test, "Allocation failed\n"); - ao2_ref(data.to_be_thrashed, -1); - return AST_TEST_FAIL; - } - ao2_link(data.to_be_thrashed, ht); - ao2_ref(ht, -1); - } - - /* add data.max_grow entries to the ao2 container */ - ast_pthread_create(&grow_thread, NULL, hash_test_grow, &data); - /* continually count the keys added by the grow thread */ - ast_pthread_create(&count_thread, NULL, hash_test_count, &data); - /* continually lookup keys added by the grow thread */ - ast_pthread_create(&lookup_thread, NULL, hash_test_lookup, &data); - /* delete all keys preloaded into the ao2 container */ - ast_pthread_create(&shrink_thread, NULL, hash_test_shrink, &data); - - pthread_join(grow_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Growth thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - pthread_join(count_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Count thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - pthread_join(lookup_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Lookup thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - pthread_join(shrink_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Shrink thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - if (ao2_container_count(data.to_be_thrashed) != data.max_grow) { - ast_test_status_update(test, - "Invalid ao2 container size. Expected: %d, Actual: %d\n", - data.max_grow, ao2_container_count(data.to_be_thrashed)); - res = AST_TEST_FAIL; - } - - ao2_ref(data.to_be_thrashed, -1); - - /* check for object leaks */ - if (ast_atomic_fetchadd_int(&alloc_count, 0) != 0) { - ast_test_status_update(test, "Leaked %d objects!\n", - ast_atomic_fetchadd_int(&alloc_count, 0)); - res = AST_TEST_FAIL; - } - - return res; -} - -static int unload_module(void) -{ - AST_TEST_UNREGISTER(hash_test); - return 0; -} - -static int load_module(void) -{ - AST_TEST_REGISTER(hash_test); - return AST_MODULE_LOAD_SUCCESS; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "astobj2 container thrash test"); diff --git a/tests/test_hashtab_thrash.c b/tests/test_hashtab_thrash.c deleted file mode 100644 index 373922bf84..0000000000 --- a/tests/test_hashtab_thrash.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2012, David M. Lee, II - * - * David M. Lee, II - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/* - *! \file \brief Thrash a hash table, for fun and profit. - * - * \author\verbatim David M. Lee, II \endverbatim - * - * Inspired by the original hashtest.c by Steve Murphy . This test runs - * several threads manipulatings a concurrent hastab to see if they maintain - * consistency. While the tests attempt to check consistency and error normally, threading - * errors often result in segfaults. - * \ingroup tests - */ - -/*** MODULEINFO - TEST_FRAMEWORK - core - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -#include -#include "asterisk/hashtab.h" -#include "asterisk/lock.h" -#include "asterisk/module.h" -#include "asterisk/test.h" -#include "asterisk/time.h" -#include "asterisk/utils.h" - -#define MAX_HASH_ENTRIES 30000 -#define MAX_TEST_SECONDS 60 - -struct hash_test { - /*! Unit under test */ - struct ast_hashtab *to_be_thrashed; - /*! Number of entries to insert in the grow thread. */ - int max_grow; - /*! Number of enteries added by the grow thread. */ - int grow_count; - /*! Entries preloaded into the hashtab; to be deleted by the shrink thread */ - int preload; - /*! When to give up on the tests */ - struct timeval deadline; -}; - -static int is_timed_out(struct hash_test const *data) { - return ast_tvdiff_us(data->deadline, ast_tvnow()) < 0; -} - -/*! /brief Create test element */ -static char *ht_new(int i) -{ - const int buflen = 12; - char *keybuf = ast_malloc(buflen); - int needed; - if (keybuf == NULL) { - return NULL; - } - needed = snprintf(keybuf, buflen, "key%08x", i); - ast_assert(needed + 1 <= buflen); - return keybuf; -} - -/*! /brief Free test element */ -static void ht_delete(void *obj) -{ - ast_free(obj); -} - -/*! /brief Grow the hash data as specified */ -static void *hash_test_grow(void *d) -{ - struct hash_test *data = d; - int i; - - for (i = 0; i < data->max_grow; ++i) { - char *obj; - if (is_timed_out(data)) { - return "Growth timed out"; - } - obj = ht_new(i); - if (obj == NULL) { - return "Allocation failed"; - } - ast_hashtab_insert_immediate(data->to_be_thrashed, obj); - ast_atomic_fetchadd_int(&data->grow_count, 1); - } - return NULL; -} - -/*! Randomly lookup data in the hash */ -static void *hash_test_lookup(void *d) -{ - struct hash_test *data = d; - int max; - unsigned seed = time(NULL); - - /* ast_atomic_fetchadd_int provide a memory fence so that the optimizer doesn't - * optimize away reads. - */ - while ((max = ast_atomic_fetchadd_int(&data->grow_count, 0)) < data->max_grow) { - int i; - char *obj; - int is_in_hashtab; - - if (is_timed_out(data)) { - return "Lookup timed out"; - } - - if (max == 0) { - /* No data yet; yield and try again */ - sched_yield(); - continue; - } - - /* Randomly lookup one object from the hash */ - i = rand_r(&seed) % max; - obj = ht_new(i); - if (obj == NULL) { - return "Allocation failed."; - } - is_in_hashtab = (ast_hashtab_lookup(data->to_be_thrashed, obj) != NULL); - ht_delete(obj); - if (!is_in_hashtab) { - return "key unexpectedly missing"; - } - } - - return NULL; -} - -/*! Delete entries from the hash */ -static void *hash_test_shrink(void *d) -{ - const struct hash_test *data = d; - int i; - - for (i = 1; i < data->preload; ++i) { - char *obj = ht_new(-i); - char *from_hashtab; - int deleted; - - if (obj == NULL) { - return "Allocation failed"; - } - from_hashtab = ast_hashtab_remove_object_via_lookup(data->to_be_thrashed, obj); - deleted = from_hashtab != NULL; - - ht_delete(obj); - ht_delete(from_hashtab); - if (!deleted) { - return "could not delete object"; - } - if (is_timed_out(data)) { - return "Shrink timed out"; - } - } - return NULL; -} - -/*! Continuously iterate through all the entries in the hash */ -static void *hash_test_count(void *d) -{ - const struct hash_test *data = d; - int count = 0; - int last_count = 0; - - while (count < data->max_grow) { - struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(data->to_be_thrashed); - char *ht = ast_hashtab_next(it); - last_count = count; - count = 0; - while (ht) { - /* only count keys added by grow thread */ - if (strncmp(ht, "key0", 4) == 0) { - ++count; - } - ht = ast_hashtab_next(it); - } - ast_hashtab_end_traversal(it); - - if (last_count == count) { - /* Allow other threads to run. */ - sched_yield(); - } else if (last_count > count) { - /* Make sure the hashtable never shrinks */ - return "hashtab unexpectedly shrank"; - } - - if (is_timed_out(data)) { - return "Count timed out"; - } - } - - /* Successfully iterated over all of the expected elements */ - return NULL; -} - -AST_TEST_DEFINE(hash_test) -{ - enum ast_test_result_state res = AST_TEST_PASS; - struct hash_test data = {}; - pthread_t grow_thread, count_thread, lookup_thread, shrink_thread; - void *thread_results; - int i; - - switch (cmd) { - case TEST_INIT: - info->name = "thrash"; - info->category = "/main/hashtab/"; - info->summary = "Testing hashtab concurrency"; - info->description = "Test hashtab concurrency correctness."; - return AST_TEST_NOT_RUN; - case TEST_EXECUTE: - break; - } - - ast_test_status_update(test, "Executing hash concurrency test...\n"); - data.preload = MAX_HASH_ENTRIES / 2; - data.max_grow = MAX_HASH_ENTRIES - data.preload; - data.deadline = ast_tvadd(ast_tvnow(), ast_tv(MAX_TEST_SECONDS, 0)); - data.to_be_thrashed = ast_hashtab_create(MAX_HASH_ENTRIES / 100, - ast_hashtab_compare_strings_nocase, ast_hashtab_resize_java, - ast_hashtab_newsize_java, ast_hashtab_hash_string_nocase, 1); - - if (data.to_be_thrashed == NULL) { - ast_test_status_update(test, "Allocation failed\n"); - /* Nothing needs to be freed; early return is fine */ - return AST_TEST_FAIL; - } - - - /* preload with data to delete */ - for (i = 1; i < data.preload; ++i) { - char *obj = ht_new(-i); - if (obj == NULL) { - ast_test_status_update(test, "Allocation failed\n"); - ast_hashtab_destroy(data.to_be_thrashed, ht_delete); - return AST_TEST_FAIL; - } - ast_hashtab_insert_immediate(data.to_be_thrashed, obj); - } - - /* add data.max_grow entries to the hashtab */ - ast_pthread_create(&grow_thread, NULL, hash_test_grow, &data); - /* continually count the keys added by the grow thread */ - ast_pthread_create(&count_thread, NULL, hash_test_count, &data); - /* continually lookup keys added by the grow thread */ - ast_pthread_create(&lookup_thread, NULL, hash_test_lookup, &data); - /* delete all keys preloaded into the hashtab */ - ast_pthread_create(&shrink_thread, NULL, hash_test_shrink, &data); - - pthread_join(grow_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Growth thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - pthread_join(count_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Count thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - pthread_join(lookup_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Lookup thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - pthread_join(shrink_thread, &thread_results); - if (thread_results != NULL) { - ast_test_status_update(test, "Shrink thread failed: %s\n", - (char *)thread_results); - res = AST_TEST_FAIL; - } - - if (ast_hashtab_size(data.to_be_thrashed) != data.max_grow) { - ast_test_status_update(test, - "Invalid hashtab size. Expected: %d, Actual: %d\n", - data.max_grow, ast_hashtab_size(data.to_be_thrashed)); - res = AST_TEST_FAIL; - } - - ast_hashtab_destroy(data.to_be_thrashed, ht_delete); - return res; -} - -static int unload_module(void) -{ - AST_TEST_UNREGISTER(hash_test); - return 0; -} - -static int load_module(void) -{ - AST_TEST_REGISTER(hash_test); - return AST_MODULE_LOAD_SUCCESS; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hash test"); diff --git a/utils/Makefile b/utils/Makefile index 0862449398..718b9ba0c0 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -88,7 +88,7 @@ clean: rm -f *.s *.i rm -f md5.c strcompat.c ast_expr2.c ast_expr2.h ast_expr2f.c pbx_ael.c pval.c hashtab.c lock.c rm -f aelparse.c aelbison.c conf2ael - rm -f utils.c strings.c threadstorage.c sha1.c astobj2.c refcounter + rm -f utils.c strings.c threadstorage.c sha1.c astobj2.c hashtest2 hashtest refcounter rm -f db1-ast/.*.d @$(MAKE) -C db1-ast clean @@ -175,6 +175,11 @@ threadstorage.c: $(ASTTOPDIR)/main/threadstorage.c $(ECHO_PREFIX) echo " [CP] $(subst $(ASTTOPDIR)/,,$<) -> $@" $(CMD_PREFIX) cp "$<" "$@" +hashtest2.o: _ASTCFLAGS+=-O0 +hashtest2: hashtest2.o md5.o lock.o utils.o strings.o astobj2.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o + +hashtest: hashtest.o md5.o hashtab.o lock.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o +hashtest.o: _ASTCFLAGS+=-O0 refcounter: refcounter.o md5.o hashtab.o lock.o utils.o strings.o sha1.o strcompat.o threadstorage.o clicompat.o poll.o refcounter.o: _ASTCFLAGS+=-O0 diff --git a/utils/hashtest.c b/utils/hashtest.c new file mode 100644 index 0000000000..390e331e02 --- /dev/null +++ b/utils/hashtest.c @@ -0,0 +1,410 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Steve Murphy + * + * Steve Murphy + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ +/*! \file + * + * \brief A program to thoroughly thrash a hash table, testing + * out locking safety, and making sure all functionality + * is functioning. Run with 5 or more threads to get that + * fully intense firestorm of activity. If your + * hash tables don't crash, lock up, or go weird, it must + * be good code! Even features some global counters + * that will get slightly behind because they aren't lock-protected. + * + * \author Steve Murphy + */ + +/*** MODULEINFO + extended + ***/ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include +#include +#include +#include +#include "asterisk/lock.h" +#include "asterisk/hashtab.h" +#include "asterisk/channel.h" +#include "asterisk/utils.h" +#include "asterisk/module.h" +int testno = 1; + +/* stuff we need to make this work with the hashtab stuff */ +#if !defined(LOW_MEMORY) +int64_t ast_mark(int prof_id, int x) +{ + return 0; +} +#endif + +void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used); +void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used) +{ +} + +struct ht_element +{ + char *key; + char *val; +}; + +static int hashtab_compare_strings_nocase(const void *a, const void *b) +{ + const struct ht_element *ae = a, *be = b; + return ast_hashtab_compare_strings_nocase(ae->key, be->key); +} + +#if 0 +static int hashtab_compare_strings(const void *a, const void *b) +{ + const struct ht_element *ae = a, *be = b; + return ast_hashtab_compare_strings(ae->key, be->key); +} + +static unsigned int hashtab_hash_string(const void *obj) +{ + const struct ht_element *o = obj; + return ast_hashtab_hash_string((const void *)o->key); +} +#endif + +static unsigned int hashtab_hash_string_nocase(const void *obj) +{ + const struct ht_element *o = obj; + return ast_hashtab_hash_string_nocase((const void*)o->key); +} + +/* random numbers */ + +static int my_rand(int incl_low, int incl_high, unsigned int *seedp) +{ + if (incl_high == 0) + return 0; + + return incl_low + (rand_r(seedp) % incl_high); +} + + + + +/* the testing routines */ + +static int glob_highwater = 0; +struct ast_hashtab *glob_hashtab = 0; +unsigned int glob_seed = 0; +int els_removed = 0; +int els_added = 0; +int els_lookedup = 0; +int els_found = 0; +int els_traversals = 0; + +/* all the operations to perform on the hashtab */ + +static void add_element(void) +{ + char keybuf[100]; + struct ht_element *x = malloc(sizeof(struct ht_element)); + sprintf(keybuf,"key%08d", glob_highwater++); + x->key = strdup(keybuf); + x->val = strdup("interesting data"); + ast_hashtab_insert_immediate(glob_hashtab, x); + els_added++; +} + +static void traverse_elements(void) +{ + struct ht_element *el; + int c=0; + struct ast_hashtab_iter *it = ast_hashtab_start_write_traversal(glob_hashtab); +#ifdef DEBUG + printf("Traverse hashtab\n"); +#endif + while ((el = ast_hashtab_next(it))) { + c++; + } + ast_hashtab_end_traversal(it); + els_traversals++; /* unprotected, sometimes off, but, not really important, either */ +} + +static void * del_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); +#ifdef DEBUG + printf("Removing %s", keybuf); +#endif + lookup.key = keybuf; + el = ast_hashtab_remove_object_via_lookup(glob_hashtab, &lookup); + + if (el) { +#ifdef DEBUG + printf("...YES (el=%x)\n", (unsigned long)el); +#endif + free(el->key); + free(el->val); + free(el); + els_removed++; + } else { +#ifdef DEBUG + printf("...NO.\n"); +#endif + return 0; + } + + + return el; +} + +static int lookup_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); + lookup.key = keybuf; + el = ast_hashtab_lookup(glob_hashtab, &lookup); + els_lookedup++; + if (el) + els_found++; + if (el) + return 1; + return 0; +} + + +static void *hashtest(void *data) +{ + int my_els_removed = 0; + int my_els_added = 0; + int my_els_lookedup = 0; + int my_els_found = 0; + int my_els_traversals = 0; + int my_testno = testno++; + int its; + + /* data will be a random number == use as a seed for random numbers */ + unsigned long seed = (unsigned long)data; + + printf("hashtest thread created... test beginning\n"); + + /* main test routine-- a global hashtab exists, pound it like crazy */ + for(its=0;its<100000;its++) + { + void *seed2 = &seed; + int op = my_rand(0,100, seed2); + if (op<60) { + my_els_lookedup++; +#ifdef DEBUG + printf("%d[%d]: LOOKUP\n", my_testno, its); +#endif + if ((my_els_lookedup%1000)==0) { + printf("."); + fflush(stdout); + } + if (lookup_element(seed2)) + my_els_found++; + } else if (op < 61) { /* make this 61 and it'll take 15 minutes to run */ +#ifdef DEBUG + printf("%d[%d]: TRAVERSE\n", my_testno, its); +#endif + traverse_elements(); + my_els_traversals++; + + } else if (op < 80) { +#ifdef DEBUG + printf("%d[%d]: REMOVE\n", my_testno, its); +#endif + if (del_element(seed2)) + my_els_removed++; + } else { + my_els_added++; +#ifdef DEBUG + printf("%d[%d]: ADD\n", my_testno, its); +#endif + add_element(); + } + } + printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", + my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals); + printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", + els_found, els_lookedup, els_added, els_removed,els_traversals); + pthread_exit(0); +} + +static void run_hashtest(int numthr) +{ + pthread_t thr[numthr]; + void *thrres[numthr]; + int i, biggest, resize_cnt, numobjs, numbuckets; + + /* init a single global hashtab, then... */ + glob_hashtab = ast_hashtab_create(180000, hashtab_compare_strings_nocase, ast_hashtab_resize_java, ast_hashtab_newsize_java, hashtab_hash_string_nocase, 1); + printf("starting with %d elements in the hashtable...\n", ast_hashtab_capacity(glob_hashtab)); + /* set a random seed */ + glob_seed = (unsigned int)time(0); + srand(glob_seed); + + /* create threads, each running hashtest */ + for(i=0;i 2 || atoi(argv[1]) < 1) + { + printf("Usage: hashtest \n"); + exit(1); + } + + /* one arg == number of threads to create */ + run_hashtest(atoi(argv[1])); + + return 0; +} +#if !defined(LOW_MEMORY) +int ast_add_profile(const char *x, uint64_t scale) +{ + return 0; +} +#endif + +int ast_loader_register(int (*updater)(void)) +{ + return 1; +} + +int ast_loader_unregister(int (*updater)(void)) +{ + return 1; +} +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + + +void ast_register_file_version(const char *file, const char *version); +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file); +void ast_unregister_file_version(const char *file) +{ + +} + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void __ast_verbose(const char *file, int line, const char *func, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("VERBOSE: "); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void ast_register_thread(char *name) +{ + +} + +void ast_unregister_thread(void *id) +{ +} + +#ifdef HAVE_BKTR +struct ast_bt *ast_bt_create(void); +struct ast_bt *ast_bt_create(void) +{ + return NULL; +} + +int ast_bt_get_addresses(struct ast_bt *bt); +int ast_bt_get_addresses(struct ast_bt *bt) +{ + return 0; +} + +char **ast_bt_get_symbols(void **addresses, size_t num_frames); +char **ast_bt_get_symbols(void **addresses, size_t num_frames) +{ + char **foo = calloc(num_frames, sizeof(char *) + 1); + if (foo) { + int i; + for (i = 0; i < num_frames; i++) { + foo[i] = (char *) foo + sizeof(char *) * num_frames; + } + } + return foo; +} + +void *ast_bt_destroy(struct ast_bt *bt); +void *ast_bt_destroy(struct ast_bt *bt) +{ + return NULL; +} +#endif diff --git a/utils/hashtest2.c b/utils/hashtest2.c new file mode 100644 index 0000000000..5bc6280615 --- /dev/null +++ b/utils/hashtest2.c @@ -0,0 +1,418 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007, Steve Murphy + * + * Steve Murphy + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ +/*! \file + * + * \brief A program to thoroughly thrash a hash table, testing + * out locking safety, and making sure all functionality + * is functioning. Run with 5 or more threads to get that + * fully intense firestorm of activity. If your + * hash tables don't crash, lock up, or go weird, it must + * be good code! Even features some global counters + * that will get slightly behind because they aren't lock-protected. + * + * \author Steve Murphy + */ + +/*** MODULEINFO + extended + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include +#include +#include + +#include "asterisk/lock.h" +#include "asterisk/astobj2.h" +#include "asterisk/channel.h" +#include "asterisk/utils.h" +#include "asterisk/module.h" + +int testno = 2; + +/* stuff we need to make this work with the astobj2 stuff */ +#if !defined(LOW_MEMORY) +int64_t ast_mark(int prof_id, int x) +{ + return 0; +} +#endif + +/* my OBJECT */ +struct ht_element +{ + char *key; + char *val; +}; + +char *pbx_substitute_variables_helper_full(struct ast_channel *chan, struct varshead *head, const char *cp1, char *cp2, int maxlen, size_t *used); +char *pbx_substitute_variables_helper_full(struct ast_channel *chan, struct varshead *head, const char *cp1, char *cp2, int maxlen, size_t *used) +{ + return NULL; +} + +static int hash_string(const void *obj, const int flags) +{ + char *str = ((struct ht_element*)obj)->key; + int total; + + for (total=0; *str; str++) + { + unsigned int tmp = total; + total <<= 1; /* multiply by 2 */ + total += tmp; /* multiply by 3 */ + total <<= 2; /* multiply by 12 */ + total += tmp; /* multiply by 13 */ + + total += ((unsigned int)(*str)); + } + if (total < 0) + total = -total; + return total; +} + +static int hashtab_compare_strings(void *a, void *b, int flags) +{ + const struct ht_element *ae = a, *be = b; + return !strcmp(ae->key, be->key) ? CMP_MATCH | CMP_STOP : 0; +} + +/* random numbers */ + +static int my_rand(int incl_low, int incl_high, unsigned int *seedp) +{ + if (incl_high == 0) + return 0; + + return incl_low + (rand_r(seedp) % incl_high); +} + + + + +/* the testing routines */ + +static int glob_highwater = 0; +struct ao2_container *glob_hashtab = 0; +unsigned int glob_seed = 0; +int els_removed = 0; +int els_added = 0; +int els_lookedup = 0; +int els_found = 0; +int els_traversals = 0; + +/* all the operations to perform on the hashtab */ + +static void ht_destroy(void *obj) +{ + const struct ht_element *o = obj; + if (o->key) + free(o->key); + if (o->val) + free(o->val); +} + + +static void add_element(void) +{ + char keybuf[100]; + struct ht_element *x = ao2_alloc(sizeof(struct ht_element), ht_destroy); + sprintf(keybuf,"key%08d", glob_highwater++); + x->key = strdup(keybuf); + x->val = strdup("interesting data"); +#ifdef DEBUG + printf("+ %s\n", keybuf); +#endif + ao2_link(glob_hashtab, x); + + els_added++; /* unprotected, sometimes off, but, not really important, either */ +} + +static int do_nothing_cb(void *obj, void *arg, int flags) +{ + return 0; +} + +static void traverse_elements(void) +{ +#ifdef DEBUG + printf("Traverse hashtab\n"); +#endif + ao2_callback(glob_hashtab, OBJ_NODATA, do_nothing_cb, NULL); + els_traversals++; /* unprotected, sometimes off, but, not really important, either */ +} + +static void * del_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); +#ifdef DEBUG + printf("- %s", keybuf); +#endif + lookup.key = keybuf; + el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); + if (el) { +#ifdef DEBUG + printf("...YES (el=%x)\n", (unsigned long)el); +#endif + ao2_unlink(glob_hashtab, el); /* mistakenly tried to use ao2_ref(c,-2) here to unlink. Bad Boy! */ + els_removed++; + } else { +#ifdef DEBUG + printf("...NO.\n"); +#endif + return 0; + } + return el; +} + +static int lookup_element(unsigned int *seedp) +{ + char keybuf[100]; + struct ht_element *el, lookup; + int x; + + /* pick a random element from 0 to highwater-1 */ + x = my_rand(0,glob_highwater-1,seedp); + sprintf(keybuf, "key%08d", x); + lookup.key = keybuf; + el = ao2_find(glob_hashtab, &lookup, OBJ_POINTER); + els_lookedup++; + if (el) { + els_found++; + ao2_ref(el, -1); /* toss out this ref, no longer needed */ + return 1; + } else { + return 0; + } +} + + +static void *hashtest(void *data) +{ + int my_els_removed = 0; + int my_els_added = 0; + int my_els_lookedup = 0; + int my_els_found = 0; + int my_els_traversals = 0; + int my_testno = testno++; + int its; + + /* data will be a random number == use as a seed for random numbers */ + unsigned long seed = (unsigned long)data; + printf("hashtest thread created... test beginning\n"); + + /* main test routine-- a global hashtab exists, pound it like crazy */ + for(its=0;its<100000;its++) + { + void *seed2 = &seed; + int op = my_rand(0,100, seed2); + if (op<60) { + my_els_lookedup++; +#ifdef DEBUG + printf("%d[%d]: LOOKUP\n", my_testno, its); +#endif + if ((my_els_lookedup%1000)==0) { + printf("."); + fflush(stdout); + } + if (lookup_element(seed2)) + my_els_found++; + } else if (op < 61) { /* make this 61 and it'll take 16 minutes to run */ +#ifdef DEBUG + printf("%d[%d]: TRAVERSE\n", my_testno, its); +#endif + traverse_elements(); + my_els_traversals++; + + } else if (op < 80) { +#ifdef DEBUG + printf("%d[%d]: REMOVE\n", my_testno, its); +#endif + if (del_element(seed2)) + my_els_removed++; + } else { + my_els_added++; +#ifdef DEBUG + printf("%d[%d]: ADD\n", my_testno, its); +#endif + add_element(); + } + } + printf("\nhashtest thread %d exiting.... lookups=%d/%d, added=%d, removed=%d, traversals=%d;\n", + my_testno, my_els_found, my_els_lookedup, my_els_added, my_els_removed, my_els_traversals); + printf("\ntotals..................... lookups=%d/%d, added=%d, removed=%d; traversals=%d\n", + els_found, els_lookedup, els_added, els_removed, els_traversals); + pthread_exit(0); + return NULL; +} + +static void run_hashtest(int numthr) +{ + pthread_t thr[numthr]; + void *thrres[numthr]; + int i; + + /* init a single global hashtab, then... */ + glob_hashtab = ao2_container_alloc(180000, hash_string, hashtab_compare_strings); + + /* set a random seed */ + glob_seed = (unsigned int)time(0); + srand(glob_seed); + + /* create threads, each running hashtest */ + for(i=0;i 2 || atoi(argv[1]) < 1) + { + printf("Usage: hashtest \n"); + exit(1); + } + + /* one arg == number of threads to create */ + run_hashtest(atoi(argv[1])); + + return 0; +} + +#if !defined(LOW_MEMORY) +int ast_add_profile(const char *x, uint64_t scale) +{ + return 0; +} +#endif + +int ast_loader_register(int (*updater)(void)) +{ + return 1; +} + +int ast_loader_unregister(int (*updater)(void)) +{ + return 1; +} +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + + +void ast_register_file_version(const char *file, const char *version); +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file); +void ast_unregister_file_version(const char *file) +{ + +} + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void __ast_verbose(const char *file, int line, const char *func, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("VERBOSE: "); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +void ast_register_thread(char *name) +{ + +} + +void ast_unregister_thread(void *id) +{ +} + +#ifdef HAVE_BKTR +struct ast_bt* ast_bt_create(void) +{ + return NULL; +} + +int ast_bt_get_addresses(struct ast_bt *bt) +{ + return -1; +} + +char **ast_bt_get_symbols(void **addresses, size_t num_frames) +{ + char **foo = calloc(num_frames, sizeof(char *) + 1); + if (foo) { + int i; + for (i = 0; i < num_frames; i++) { + foo[i] = (char *) foo + sizeof(char *) * num_frames; + } + } + return foo; +} + +void *ast_bt_destroy(struct ast_bt *bt) +{ + return NULL; +} +#endif diff --git a/utils/utils.xml b/utils/utils.xml index 58d2932e4d..a9b6e28ca1 100644 --- a/utils/utils.xml +++ b/utils/utils.xml @@ -32,6 +32,14 @@ no extended + + no + extended + + + no + extended + no extended