Asterisk - The Open Source Telephony Project  18.5.0
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)
 
static void * hash_test_lookup (void *d)
 
static void * hash_test_shrink (void *d)
 
static void ht_delete (void *obj)
 
static char * ht_new (int i)
 
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.

Referenced by hash_test_count().

◆ HASH_BUCKETS

#define HASH_BUCKETS   151

Definition at line 52 of file test_astobj2_thrash.c.

Referenced by AST_TEST_DEFINE().

◆ MAX_HASH_ENTRIES

#define MAX_HASH_ENTRIES   15000

Definition at line 47 of file test_astobj2_thrash.c.

Referenced by AST_TEST_DEFINE().

◆ MAX_TEST_SECONDS

#define MAX_TEST_SECONDS   60

Definition at line 55 of file test_astobj2_thrash.c.

Referenced by AST_TEST_DEFINE().

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.

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.

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 MAX_HASH_ENTRIES
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
static int alloc_count
static int compare_strings(void *lhs, void *rhs, int flags)
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define MAX_TEST_SECONDS
#define NULL
Definition: resample.c:96
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:755
static int hash_string(const void *obj, const int flags)
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
static void * hash_test_grow(void *d)
#define HASH_BUCKETS
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
def info(msg)
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2283
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:559
static void * hash_test_lookup(void *d)
static char * ht_new(int i)
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:226
struct ao2_container * to_be_thrashed
static void * hash_test_shrink(void *d)
struct timeval deadline
ast_test_result_state
Definition: test.h:200
static void * hash_test_count(void *d)
#define ao2_link(container, obj)
Definition: astobj2.h:1549

◆ compare_strings()

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

Definition at line 236 of file test_astobj2_thrash.c.

References CMP_MATCH, and CMP_STOP.

Referenced by AST_TEST_DEFINE().

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 }

◆ hash_string()

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

Definition at line 231 of file test_astobj2_thrash.c.

References ast_hashtab_hash_string_nocase().

Referenced by AST_TEST_DEFINE().

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

◆ 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.

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().

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 }
static struct test_val d
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
#define NULL
Definition: resample.c:96
#define COUNT_SLEEP_US
struct ao2_container * to_be_thrashed
static int increment_count(void *obj, void *arg, int flags)
static int is_timed_out(struct hash_test const *data)

◆ hash_test_grow()

static void* hash_test_grow ( void *  d)
static

/brief Grow the hash data as specified

Definition at line 98 of file test_astobj2_thrash.c.

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().

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 }
static struct test_val d
#define NULL
Definition: resample.c:96
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:755
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static char * ht_new(int i)
struct ao2_container * to_be_thrashed
static int is_timed_out(struct hash_test const *data)
#define ao2_link(container, obj)
Definition: astobj2.h:1549

◆ 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.

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().

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:1154
static struct test_val d
#define NULL
Definition: resample.c:96
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:755
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
static char * ht_new(int i)
struct ao2_container * to_be_thrashed
static int is_timed_out(struct hash_test const *data)
#define max(a, b)
Definition: f2c.h:198

◆ 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.

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().

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 }
#define OBJ_POINTER
Definition: astobj2.h:1154
static struct test_val d
#define NULL
Definition: resample.c:96
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
static char * ht_new(int i)
struct ao2_container * to_be_thrashed
static int is_timed_out(struct hash_test const *data)

◆ ht_delete()

static void ht_delete ( void *  obj)
static

/brief 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().

78 {
80 }
static int alloc_count
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:755

◆ ht_new()

static char* ht_new ( int  i)
static

/brief Create test element

Definition at line 83 of file test_astobj2_thrash.c.

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().

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 }
static int alloc_count
#define ast_assert(a)
Definition: utils.h:695
#define NULL
Definition: resample.c:96
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:755
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static void ht_delete(void *obj)

◆ 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.

Referenced by hash_test_count().

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

◆ is_timed_out()

static int is_timed_out ( struct hash_test const *  data)
static

Definition at line 72 of file test_astobj2_thrash.c.

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().

72  {
73  return ast_tvdiff_us(data->deadline, ast_tvnow()) < 0;
74 }
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
Definition: time.h:78

◆ load_module()

static int load_module ( void  )
static

Definition at line 353 of file test_astobj2_thrash.c.

References AST_MODULE_LOAD_SUCCESS, and AST_TEST_REGISTER.

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

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 347 of file test_astobj2_thrash.c.

References AST_TEST_UNREGISTER.

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

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.