47 #define MAX_HASH_ENTRIES 15000
52 #define HASH_BUCKETS 151
54 #define COUNT_SLEEP_US 500
55 #define MAX_TEST_SECONDS 60
72 static int is_timed_out(
struct hash_test const *data) {
77 static void ht_delete(
void *obj)
83 static char *ht_new(
int i)
85 const int buflen = 12;
86 char *keybuf = ao2_alloc(buflen, ht_delete);
91 needed = snprintf(keybuf, buflen,
"key%08x", (
unsigned)i);
93 ast_assert(needed + 1 <= buflen);
98 static void *hash_test_grow(
void *d)
103 for (i = 0; i < data->
max_grow; ++i) {
105 if (is_timed_out(data)) {
106 printf(
"Growth timed out at %d\n", i);
107 return "Growth timed out";
111 return "Allocation failed";
121 static void *hash_test_lookup(
void *d)
125 unsigned seed = time(NULL);
135 if (is_timed_out(data)) {
136 return "Lookup timed out";
146 i = rand_r(&seed) % max;
149 return "Allocation failed";
154 if (from_ao2 == NULL) {
155 return "Key unexpectedly missing";
163 static void *hash_test_shrink(
void *d)
168 for (i = 1; i < data->
preload; ++i) {
169 char *obj = ht_new(-i);
173 return "Allocation failed";
181 return "Could not find object to delete";
184 if (is_timed_out(data)) {
185 return "Shrink timed out";
193 static int increment_count(
void *obj,
void *arg,
int flags) {
196 if (strncmp(ht,
"key0", 4) == 0) {
203 static void *hash_test_count(
void *d)
214 if (last_count == count) {
216 usleep(COUNT_SLEEP_US);
217 }
else if (last_count > count) {
219 return "ao2 container unexpectedly shrank";
222 if (is_timed_out(data)) {
223 return "Count timed out";
231 static int hash_string(
const void *obj,
const int flags)
236 static int compare_strings(
void *lhs,
void *rhs,
int flags)
238 const char *lhs_str = lhs;
239 const char *rhs_str = rhs;
240 if (strcasecmp(lhs_str, rhs_str) == 0) {
249 enum ast_test_result_state res = AST_TEST_PASS;
251 pthread_t grow_thread, count_thread, lookup_thread, shrink_thread;
252 void *thread_results;
257 info->name =
"thrash";
258 info->category =
"/main/astobj2/";
259 info->summary =
"Testing astobj2 container concurrency";
260 info->description =
"Test astobj2 container concurrency correctness.";
261 return AST_TEST_NOT_RUN;
266 ast_test_status_update(
test,
"Executing hash concurrency test...\n");
267 data.
preload = MAX_HASH_ENTRIES / 2;
271 HASH_BUCKETS, hash_string, NULL, compare_strings);
274 ast_test_status_update(
test,
"Allocation failed\n");
276 return AST_TEST_FAIL;
280 for (i = 1; i < data.
preload; ++i) {
281 char *ht = ht_new(-i);
283 ast_test_status_update(
test,
"Allocation failed\n");
285 return AST_TEST_FAIL;
292 ast_pthread_create(&grow_thread, NULL, hash_test_grow, &data);
294 ast_pthread_create(&count_thread, NULL, hash_test_count, &data);
296 ast_pthread_create(&lookup_thread, NULL, hash_test_lookup, &data);
298 ast_pthread_create(&shrink_thread, NULL, hash_test_shrink, &data);
300 pthread_join(grow_thread, &thread_results);
301 if (thread_results != NULL) {
302 ast_test_status_update(
test,
"Growth thread failed: %s\n",
303 (
char *)thread_results);
307 pthread_join(count_thread, &thread_results);
308 if (thread_results != NULL) {
309 ast_test_status_update(
test,
"Count thread failed: %s\n",
310 (
char *)thread_results);
314 pthread_join(lookup_thread, &thread_results);
315 if (thread_results != NULL) {
316 ast_test_status_update(
test,
"Lookup thread failed: %s\n",
317 (
char *)thread_results);
321 pthread_join(shrink_thread, &thread_results);
322 if (thread_results != NULL) {
323 ast_test_status_update(
test,
"Shrink thread failed: %s\n",
324 (
char *)thread_results);
329 ast_test_status_update(
test,
330 "Invalid ao2 container size. Expected: %d, Actual: %d\n",
339 ast_test_status_update(
test,
"Leaked %d objects!\n",
347 static int unload_module(
void)
353 static int load_module(
void)
359 AST_MODULE_INFO_STANDARD(
ASTERISK_GPL_KEY,
"astobj2 container thrash test");
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
Time-related functions and macros.
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Generic (perhaps overly so) hashtable implementation Hash Table support in Asterisk.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
static size_t alloc_count
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
#define AST_TEST_DEFINE(hdr)
struct ao2_container * to_be_thrashed
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
unsigned int ast_hashtab_hash_string_nocase(const void *obj)
Hashes a string to a number ignoring case.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
#define ao2_link(container, obj)
Add an object to a container.