42 #define TEST_THRASH_TIME 3
45 #define TEST_THRASH_RETRIEVERS 25
48 #define TEST_THRASH_UPDATERS 25
107 const char *type,
const char *
id)
141 static void sorcery_memory_cache_thrash_destroy(
void *obj)
174 unsigned int update_threads,
unsigned int retrieve_threads,
unsigned int unique_objects)
180 thrash = ao2_alloc_options(
sizeof(*thrash), sorcery_memory_cache_thrash_destroy,
198 !strcmp(cache_configuration,
"default") ?
"" : cache_configuration, 1);
215 thread->
thread = AST_PTHREADT_NULL;
237 static void *sorcery_memory_cache_thrash_update(
void *data)
240 struct timeval start;
241 unsigned int object_id;
242 char object_id_str[AST_UUID_STR_LEN];
245 while (!thread->
stop) {
247 snprintf(object_id_str,
sizeof(object_id_str),
"%u", object_id);
250 ast_assert(
object != NULL);
267 static void *sorcery_memory_cache_thrash_retrieve(
void *data)
270 struct timeval start;
271 unsigned int object_id;
272 char object_id_str[AST_UUID_STR_LEN];
275 while (!thread->
stop) {
277 snprintf(object_id_str,
sizeof(object_id_str),
"%u", object_id);
282 ast_assert(
object != NULL);
304 if (thread->
thread == AST_PTHREADT_NULL) {
315 if (thread->
thread == AST_PTHREADT_NULL) {
319 pthread_join(thread->
thread, NULL);
321 if (idx < thrash->update_threads) {
354 if (ast_pthread_create(&thread->
thread, NULL, idx < thrash->update_threads ?
355 sorcery_memory_cache_thrash_update : sorcery_memory_cache_thrash_retrieve, thread)) {
356 sorcery_memory_cache_thrash_stop(thrash);
375 e->
command =
"sorcery memory cache thrash";
377 "Usage: sorcery memory cache thrash <cache configuration> <amount of time to thrash the cache> <number of unique objects> <number of retrieve threads> <number of update threads>\n"
378 " Create a sorcery instance with a memory cache using the provided configuration and thrash it.\n";
385 return CLI_SHOWUSAGE;
388 if (sscanf(a->argv[5],
"%30u", &thrash_time) != 1) {
389 ast_cli(a->fd,
"An invalid value of '%s' has been provided for the thrashing time\n", a->argv[5]);
391 }
else if (sscanf(a->argv[6],
"%30u", &unique_objects) != 1) {
392 ast_cli(a->fd,
"An invalid value of '%s' has been provided for number of unique objects\n", a->argv[6]);
394 }
else if (sscanf(a->argv[7],
"%30u", &retrieve_threads) != 1) {
395 ast_cli(a->fd,
"An invalid value of '%s' has been provided for the number of retrieve threads\n", a->argv[7]);
397 }
else if (sscanf(a->argv[8],
"%30u", &update_threads) != 1) {
398 ast_cli(a->fd,
"An invalid value of '%s' has been provided for the number of update threads\n", a->argv[8]);
402 thrash = sorcery_memory_cache_thrash_create(a->argv[4], update_threads, retrieve_threads, unique_objects);
404 ast_cli(a->fd,
"Could not create a sorcery memory cache thrash test using the provided arguments\n");
408 ast_cli(a->fd,
"Starting cache thrash test.\n");
409 ast_cli(a->fd,
"Memory cache configuration: %s\n", a->argv[4]);
410 ast_cli(a->fd,
"Amount of time to perform test: %u seconds\n", thrash_time);
411 ast_cli(a->fd,
"Number of unique objects: %u\n", unique_objects);
412 ast_cli(a->fd,
"Number of retrieve threads: %u\n", retrieve_threads);
413 ast_cli(a->fd,
"Number of update threads: %u\n", update_threads);
415 sorcery_memory_cache_thrash_start(thrash);
416 while ((thrash_time = sleep(thrash_time)));
417 sorcery_memory_cache_thrash_stop(thrash);
419 ast_cli(a->fd,
"Stopped cache thrash test\n");
430 AST_CLI_DEFINE(sorcery_memory_cache_cli_thrash,
"Thrash a sorcery memory cache"),
447 static enum ast_test_result_state nominal_thrash(
struct ast_test *
test,
const char *cache_configuration,
448 unsigned int thrash_time,
unsigned int unique_objects,
unsigned int retrieve_threads,
449 unsigned int update_threads)
453 thrash = sorcery_memory_cache_thrash_create(cache_configuration, update_threads, retrieve_threads, unique_objects);
455 return AST_TEST_FAIL;
458 sorcery_memory_cache_thrash_start(thrash);
459 while ((thrash_time = sleep(thrash_time)));
460 sorcery_memory_cache_thrash_stop(thrash);
464 return AST_TEST_PASS;
471 info->name =
"low_unique_object_count_immediately_stale";
472 info->category =
"/res/res_sorcery_memory_cache/thrash/";
473 info->summary =
"Thrash a cache with low number of unique objects that are immediately stale";
474 info->description =
"This test creates a cache with objects that are stale\n"
475 "after 1 second. It also creates 25 threads which are constantly attempting\n"
476 "to retrieve the objects. This test confirms that the background refreshes\n"
477 "being done as a result of going stale do not conflict or cause problems with\n"
478 "the large number of retrieve threads.";
479 return AST_TEST_NOT_RUN;
491 info->name =
"low_unique_object_count_immediately_expire";
492 info->category =
"/res/res_sorcery_memory_cache/thrash/";
493 info->summary =
"Thrash a cache with low number of unique objects that are immediately expired";
494 info->description =
"This test creates a cache with objects that are expired\n"
495 "after 1 second. It also creates 25 threads which are constantly attempting\n"
496 "to retrieve the objects. This test confirms that the expiration process does\n"
497 "not cause a problem as the retrieve threads execute.";
498 return AST_TEST_NOT_RUN;
510 info->name =
"low_unique_object_count_high_concurrent_updates";
511 info->category =
"/res/res_sorcery_memory_cache/thrash/";
512 info->summary =
"Thrash a cache with low number of unique objects that are updated frequently";
513 info->description =
"This test creates a cache with objects that are being constantly\n"
514 "updated and retrieved at the same time. This will create contention between all\n"
515 "of the threads as the write lock is held for the updates. This test confirms that\n"
516 "no problems occur in this situation.";
517 return AST_TEST_NOT_RUN;
529 info->name =
"unique_objects_exceeding_maximum";
530 info->category =
"/res/res_sorcery_memory_cache/thrash/";
531 info->summary =
"Thrash a cache with a fixed maximum object count";
532 info->description =
"This test creates a cache with a maximum number of objects\n"
533 "allowed in it. The maximum number of unique objects, however, far exceeds the\n"
534 "the maximum number allowed in the cache. This test confirms that the cache does\n"
535 "not exceed the maximum and that the removal of older objects does not cause\n"
537 return AST_TEST_NOT_RUN;
545 AST_TEST_DEFINE(unique_objects_exceeding_maximum_with_expire_and_stale)
549 info->name =
"unique_objects_exceeding_maximum_with_expire_and_stale";
550 info->category =
"/res/res_sorcery_memory_cache/thrash/";
551 info->summary =
"Thrash a cache with a fixed maximum object count with objects that expire and go stale";
552 info->description =
"This test creates a cache with a maximum number of objects\n"
553 "allowed in it with objects that also go stale after a period of time and expire.\n"
554 "A number of threads are created that constantly retrieve from the cache, causing\n"
555 "both stale refresh and expiration to occur. This test confirms that the combination\n"
556 "of these do not present a problem.";
557 return AST_TEST_NOT_RUN;
562 return nominal_thrash(test,
"maximum_objects=10,object_lifetime_maximum=2,object_lifetime_stale=1",
570 info->name =
"conflicting_expire_and_stale";
571 info->category =
"/res/res_sorcery_memory_cache/thrash/";
572 info->summary =
"Thrash a cache with a large number of objects that expire and go stale";
573 info->description =
"This test creates a cache with a large number of objects that expire\n"
574 "and go stale. As there is such a large number this ensures that both operations occur.\n"
575 "This test confirms that stale refreshing and expiration do not conflict.";
576 return AST_TEST_NOT_RUN;
581 return nominal_thrash(test,
"object_lifetime_maximum=2,object_lifetime_stale=1",
TEST_THRASH_TIME * 2, 5000,
589 info->name =
"high_object_count_without_expiration";
590 info->category =
"/res/res_sorcery_memory_cache/thrash/";
591 info->summary =
"Thrash a cache with a large number of objects";
592 info->description =
"This test creates a cache with a large number of objects that persist.\n"
593 "A large number of threads are created which constantly retrieve from the cache.\n"
594 "This test confirms that the large number of retrieves do not cause a problem.";
595 return AST_TEST_NOT_RUN;
603 static int unload_module(
void)
606 AST_TEST_UNREGISTER(low_unique_object_count_immediately_stale);
607 AST_TEST_UNREGISTER(low_unique_object_count_immediately_expire);
608 AST_TEST_UNREGISTER(low_unique_object_count_high_concurrent_updates);
609 AST_TEST_UNREGISTER(unique_objects_exceeding_maximum);
610 AST_TEST_UNREGISTER(unique_objects_exceeding_maximum_with_expire_and_stale);
611 AST_TEST_UNREGISTER(conflicting_expire_and_stale);
612 AST_TEST_UNREGISTER(high_object_count_without_expiration);
617 static int load_module(
void)
620 AST_TEST_REGISTER(low_unique_object_count_immediately_stale);
621 AST_TEST_REGISTER(low_unique_object_count_immediately_expire);
622 AST_TEST_REGISTER(low_unique_object_count_high_concurrent_updates);
623 AST_TEST_REGISTER(unique_objects_exceeding_maximum);
624 AST_TEST_REGISTER(unique_objects_exceeding_maximum_with_expire_and_stale);
625 AST_TEST_REGISTER(conflicting_expire_and_stale);
626 AST_TEST_REGISTER(high_object_count_without_expiration);
631 AST_MODULE_INFO_STANDARD(
ASTERISK_GPL_KEY,
"Sorcery Cache Thrasing test module");
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
unsigned int update_threads
The number of threads which are updating.
struct ast_sorcery * sorcery
Sorcery instance being tested.
#define TEST_THRASH_RETRIEVERS
The number of threads to use for retrieving for applicable tests.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching)
Apply additional object wizard mappings.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
struct sorcery_memory_cache_thrash::@512 threads
Threads which are updating or reading from the cache.
descriptor for a cli entry.
static int mock_update(const struct ast_sorcery *sorcery, void *data, void *object)
Callback for updating a sorcery object.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Full structure for sorcery.
#define SORCERY_OBJECT(details)
Macro which must be used at the beginning of each sorcery capable object.
Structure for a memory cache thras thread.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
const char * name
Name of the wizard.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
unsigned int average_retrieve_execution_time
The average execution time of sorcery retrieve operations.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
unsigned int average_execution_time
Average time spent executing sorcery operation in this thread.
static void * test_data_alloc(const char *id)
Allocation callback for test_data sorcery object.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
static void * mock_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
Callback for retrieving sorcery object by ID.
pthread_t thread
The thread thrashing the cache.
unsigned int unique_objects
The number of unique objects we should restrict ourself to.
struct ast_sorcery * sorcery
The sorcery instance being tested.
#define AST_VECTOR(name, type)
Define a vector structure.
Structure for memory cache thrashing.
#define ast_sorcery_wizard_register(interface)
See __ast_sorcery_wizard_register()
#define ast_sorcery_internal_object_register(sorcery, type, alloc, transform, apply)
Register an internal, hidden object type.
int ast_sorcery_wizard_unregister(const struct ast_sorcery_wizard *interface)
Unregister a sorcery wizard.
Sorcery object created based on backend data.
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
#define ast_calloc(num, len)
A wrapper for calloc()
Support for logging to various files, console and syslog Configuration in file logger.conf.
Vector container support.
#define TEST_THRASH_UPDATERS
The number of threads to use for updating for applicable tests.
unsigned int retrieve_threads
The number of threads which are retrieving.
Interface for a sorcery wizard.
unsigned int stop
Set when the thread should stop.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Standard Command Line Interface.
unsigned int average_update_execution_time
The average execution time of sorcery update operations.
#define ast_sorcery_open()
Open a new sorcery structure.
#define AST_TEST_DEFINE(hdr)
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
static struct ast_sorcery_wizard mock_wizard
A mock sorcery wizard used for the stale test.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
#define TEST_THRASH_TIME
The default amount of time (in seconds) that thrash unit tests execute for.
Sorcery Data Access Layer API.
int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
Update an object.