Asterisk - The Open Source Telephony Project GIT-master-2de1a68
Data Structures | Macros | Functions | Variables
test_astobj2_thrash.c File Reference
#include "asterisk.h"
#include <pthread.h>
#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"
Include dependency graph for test_astobj2_thrash.c:

Go to the source code of this file.

Data Structures

struct  hash_test
 

Macros

#define COUNT_SLEEP_US   500
 
#define HASH_BUCKETS   151
 
#define MAX_HASH_ENTRIES   15000
 
#define MAX_TEST_SECONDS   60
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
 AST_TEST_DEFINE (hash_test)
 
static int compare_strings (void *lhs, void *rhs, int flags)
 
static int hash_string (const void *obj, const int flags)
 
static void * hash_test_count (void *d)
 
static void * hash_test_grow (void *d)
 Grow the hash data as specified. More...
 
static void * hash_test_lookup (void *d)
 
static void * hash_test_shrink (void *d)
 
static void ht_delete (void *obj)
 Free test element. More...
 
static char * ht_new (int i)
 Create test element. More...
 
static int increment_count (void *obj, void *arg, int flags)
 
static int is_timed_out (struct hash_test const *data)
 
static int load_module (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "astobj2 container thrash test" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, .support_level = AST_MODULE_SUPPORT_CORE, }
 
static int alloc_count = 0
 
static const struct ast_module_infoast_module_info = &__mod_info
 

Macro Definition Documentation

◆ COUNT_SLEEP_US

#define COUNT_SLEEP_US   500

Definition at line 54 of file test_astobj2_thrash.c.

◆ HASH_BUCKETS

#define HASH_BUCKETS   151

Definition at line 52 of file test_astobj2_thrash.c.

◆ MAX_HASH_ENTRIES

#define MAX_HASH_ENTRIES   15000

Definition at line 47 of file test_astobj2_thrash.c.

◆ MAX_TEST_SECONDS

#define MAX_TEST_SECONDS   60

Definition at line 55 of file test_astobj2_thrash.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 359 of file test_astobj2_thrash.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 359 of file test_astobj2_thrash.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 359 of file test_astobj2_thrash.c.

◆ AST_TEST_DEFINE()

AST_TEST_DEFINE ( hash_test  )

Definition at line 247 of file test_astobj2_thrash.c.

248{
250 struct hash_test data = {};
251 pthread_t grow_thread, count_thread, lookup_thread, shrink_thread;
252 void *thread_results;
253 int i;
254
255 switch (cmd) {
256 case TEST_INIT:
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;
262 case TEST_EXECUTE:
263 break;
264 }
265
266 ast_test_status_update(test, "Executing hash concurrency test...\n");
267 data.preload = MAX_HASH_ENTRIES / 2;
268 data.max_grow = MAX_HASH_ENTRIES - data.preload;
272
273 if (data.to_be_thrashed == NULL) {
274 ast_test_status_update(test, "Allocation failed\n");
275 /* Nothing needs to be freed; early return is fine */
276 return AST_TEST_FAIL;
277 }
278
279 /* preload with data to delete */
280 for (i = 1; i < data.preload; ++i) {
281 char *ht = ht_new(-i);
282 if (ht == NULL) {
283 ast_test_status_update(test, "Allocation failed\n");
284 ao2_ref(data.to_be_thrashed, -1);
285 return AST_TEST_FAIL;
286 }
287 ao2_link(data.to_be_thrashed, ht);
288 ao2_ref(ht, -1);
289 }
290
291 /* add data.max_grow entries to the ao2 container */
292 ast_pthread_create(&grow_thread, NULL, hash_test_grow, &data);
293 /* continually count the keys added by the grow thread */
294 ast_pthread_create(&count_thread, NULL, hash_test_count, &data);
295 /* continually lookup keys added by the grow thread */
296 ast_pthread_create(&lookup_thread, NULL, hash_test_lookup, &data);
297 /* delete all keys preloaded into the ao2 container */
298 ast_pthread_create(&shrink_thread, NULL, hash_test_shrink, &data);
299
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);
304 res = AST_TEST_FAIL;
305 }
306
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);
311 res = AST_TEST_FAIL;
312 }
313
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);
318 res = AST_TEST_FAIL;
319 }
320
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);
325 res = AST_TEST_FAIL;
326 }
327
328 if (ao2_container_count(data.to_be_thrashed) != data.max_grow) {
330 "Invalid ao2 container size. Expected: %d, Actual: %d\n",
332 res = AST_TEST_FAIL;
333 }
334
335 ao2_ref(data.to_be_thrashed, -1);
336
337 /* check for object leaks */
338 if (ast_atomic_fetchadd_int(&alloc_count, 0) != 0) {
339 ast_test_status_update(test, "Leaked %d objects!\n",
341 res = AST_TEST_FAIL;
342 }
343
344 return res;
345}
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#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.
Definition: astobj2.h:1303
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
def info(msg)
#define NULL
Definition: resample.c:96
struct timeval deadline
struct ao2_container * to_be_thrashed
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
static void * hash_test_lookup(void *d)
static void * hash_test_shrink(void *d)
#define MAX_TEST_SECONDS
static void * hash_test_count(void *d)
static int compare_strings(void *lhs, void *rhs, int flags)
static int hash_string(const void *obj, const int flags)
static char * ht_new(int i)
Create test element.
#define MAX_HASH_ENTRIES
static void * hash_test_grow(void *d)
Grow the hash data as specified.
#define HASH_BUCKETS
static int alloc_count
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:235
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:584

References alloc_count, AO2_ALLOC_OPT_LOCK_MUTEX, ao2_container_alloc_hash, ao2_container_count(), ao2_link, ao2_ref, ast_atomic_fetchadd_int(), ast_pthread_create, AST_TEST_FAIL, AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, ast_tv(), ast_tvadd(), ast_tvnow(), compare_strings(), hash_test::deadline, HASH_BUCKETS, hash_string(), hash_test_count(), hash_test_grow(), hash_test_lookup(), hash_test_shrink(), ht_new(), sip_to_pjsip::info(), hash_test::max_grow, MAX_HASH_ENTRIES, MAX_TEST_SECONDS, NULL, hash_test::preload, TEST_EXECUTE, TEST_INIT, and hash_test::to_be_thrashed.

◆ compare_strings()

static int compare_strings ( void *  lhs,
void *  rhs,
int  flags 
)
static

Definition at line 236 of file test_astobj2_thrash.c.

237{
238 const char *lhs_str = lhs;
239 const char *rhs_str = rhs;
240 if (strcasecmp(lhs_str, rhs_str) == 0) {
241 return CMP_MATCH | CMP_STOP;
242 } else {
243 return 0;
244 }
245}
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028

References CMP_MATCH, and CMP_STOP.

Referenced by AST_TEST_DEFINE().

◆ hash_string()

static int hash_string ( const void *  obj,
const int  flags 
)
static

Definition at line 231 of file test_astobj2_thrash.c.

232{
234}
unsigned int ast_hashtab_hash_string_nocase(const void *obj)
Hashes a string to a number ignoring case.
Definition: hashtab.c:181

References ast_hashtab_hash_string_nocase().

Referenced by AST_TEST_DEFINE().

◆ hash_test_count()

static void * hash_test_count ( void *  d)
static

Continuously iterate through all the entries in the hash

Definition at line 203 of file test_astobj2_thrash.c.

204{
205 const struct hash_test *data = d;
206 int count = 0;
207 int last_count = 0;
208
209 while (count < data->max_grow) {
210 last_count = count;
211 count = 0;
213
214 if (last_count == count) {
215 /* Allow other threads to run. */
216 usleep(COUNT_SLEEP_US);
217 } else if (last_count > count) {
218 /* Make sure the ao2 container never shrinks */
219 return "ao2 container unexpectedly shrank";
220 }
221
222 if (is_timed_out(data)) {
223 return "Count timed out";
224 }
225 }
226
227 /* Successfully iterated over all of the expected elements */
228 return NULL;
229}
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
static int is_timed_out(struct hash_test const *data)
static int increment_count(void *obj, void *arg, int flags)
#define COUNT_SLEEP_US
static struct test_val d

References ao2_callback, COUNT_SLEEP_US, d, increment_count(), is_timed_out(), hash_test::max_grow, NULL, OBJ_MULTIPLE, and hash_test::to_be_thrashed.

Referenced by AST_TEST_DEFINE().

◆ hash_test_grow()

static void * hash_test_grow ( void *  d)
static

Grow the hash data as specified.

Definition at line 98 of file test_astobj2_thrash.c.

99{
100 struct hash_test *data = d;
101 int i;
102
103 for (i = 0; i < data->max_grow; ++i) {
104 char *ht;
105 if (is_timed_out(data)) {
106 printf("Growth timed out at %d\n", i);
107 return "Growth timed out";
108 }
109 ht = ht_new(i);
110 if (ht == NULL) {
111 return "Allocation failed";
112 }
113 ao2_link(data->to_be_thrashed, ht);
114 ao2_ref(ht, -1);
116 }
117 return NULL;
118}

References ao2_link, ao2_ref, ast_atomic_fetchadd_int(), d, hash_test::grow_count, ht_new(), is_timed_out(), hash_test::max_grow, NULL, and hash_test::to_be_thrashed.

Referenced by AST_TEST_DEFINE().

◆ hash_test_lookup()

static void * hash_test_lookup ( void *  d)
static

Randomly lookup data in the hash

Definition at line 121 of file test_astobj2_thrash.c.

122{
123 struct hash_test *data = d;
124 int max;
125 unsigned seed = time(NULL);
126
127 /* ast_atomic_fetchadd_int provide a memory fence so that the optimizer doesn't
128 * optimize away reads.
129 */
130 while ((max = ast_atomic_fetchadd_int(&data->grow_count, 0)) < data->max_grow) {
131 int i;
132 char *obj;
133 char *from_ao2;
134
135 if (is_timed_out(data)) {
136 return "Lookup timed out";
137 }
138
139 if (max == 0) {
140 /* No data yet; yield and try again */
141 sched_yield();
142 continue;
143 }
144
145 /* Randomly lookup one object from the hash */
146 i = rand_r(&seed) % max;
147 obj = ht_new(i);
148 if (obj == NULL) {
149 return "Allocation failed";
150 }
151 from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_POINTER);
152 ao2_ref(obj, -1);
153 ao2_ref(from_ao2, -1);
154 if (from_ao2 == NULL) {
155 return "Key unexpectedly missing";
156 }
157 }
158
159 return NULL;
160}
#define OBJ_POINTER
Definition: astobj2.h:1150
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
#define max(a, b)
Definition: f2c.h:198

References ao2_find, ao2_ref, ast_atomic_fetchadd_int(), d, hash_test::grow_count, ht_new(), is_timed_out(), max, hash_test::max_grow, NULL, OBJ_POINTER, and hash_test::to_be_thrashed.

Referenced by AST_TEST_DEFINE().

◆ hash_test_shrink()

static void * hash_test_shrink ( void *  d)
static

Delete entries from the hash

Definition at line 163 of file test_astobj2_thrash.c.

164{
165 const struct hash_test *data = d;
166 int i;
167
168 for (i = 1; i < data->preload; ++i) {
169 char *obj = ht_new(-i);
170 char *from_ao2;
171
172 if (obj == NULL) {
173 return "Allocation failed";
174 }
175 from_ao2 = ao2_find(data->to_be_thrashed, obj, OBJ_UNLINK | OBJ_POINTER);
176
177 ao2_ref(obj, -1);
178 if (from_ao2) {
179 ao2_ref(from_ao2, -1);
180 } else {
181 return "Could not find object to delete";
182 }
183
184 if (is_timed_out(data)) {
185 return "Shrink timed out";
186 }
187 }
188
189 return NULL;
190}
@ OBJ_UNLINK
Definition: astobj2.h:1039

References ao2_find, ao2_ref, d, ht_new(), is_timed_out(), NULL, OBJ_POINTER, OBJ_UNLINK, hash_test::preload, and hash_test::to_be_thrashed.

Referenced by AST_TEST_DEFINE().

◆ ht_delete()

static void ht_delete ( void *  obj)
static

Free test element.

Definition at line 77 of file test_astobj2_thrash.c.

References alloc_count, and ast_atomic_fetchadd_int().

Referenced by ht_new().

◆ ht_new()

static char * ht_new ( int  i)
static

Create test element.

Definition at line 83 of file test_astobj2_thrash.c.

84{
85 const int buflen = 12;
86 char *keybuf = ao2_alloc(buflen, ht_delete);
87 int needed;
88 if (keybuf == NULL) {
89 return NULL;
90 }
91 needed = snprintf(keybuf, buflen, "key%08x", (unsigned)i);
93 ast_assert(needed + 1 <= buflen);
94 return keybuf;
95}
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static void ht_delete(void *obj)
Free test element.
#define ast_assert(a)
Definition: utils.h:739

References alloc_count, ao2_alloc, ast_assert, ast_atomic_fetchadd_int(), ht_delete(), and NULL.

Referenced by AST_TEST_DEFINE(), hash_test_grow(), hash_test_lookup(), and hash_test_shrink().

◆ increment_count()

static int increment_count ( void *  obj,
void *  arg,
int  flags 
)
static

ao2_callback for hash_test_count

Definition at line 193 of file test_astobj2_thrash.c.

193 {
194 char *ht = obj;
195 int *count = arg;
196 if (strncmp(ht, "key0", 4) == 0) {
197 ++(*count);
198 }
199 return 0;
200}

Referenced by hash_test_count().

◆ is_timed_out()

static int is_timed_out ( struct hash_test const *  data)
static

Definition at line 72 of file test_astobj2_thrash.c.

72 {
73 return ast_tvdiff_us(data->deadline, ast_tvnow()) < 0;
74}
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:87

References ast_tvdiff_us(), ast_tvnow(), and hash_test::deadline.

Referenced by hash_test_count(), hash_test_grow(), hash_test_lookup(), and hash_test_shrink().

◆ load_module()

static int load_module ( void  )
static

Definition at line 353 of file test_astobj2_thrash.c.

354{
357}
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
#define AST_TEST_REGISTER(cb)
Definition: test.h:127

References AST_MODULE_LOAD_SUCCESS, and AST_TEST_REGISTER.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 347 of file test_astobj2_thrash.c.

348{
350 return 0;
351}
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128

References AST_TEST_UNREGISTER.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "astobj2 container thrash test" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, .support_level = AST_MODULE_SUPPORT_CORE, }
static

Definition at line 359 of file test_astobj2_thrash.c.

◆ alloc_count

int alloc_count = 0
static

Definition at line 70 of file test_astobj2_thrash.c.

Referenced by AST_TEST_DEFINE(), ht_delete(), and ht_new().

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 359 of file test_astobj2_thrash.c.