154 #ifdef TEST_FRAMEWORK
156 unsigned int cache_notify;
162 unsigned int cache_completed;
198 static void *sorcery_memory_cache_open(
const char *data);
199 static int sorcery_memory_cache_create(
const struct ast_sorcery *sorcery,
void *data,
void *
object);
200 static void sorcery_memory_cache_load(
void *data,
const struct ast_sorcery *sorcery,
const char *type);
201 static void sorcery_memory_cache_reload(
void *data,
const struct ast_sorcery *sorcery,
const char *type);
202 static void *sorcery_memory_cache_retrieve_id(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
204 static void *sorcery_memory_cache_retrieve_fields(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
206 static void sorcery_memory_cache_retrieve_multiple(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
208 static void sorcery_memory_cache_retrieve_regex(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
210 static void sorcery_memory_cache_retrieve_prefix(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
211 struct ao2_container *objects,
const char *prefix,
const size_t prefix_len);
212 static int sorcery_memory_cache_delete(
const struct ast_sorcery *sorcery,
void *data,
void *
object);
213 static void sorcery_memory_cache_close(
void *data);
216 .
name =
"memory_cache",
217 .open = sorcery_memory_cache_open,
218 .create = sorcery_memory_cache_create,
219 .update = sorcery_memory_cache_create,
220 .delete = sorcery_memory_cache_delete,
221 .load = sorcery_memory_cache_load,
222 .reload = sorcery_memory_cache_reload,
223 .retrieve_id = sorcery_memory_cache_retrieve_id,
224 .retrieve_fields = sorcery_memory_cache_retrieve_fields,
225 .retrieve_multiple = sorcery_memory_cache_retrieve_multiple,
226 .retrieve_regex = sorcery_memory_cache_retrieve_regex,
227 .retrieve_prefix = sorcery_memory_cache_retrieve_prefix,
228 .close = sorcery_memory_cache_close,
232 #define CACHES_CONTAINER_BUCKET_SIZE 53
235 #define CACHE_CONTAINER_BUCKET_SIZE 53
238 #define CACHE_HEAP_INIT_HEIGHT 5
246 #define PASSTHRU_UPDATE_THREAD_ID 0x5EED1E55
249 static int is_passthru_update(
void)
251 uint32_t *passthru_update_thread_id;
254 sizeof(*passthru_update_thread_id));
255 if (!passthru_update_thread_id) {
259 return *passthru_update_thread_id == PASSTHRU_UPDATE_THREAD_ID;
262 static void set_passthru_update(uint32_t value)
264 uint32_t *passthru_update_thread_id;
267 sizeof(*passthru_update_thread_id));
268 if (!passthru_update_thread_id) {
269 ast_log(LOG_ERROR,
"Could not set passthru update ID for sorcery memory cache thread\n");
273 *passthru_update_thread_id = value;
276 static void start_passthru_update(
void)
278 set_passthru_update(PASSTHRU_UPDATE_THREAD_ID);
281 static void end_passthru_update(
void)
283 set_passthru_update(0);
295 static int sorcery_memory_cache_hash(
const void *obj,
int flags)
298 const char *
name = obj;
329 static int sorcery_memory_cache_cmp(
void *obj,
void *arg,
int flags)
333 const char *right_name = arg;
339 right_name = right->
name;
342 cmp = strcmp(left->
name, right_name);
345 cmp = strncmp(left->
name, right_name, strlen(right_name));
360 static int sorcery_memory_cached_object_hash(
const void *obj,
int flags)
363 const char *name = obj;
366 switch (flags & OBJ_SEARCH_MASK) {
394 static int sorcery_memory_cached_object_cmp(
void *obj,
void *arg,
int flags)
398 const char *right_name = arg;
401 switch (flags & OBJ_SEARCH_MASK) {
422 static void sorcery_memory_cache_destructor(
void *obj)
426 ast_free(cache->
name);
440 static void sorcery_memory_cached_object_destructor(
void *obj)
444 ao2_cleanup(cached->
object);
465 static int remove_from_cache(
struct sorcery_memory_cache *cache,
const char *
id,
int reschedule)
481 ast_assert(heap_object == hash_object);
485 if (reschedule && (oldest_object == heap_object)) {
486 schedule_cache_expiration(cache);
498 static int expire_objects_from_cache(
const void *data)
510 while (ao2_trywrlock(cache->
objects)) {
528 if (expiration > 0) {
535 schedule_cache_expiration(cache);
577 static int object_stale_callback(
void *obj,
void *arg,
int flags)
616 object_stale_callback(cached, cache, 0);
663 #ifdef TEST_FRAMEWORK
664 ast_mutex_lock(&cache->lock);
665 cache->cache_completed = 1;
666 ast_cond_signal(&cache->cond);
667 ast_mutex_unlock(&cache->lock);
701 if (!heap_old_object) {
704 hash_old_object = ao2_find(cache->
objects, heap_old_object,
707 ast_assert(heap_old_object == hash_old_object);
711 schedule_cache_expiration(cache);
743 ao2_find(cache->
objects, cached_object,
749 schedule_cache_expiration(cache);
771 cached = ao2_alloc(
sizeof(*cached), sorcery_memory_cached_object_destructor);
805 static int sorcery_memory_cache_create(
const struct ast_sorcery *sorcery,
void *data,
void *
object)
810 cached = sorcery_memory_cached_object_alloc(sorcery, cache,
object);
824 if (remove_oldest_from_cache(cache)) {
825 ast_log(LOG_ERROR,
"Unable to make room in cache for sorcery object '%s'.\n",
833 if (add_to_cache(cache, cached)) {
834 ast_log(LOG_ERROR,
"Unable to add object '%s' to the cache\n",
855 static int object_add_to_cache_callback(
void *obj,
void *arg,
void *data,
int flags)
860 cached = sorcery_memory_cached_object_alloc(arg, cache, obj);
865 add_to_cache(cache, cached);
877 static void stale_cache_update_task_data_destructor(
void *obj)
881 ao2_cleanup(task_data->cache);
883 ast_free(task_data->type);
891 task_data = ao2_alloc_options(
sizeof(*task_data), stale_cache_update_task_data_destructor,
897 task_data->sorcery =
ao2_bump(sorcery);
900 if (!task_data->type) {
908 static int stale_cache_update(
const void *data)
913 start_passthru_update();
916 end_passthru_update();
918 if (!backend_objects) {
925 ast_log(LOG_ERROR,
"The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
932 ao2_wrlock(task_data->cache->
objects);
933 remove_all_from_cache(task_data->cache);
935 task_data->sorcery, task_data->cache);
942 ast_log(LOG_WARNING,
"The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
944 remove_all_from_cache(task_data->cache);
947 ao2_unlock(task_data->cache->
objects);
962 static void stale_update_task_data_destructor(
void *obj)
966 ao2_cleanup(task_data->cache);
967 ao2_cleanup(task_data->object);
976 task_data = ao2_alloc_options(
sizeof(*task_data), stale_update_task_data_destructor,
982 task_data->sorcery =
ao2_bump(sorcery);
984 task_data->object =
ao2_bump(
object);
989 static int stale_item_update(
const void *data)
994 start_passthru_update();
1000 ast_debug(1,
"Backend no longer has object type '%s' ID '%s'. Removing from cache\n",
1003 sorcery_memory_cache_delete(task_data->sorcery, task_data->cache,
1006 ast_debug(1,
"Refreshing stale cache object type '%s' ID '%s'\n",
1009 sorcery_memory_cache_create(task_data->sorcery, task_data->cache,
1019 end_passthru_update();
1038 start_passthru_update();
1040 end_passthru_update();
1042 if (!backend_objects) {
1048 ast_log(LOG_ERROR,
"The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
1061 ast_log(LOG_WARNING,
"The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
1063 remove_all_from_cache(cache);
1085 memory_cache_populate(sorcery, type, cache);
1105 task_data = stale_cache_update_task_data_alloc((
struct ast_sorcery *) sorcery,
1109 stale_cache_update, task_data);
1112 ao2_cleanup(task_data);
1133 task_data = stale_update_task_data_alloc((
struct ast_sorcery *) sorcery,
1136 ast_debug(1,
"Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
1139 stale_item_update, task_data);
1142 ao2_cleanup(task_data);
1143 ast_log(LOG_ERROR,
"Unable to update stale cached object type '%s', ID '%s'.\n",
1161 struct timeval elapsed;
1177 memory_cache_stale_update_object(sorcery, cache, cached);
1203 memory_cache_stale_check_object(sorcery, cache, cached);
1219 static void *sorcery_memory_cache_retrieve_id(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
const char *
id)
1225 if (is_passthru_update()) {
1229 memory_cache_full_update(sorcery, type, cache);
1238 memory_cache_stale_check_object(sorcery, cache, cached);
1254 static int sorcery_memory_cache_fields_cmp(
void *obj,
void *arg,
int flags)
1260 if (params->
regex) {
1266 }
else if (params->
prefix) {
1271 }
else if (params->
fields &&
1299 static void *sorcery_memory_cache_retrieve_fields(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
1309 void *
object = NULL;
1318 memory_cache_stale_check_object(sorcery, cache, cached);
1336 static void sorcery_memory_cache_retrieve_multiple(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
1344 .container = objects,
1351 memory_cache_full_update(sorcery, type, cache);
1355 memory_cache_stale_check(sorcery, cache);
1369 static void sorcery_memory_cache_retrieve_regex(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
1377 .container = objects,
1378 .regex = &expression,
1381 if (is_passthru_update() || !cache->
full_backend_cache || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
1385 memory_cache_full_update(sorcery, type, cache);
1387 regfree(&expression);
1390 memory_cache_stale_check(sorcery, cache);
1404 static void sorcery_memory_cache_retrieve_prefix(
const struct ast_sorcery *sorcery,
void *data,
const char *type,
1411 .container = objects,
1420 memory_cache_full_update(sorcery, type, cache);
1424 memory_cache_stale_check(sorcery, cache);
1436 static void sorcery_memory_cache_load(
void *data,
const struct ast_sorcery *sorcery,
const char *type)
1441 if (ast_strlen_zero(cache->
name)) {
1446 ast_debug(1,
"Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
1461 static void sorcery_memory_cache_reload(
void *data,
const struct ast_sorcery *sorcery,
const char *type)
1470 remove_all_from_cache(cache);
1484 static int configuration_parse_unsigned_integer(
const char *value,
unsigned int *result)
1486 if (ast_strlen_zero(value) || !strncmp(value,
"-", 1)) {
1490 return sscanf(value,
"%30u", result);
1493 static int age_cmp(
void *a,
void *b)
1508 static void *sorcery_memory_cache_open(
const char *data)
1524 while (!ast_strlen_zero(options) && (option = strsep(&options,
","))) {
1525 char *name = strsep(&option,
"="), *value = option;
1527 if (!strcasecmp(name,
"name")) {
1528 if (ast_strlen_zero(value)) {
1529 ast_log(LOG_ERROR,
"A name must be specified for the memory cache\n");
1532 ast_free(cache->
name);
1534 }
else if (!strcasecmp(name,
"maximum_objects")) {
1535 if (configuration_parse_unsigned_integer(value, &cache->
maximum_objects) != 1) {
1536 ast_log(LOG_ERROR,
"Unsupported maximum objects value of '%s' used for memory cache\n",
1540 }
else if (!strcasecmp(name,
"object_lifetime_maximum")) {
1542 ast_log(LOG_ERROR,
"Unsupported object maximum lifetime value of '%s' used for memory cache\n",
1546 }
else if (!strcasecmp(name,
"object_lifetime_stale")) {
1548 ast_log(LOG_ERROR,
"Unsupported object stale lifetime value of '%s' used for memory cache\n",
1552 }
else if (!strcasecmp(name,
"expire_on_reload")) {
1554 }
else if (!strcasecmp(name,
"full_backend_cache")) {
1557 ast_log(LOG_ERROR,
"Unsupported option '%s' used for memory cache\n", name);
1564 sorcery_memory_cached_object_hash, NULL, sorcery_memory_cached_object_cmp);
1566 ast_log(LOG_ERROR,
"Could not create a container to hold cached objects for memory cache\n");
1573 ast_log(LOG_ERROR,
"Could not create heap to hold cached objects\n");
1598 static int sorcery_memory_cache_delete(
const struct ast_sorcery *sorcery,
void *data,
void *
object)
1620 static void sorcery_memory_cache_close(
void *data)
1625 if (!ast_strlen_zero(cache->
name)) {
1635 remove_all_from_cache(cache);
1652 static char *sorcery_memory_cache_complete_name(
const char *word,
int state)
1656 int wordlen = strlen(word);
1658 char *result = NULL;
1661 while ((cache = ao2_iterator_next(&it_caches))) {
1662 if (!strncasecmp(word, cache->
name, wordlen)
1663 && ++which > state) {
1685 e->
command =
"sorcery memory cache show";
1687 "Usage: sorcery memory cache show <name>\n"
1688 " Show sorcery memory cache configuration and statistics.\n";
1692 return sorcery_memory_cache_complete_name(a->word, a->n);
1699 return CLI_SHOWUSAGE;
1704 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1708 ast_cli(a->fd,
"Sorcery memory cache: %s\n", cache->
name);
1711 ast_cli(a->fd,
"Maximum allowed objects: %d\n", cache->
maximum_objects);
1713 ast_cli(a->fd,
"There is no limit on the maximum number of objects in the cache\n");
1718 ast_cli(a->fd,
"Object expiration is not enabled - cached objects will not expire\n");
1721 ast_cli(a->fd,
"Number of seconds before object becomes stale: %d\n", cache->
object_lifetime_stale);
1723 ast_cli(a->fd,
"Object staleness is not enabled - cached objects will not go stale\n");
1744 static int sorcery_memory_cache_print_object(
void *obj,
void *arg,
int flags)
1746 #define FORMAT "%-25.25s %-15u %-15u \n"
1749 int seconds_until_expire = 0, seconds_until_stale = 0;
1770 #define FORMAT "%-25.25s %-15.15s %-15.15s \n"
1776 e->
command =
"sorcery memory cache dump";
1778 "Usage: sorcery memory cache dump <name>\n"
1779 " Dump a list of the objects within the cache, listed by object identifier.\n";
1783 return sorcery_memory_cache_complete_name(a->word, a->n);
1790 return CLI_SHOWUSAGE;
1795 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1802 ast_cli(a->fd,
"Dumping sorcery memory cache '%s':\n", cache->
name);
1804 ast_cli(a->fd,
" * Staleness is not enabled - objects will not go stale\n");
1807 ast_cli(a->fd,
" * Object lifetime is not enabled - objects will not expire\n");
1809 ast_cli(a->fd, FORMAT,
"Object Name",
"Stale In",
"Expires In");
1810 ast_cli(a->fd, FORMAT,
"-------------------------",
"---------------",
"---------------");
1812 ast_cli(a->fd, FORMAT,
"-------------------------",
"---------------",
"---------------");
1825 static char *sorcery_memory_cache_complete_object_name(
const char *cache_name,
const char *word,
int state)
1830 int wordlen = strlen(word);
1832 char *result = NULL;
1840 while ((cached = ao2_iterator_next(&it_cached))) {
1842 && ++which > state) {
1867 e->
command =
"sorcery memory cache expire";
1869 "Usage: sorcery memory cache expire <cache name> [object name]\n"
1870 " Expire a specific object or ALL objects within a sorcery memory cache.\n";
1874 return sorcery_memory_cache_complete_name(a->word, a->n);
1875 }
else if (a->pos == 5) {
1876 return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
1882 if (a->argc < 5 || a->argc > 6) {
1883 return CLI_SHOWUSAGE;
1888 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1894 remove_all_from_cache(cache);
1895 ast_cli(a->fd,
"All objects have been removed from cache '%s'\n", a->argv[4]);
1898 ast_cli(a->fd,
"Due to full backend caching per-object expiration is not available on cache '%s'\n", a->argv[4]);
1899 }
else if (!remove_from_cache(cache, a->argv[5], 1)) {
1900 ast_cli(a->fd,
"Successfully expired object '%s' from cache '%s'\n", a->argv[5], a->argv[4]);
1902 ast_cli(a->fd,
"Object '%s' was not expired from cache '%s' as it was not found\n", a->argv[5],
1924 e->
command =
"sorcery memory cache stale";
1926 "Usage: sorcery memory cache stale <cache name> [object name [reload]]\n"
1927 " Mark a specific object or ALL objects as stale in a sorcery memory cache.\n"
1928 " If \"reload\" is specified, then the object is marked stale and immediately\n"
1929 " retrieved from backend storage to repopulate the cache\n";
1933 return sorcery_memory_cache_complete_name(a->word, a->n);
1934 }
else if (a->pos == 5) {
1935 return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
1936 }
else if (a->pos == 6) {
1937 static const char *
const completions[] = {
"reload", NULL };
1944 if (a->argc < 5 || a->argc > 7) {
1945 return CLI_SHOWUSAGE;
1949 if (!strcasecmp(a->argv[6],
"reload")) {
1952 return CLI_SHOWUSAGE;
1958 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1963 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not have staleness enabled\n", a->argv[4]);
1970 mark_all_as_stale_in_cache(cache);
1971 ast_cli(a->fd,
"Marked all objects in sorcery memory cache '%s' as stale\n", a->argv[4]);
1973 if (!mark_object_as_stale_in_cache(cache, a->argv[5])) {
1974 ast_cli(a->fd,
"Successfully marked object '%s' in memory cache '%s' as stale\n",
1975 a->argv[5], a->argv[4]);
1981 memory_cache_stale_update_object(cache->
sorcery, cache, cached);
1986 ast_cli(a->fd,
"Object '%s' in sorcery memory cache '%s' could not be marked as stale as it was not found\n",
1987 a->argv[5], a->argv[4]);
2007 e->
command =
"sorcery memory cache populate";
2009 "Usage: sorcery memory cache populate <cache name>\n"
2010 " Expire all objects in the cache and populate it with ALL objects from backend.\n";
2014 return sorcery_memory_cache_complete_name(a->word, a->n);
2021 return CLI_SHOWUSAGE;
2026 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
2031 ast_cli(a->fd,
"Specified sorcery memory cache '%s' does not have full backend caching enabled\n", a->argv[4]);
2038 ast_cli(a->fd,
"Specified sorcery memory cache '%s' is no longer active\n", a->argv[4]);
2044 remove_all_from_cache(cache);
2047 ast_cli(a->fd,
"Specified sorcery memory cache '%s' has been populated with '%d' objects from the backend\n",
2058 AST_CLI_DEFINE(sorcery_memory_cache_show,
"Show sorcery memory cache information"),
2059 AST_CLI_DEFINE(sorcery_memory_cache_dump,
"Dump all objects within a sorcery memory cache"),
2060 AST_CLI_DEFINE(sorcery_memory_cache_expire,
"Expire a specific object or ALL objects within a sorcery memory cache"),
2061 AST_CLI_DEFINE(sorcery_memory_cache_stale,
"Mark a specific object or ALL objects as stale within a sorcery memory cache"),
2062 AST_CLI_DEFINE(sorcery_memory_cache_populate,
"Clear and populate the sorcery memory cache with objects from the backend"),
2069 static int sorcery_memory_cache_ami_expire_object(
struct mansession *s,
const struct message *m)
2076 if (ast_strlen_zero(cache_name)) {
2077 astman_send_error(s, m,
"SorceryMemoryCacheExpireObject requires that a cache name be provided.\n");
2079 }
else if (ast_strlen_zero(object_name)) {
2080 astman_send_error(s, m,
"SorceryMemoryCacheExpireObject requires that an object name be provided\n");
2094 res = remove_from_cache(cache, object_name, 1);
2101 astman_send_error(s, m,
"Due to full backend caching per-object expiration is not available, consider using SorceryMemoryCachePopulate or SorceryMemoryCacheExpire instead\n");
2103 astman_send_ack(s, m,
"The provided object was expired from the cache\n");
2105 astman_send_error(s, m,
"The provided object could not be expired from the cache\n");
2115 static int sorcery_memory_cache_ami_expire(
struct mansession *s,
const struct message *m)
2120 if (ast_strlen_zero(cache_name)) {
2121 astman_send_error(s, m,
"SorceryMemoryCacheExpire requires that a cache name be provided.\n");
2132 remove_all_from_cache(cache);
2146 static int sorcery_memory_cache_ami_stale_object(
struct mansession *s,
const struct message *m)
2154 if (ast_strlen_zero(cache_name)) {
2155 astman_send_error(s, m,
"SorceryMemoryCacheStaleObject requires that a cache name be provided.\n");
2157 }
else if (ast_strlen_zero(object_name)) {
2158 astman_send_error(s, m,
"SorceryMemoryCacheStaleObject requires that an object name be provided\n");
2170 res = mark_object_as_stale_in_cache(cache, object_name);
2177 memory_cache_stale_update_object(cache->
sorcery, cache, cached);
2187 astman_send_ack(s, m,
"The provided object was marked as stale in the cache\n");
2189 astman_send_error(s, m,
"The provided object could not be marked as stale in the cache\n");
2199 static int sorcery_memory_cache_ami_stale(
struct mansession *s,
const struct message *m)
2204 if (ast_strlen_zero(cache_name)) {
2205 astman_send_error(s, m,
"SorceryMemoryCacheStale requires that a cache name be provided.\n");
2216 mark_all_as_stale_in_cache(cache);
2221 astman_send_ack(s, m,
"All objects were marked as stale in the cache\n");
2230 static int sorcery_memory_cache_ami_populate(
struct mansession *s,
const struct message *m)
2235 if (ast_strlen_zero(cache_name)) {
2236 astman_send_error(s, m,
"SorceryMemoryCachePopulate requires that a cache name be provided.\n");
2247 astman_send_error(s, m,
"The provided cache does not have full backend caching enabled\n");
2260 remove_all_from_cache(cache);
2272 #ifdef TEST_FRAMEWORK
2276 SORCERY_OBJECT(details);
2300 static struct ast_sorcery *alloc_and_initialize_sorcery(
void)
2319 int res = AST_TEST_PASS;
2324 info->name =
"open_with_valid_options";
2325 info->category =
"/res/res_sorcery_memory_cache/";
2326 info->summary =
"Attempt to create sorcery memory caches using valid options";
2327 info->description =
"This test performs the following:\n"
2328 "\t* Creates a memory cache with default configuration\n"
2329 "\t* Creates a memory cache with a maximum object count of 10 and verifies it\n"
2330 "\t* Creates a memory cache with a maximum object lifetime of 60 and verifies it\n"
2331 "\t* Creates a memory cache with a stale object lifetime of 90 and verifies it";
2332 return AST_TEST_NOT_RUN;
2337 cache = sorcery_memory_cache_open(
"");
2339 ast_test_status_update(
test,
"Failed to create a sorcery memory cache using default configuration\n");
2340 res = AST_TEST_FAIL;
2342 sorcery_memory_cache_close(cache);
2345 cache = sorcery_memory_cache_open(
"maximum_objects=10");
2347 ast_test_status_update(
test,
"Failed to create a sorcery memory cache with a maximum object count of 10\n");
2348 res = AST_TEST_FAIL;
2351 ast_test_status_update(
test,
"Created a sorcery memory cache with a maximum object count of 10 but it has '%u'\n",
2354 sorcery_memory_cache_close(cache);
2357 cache = sorcery_memory_cache_open(
"object_lifetime_maximum=60");
2359 ast_test_status_update(
test,
"Failed to create a sorcery memory cache with a maximum object lifetime of 60\n");
2360 res = AST_TEST_FAIL;
2363 ast_test_status_update(
test,
"Created a sorcery memory cache with a maximum object lifetime of 60 but it has '%u'\n",
2366 sorcery_memory_cache_close(cache);
2369 cache = sorcery_memory_cache_open(
"object_lifetime_stale=90");
2371 ast_test_status_update(
test,
"Failed to create a sorcery memory cache with a stale object lifetime of 90\n");
2372 res = AST_TEST_FAIL;
2375 ast_test_status_update(
test,
"Created a sorcery memory cache with a stale object lifetime of 90 but it has '%u'\n",
2378 sorcery_memory_cache_close(cache);
2387 int res = AST_TEST_PASS;
2392 info->name =
"open_with_invalid_options";
2393 info->category =
"/res/res_sorcery_memory_cache/";
2394 info->summary =
"Attempt to create sorcery memory caches using invalid options";
2395 info->description =
"This test attempts to perform the following:\n"
2396 "\t* Create a memory cache with an empty name\n"
2397 "\t* Create a memory cache with a maximum object count of -1\n"
2398 "\t* Create a memory cache with a maximum object count of toast\n"
2399 "\t* Create a memory cache with a maximum object lifetime of -1\n"
2400 "\t* Create a memory cache with a maximum object lifetime of toast\n"
2401 "\t* Create a memory cache with a stale object lifetime of -1\n"
2402 "\t* Create a memory cache with a stale object lifetime of toast";
2403 return AST_TEST_NOT_RUN;
2408 cache = sorcery_memory_cache_open(
"name=");
2410 ast_test_status_update(
test,
"Created a sorcery memory cache with an empty name\n");
2411 sorcery_memory_cache_close(cache);
2412 res = AST_TEST_FAIL;
2415 cache = sorcery_memory_cache_open(
"maximum_objects=-1");
2417 ast_test_status_update(
test,
"Created a sorcery memory cache with a maximum object count of -1\n");
2418 sorcery_memory_cache_close(cache);
2419 res = AST_TEST_FAIL;
2422 cache = sorcery_memory_cache_open(
"maximum_objects=toast");
2424 ast_test_status_update(
test,
"Created a sorcery memory cache with a maximum object count of toast\n");
2425 sorcery_memory_cache_close(cache);
2426 res = AST_TEST_FAIL;
2429 cache = sorcery_memory_cache_open(
"object_lifetime_maximum=-1");
2431 ast_test_status_update(
test,
"Created a sorcery memory cache with an object lifetime maximum of -1\n");
2432 sorcery_memory_cache_close(cache);
2433 res = AST_TEST_FAIL;
2436 cache = sorcery_memory_cache_open(
"object_lifetime_maximum=toast");
2438 ast_test_status_update(
test,
"Created a sorcery memory cache with an object lifetime maximum of toast\n");
2439 sorcery_memory_cache_close(cache);
2440 res = AST_TEST_FAIL;
2443 cache = sorcery_memory_cache_open(
"object_lifetime_stale=-1");
2445 ast_test_status_update(
test,
"Created a sorcery memory cache with a stale object lifetime of -1\n");
2446 sorcery_memory_cache_close(cache);
2447 res = AST_TEST_FAIL;
2450 cache = sorcery_memory_cache_open(
"object_lifetime_stale=toast");
2452 ast_test_status_update(
test,
"Created a sorcery memory cache with a stale object lifetime of toast\n");
2453 sorcery_memory_cache_close(cache);
2454 res = AST_TEST_FAIL;
2457 cache = sorcery_memory_cache_open(
"tacos");
2459 ast_test_status_update(
test,
"Created a sorcery memory cache with an invalid configuration option 'tacos'\n");
2460 sorcery_memory_cache_close(cache);
2461 res = AST_TEST_FAIL;
2469 int res = AST_TEST_FAIL;
2472 RAII_VAR(
void *,
object, NULL, ao2_cleanup);
2473 RAII_VAR(
void *, cached_object, NULL, ao2_cleanup);
2477 info->name =
"create";
2478 info->category =
"/res/res_sorcery_memory_cache/";
2479 info->summary =
"Attempt to create an object in the cache";
2480 info->description =
"This test performs the following:\n"
2481 "\t* Creates a memory cache with default options\n"
2482 "\t* Creates a sorcery instance with a test object\n"
2483 "\t* Creates a test object with an id of test\n"
2484 "\t* Pushes the test object into the memory cache\n"
2485 "\t* Confirms that the test object is in the cache";
2486 return AST_TEST_NOT_RUN;
2491 cache = sorcery_memory_cache_open(
"");
2493 ast_test_status_update(
test,
"Failed to create a sorcery memory cache using default options\n");
2498 ast_test_status_update(
test,
"Memory cache contains cached objects before we added one\n");
2502 sorcery = alloc_and_initialize_sorcery();
2504 ast_test_status_update(
test,
"Failed to create a test sorcery instance\n");
2510 ast_test_status_update(
test,
"Failed to allocate a test object\n");
2514 sorcery_memory_cache_create(sorcery, cache,
object);
2517 ast_test_status_update(
test,
"Added test object to memory cache but cache remains empty\n");
2521 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache,
"test",
"test");
2522 if (!cached_object) {
2523 ast_test_status_update(
test,
"Object placed into memory cache could not be retrieved\n");
2527 if (cached_object !=
object) {
2528 ast_test_status_update(
test,
"Object retrieved from memory cached is not the one we cached\n");
2532 res = AST_TEST_PASS;
2536 sorcery_memory_cache_close(cache);
2547 int res = AST_TEST_FAIL;
2550 RAII_VAR(
void *, original_object, NULL, ao2_cleanup);
2551 RAII_VAR(
void *, updated_object, NULL, ao2_cleanup);
2552 RAII_VAR(
void *, cached_object, NULL, ao2_cleanup);
2556 info->name =
"create";
2557 info->category =
"/res/res_sorcery_memory_cache/";
2558 info->summary =
"Attempt to create and then update an object in the cache";
2559 info->description =
"This test performs the following:\n"
2560 "\t* Creates a memory cache with default options\n"
2561 "\t* Creates a sorcery instance with a test object\n"
2562 "\t* Creates a test object with an id of test\n"
2563 "\t* Pushes the test object into the memory cache\n"
2564 "\t* Confirms that the test object is in the cache\n"
2565 "\t* Creates a new test object with the same id of test\n"
2566 "\t* Pushes the new test object into the memory cache\n"
2567 "\t* Confirms that the new test object has replaced the old one";
2568 return AST_TEST_NOT_RUN;
2573 cache = sorcery_memory_cache_open(
"");
2575 ast_test_status_update(
test,
"Failed to create a sorcery memory cache using default options\n");
2580 ast_test_status_update(
test,
"Memory cache contains cached objects before we added one\n");
2584 sorcery = alloc_and_initialize_sorcery();
2586 ast_test_status_update(
test,
"Failed to create a test sorcery instance\n");
2591 if (!original_object) {
2592 ast_test_status_update(
test,
"Failed to allocate a test object\n");
2596 sorcery_memory_cache_create(sorcery, cache, original_object);
2599 if (!updated_object) {
2600 ast_test_status_update(
test,
"Failed to allocate an updated test object\n");
2604 sorcery_memory_cache_create(sorcery, cache, updated_object);
2607 ast_test_status_update(
test,
"Added updated test object to memory cache but cache now contains %d objects instead of 1\n",
2612 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache,
"test",
"test");
2613 if (!cached_object) {
2614 ast_test_status_update(
test,
"Updated object placed into memory cache could not be retrieved\n");
2618 if (cached_object == original_object) {
2619 ast_test_status_update(
test,
"Updated object placed into memory cache but old one is being retrieved\n");
2621 }
else if (cached_object != updated_object) {
2622 ast_test_status_update(
test,
"Updated object placed into memory cache but different one is being retrieved\n");
2626 res = AST_TEST_PASS;
2630 sorcery_memory_cache_close(cache);
2641 int res = AST_TEST_FAIL;
2644 RAII_VAR(
void *,
object, NULL, ao2_cleanup);
2645 RAII_VAR(
void *, cached_object, NULL, ao2_cleanup);
2649 info->name =
"delete";
2650 info->category =
"/res/res_sorcery_memory_cache/";
2651 info->summary =
"Attempt to create and then delete an object in the cache";
2652 info->description =
"This test performs the following:\n"
2653 "\t* Creates a memory cache with default options\n"
2654 "\t* Creates a sorcery instance with a test object\n"
2655 "\t* Creates a test object with an id of test\n"
2656 "\t* Pushes the test object into the memory cache\n"
2657 "\t* Confirms that the test object is in the cache\n"
2658 "\t* Deletes the test object from the cache\n"
2659 "\t* Confirms that the test object is no longer in the cache";
2660 return AST_TEST_NOT_RUN;
2665 cache = sorcery_memory_cache_open(
"");
2667 ast_test_status_update(
test,
"Failed to create a sorcery memory cache using default options\n");
2672 ast_test_status_update(
test,
"Memory cache contains cached objects before we added one\n");
2676 sorcery = alloc_and_initialize_sorcery();
2678 ast_test_status_update(
test,
"Failed to create a test sorcery instance\n");
2684 ast_test_status_update(
test,
"Failed to allocate a test object\n");
2688 sorcery_memory_cache_create(sorcery, cache,
object);
2691 ast_test_status_update(
test,
"Added test object to memory cache but cache contains no objects\n");
2695 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache,
"test",
"test");
2696 if (!cached_object) {
2697 ast_test_status_update(
test,
"Test object placed into memory cache could not be retrieved\n");
2702 cached_object = NULL;
2704 sorcery_memory_cache_delete(sorcery, cache,
object);
2706 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache,
"test",
"test");
2707 if (cached_object) {
2708 ast_test_status_update(
test,
"Test object deleted from memory cache can still be retrieved\n");
2712 res = AST_TEST_PASS;
2716 sorcery_memory_cache_close(cache);
2726 const char **in_cache,
size_t num_in_cache,
const char **not_in_cache,
size_t num_not_in_cache)
2730 RAII_VAR(
void *, cached_object, NULL, ao2_cleanup);
2732 for (i = 0; i < num_in_cache; ++i) {
2733 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache,
"test", in_cache[i]);
2734 if (!cached_object) {
2735 ast_test_status_update(test,
"Failed to retrieve '%s' object from the cache\n",
2742 for (i = 0; i < num_not_in_cache; ++i) {
2743 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache,
"test", not_in_cache[i]);
2744 if (cached_object) {
2745 ast_test_status_update(test,
"Retrieved '%s' object from the cache unexpectedly\n",
2757 int res = AST_TEST_FAIL;
2760 RAII_VAR(
void *, alice, NULL, ao2_cleanup);
2761 RAII_VAR(
void *, bob, NULL, ao2_cleanup);
2762 RAII_VAR(
void *, charlie, NULL, ao2_cleanup);
2763 RAII_VAR(
void *, cached_object, NULL, ao2_cleanup);
2764 const char *in_cache[2];
2765 const char *not_in_cache[2];
2769 info->name =
"maximum_objects";
2770 info->category =
"/res/res_sorcery_memory_cache/";
2771 info->summary =
"Ensure that the 'maximum_objects' option works as expected";
2772 info->description =
"This test performs the following:\n"
2773 "\t* Creates a memory cache with maximum_objects=2\n"
2774 "\t* Creates a sorcery instance\n"
2775 "\t* Creates a three test objects: alice, bob, charlie, and david\n"
2776 "\t* Pushes alice and bob into the memory cache\n"
2777 "\t* Confirms that alice and bob are in the memory cache\n"
2778 "\t* Pushes charlie into the memory cache\n"
2779 "\t* Confirms that bob and charlie are in the memory cache\n"
2780 "\t* Deletes charlie from the memory cache\n"
2781 "\t* Confirms that only bob is in the memory cache\n"
2782 "\t* Pushes alice into the memory cache\n"
2783 "\t* Confirms that bob and alice are in the memory cache";
2784 return AST_TEST_NOT_RUN;
2789 cache = sorcery_memory_cache_open(
"maximum_objects=2");
2791 ast_test_status_update(test,
"Failed to create a sorcery memory cache with maximum_objects=2\n");
2796 ast_test_status_update(test,
"Memory cache contains cached objects before we added one\n");
2800 sorcery = alloc_and_initialize_sorcery();
2802 ast_test_status_update(test,
"Failed to create a test sorcery instance\n");
2810 if (!alice || !bob || !charlie) {
2811 ast_test_status_update(test,
"Failed to allocate sorcery object(s)\n");
2815 sorcery_memory_cache_create(sorcery, cache, alice);
2816 in_cache[0] =
"alice";
2818 not_in_cache[0] =
"bob";
2819 not_in_cache[1] =
"charlie";
2820 if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
2829 sorcery_memory_cache_create(sorcery, cache, bob);
2830 in_cache[0] =
"alice";
2831 in_cache[1] =
"bob";
2832 not_in_cache[0] =
"charlie";
2833 not_in_cache[1] = NULL;
2834 if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2840 sorcery_memory_cache_create(sorcery, cache, charlie);
2841 in_cache[0] =
"bob";
2842 in_cache[1] =
"charlie";
2843 not_in_cache[0] =
"alice";
2844 not_in_cache[1] = NULL;
2845 if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2850 sorcery_memory_cache_delete(sorcery, cache, charlie);
2851 in_cache[0] =
"bob";
2853 not_in_cache[0] =
"alice";
2854 not_in_cache[1] =
"charlie";
2855 if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
2860 sorcery_memory_cache_create(sorcery, cache, alice);
2861 in_cache[0] =
"bob";
2862 in_cache[1] =
"alice";
2863 not_in_cache[0] =
"charlie";
2864 not_in_cache[1] = NULL;
2865 if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2869 res = AST_TEST_PASS;
2873 sorcery_memory_cache_close(cache);
2884 int res = AST_TEST_FAIL;
2891 info->name =
"expiration";
2892 info->category =
"/res/res_sorcery_memory_cache/";
2893 info->summary =
"Add objects to a cache configured with maximum lifetime, confirm they are removed";
2894 info->description =
"This test performs the following:\n"
2895 "\t* Creates a memory cache with a maximum object lifetime of 5 seconds\n"
2896 "\t* Pushes 10 objects into the memory cache\n"
2897 "\t* Waits (up to) 10 seconds for expiration to occur\n"
2898 "\t* Confirms that the objects have been removed from the cache";
2899 return AST_TEST_NOT_RUN;
2904 cache = sorcery_memory_cache_open(
"object_lifetime_maximum=5");
2906 ast_test_status_update(test,
"Failed to create a sorcery memory cache using default options\n");
2910 sorcery = alloc_and_initialize_sorcery();
2912 ast_test_status_update(test,
"Failed to create a test sorcery instance\n");
2916 cache->cache_notify = 1;
2917 ast_mutex_init(&cache->lock);
2918 ast_cond_init(&cache->cond, NULL);
2920 for (i = 0; i < 5; ++i) {
2921 char uuid[AST_UUID_STR_LEN];
2926 ast_test_status_update(test,
"Failed to allocate test object for expiration\n");
2930 sorcery_memory_cache_create(sorcery, cache,
object);
2935 ast_mutex_lock(&cache->lock);
2936 while (!cache->cache_completed) {
2938 struct timespec end = {
2939 .tv_sec = start.tv_sec + 10,
2940 .tv_nsec = start.tv_usec * 1000,
2943 if (ast_cond_timedwait(&cache->cond, &cache->lock, &end) == ETIMEDOUT) {
2947 ast_mutex_unlock(&cache->lock);
2950 ast_test_status_update(test,
"Objects placed into the memory cache did not expire and get removed\n");
2954 res = AST_TEST_PASS;
2958 if (cache->cache_notify) {
2959 ast_cond_destroy(&cache->cond);
2960 ast_mutex_destroy(&cache->lock);
2962 sorcery_memory_cache_close(cache);
2974 static struct backend_data {
2981 } *real_backend_data;
2987 SORCERY_OBJECT(details);
3018 const char *type,
const char *
id)
3022 if (!real_backend_data->exists) {
3031 b_data->salt = real_backend_data->salt;
3032 b_data->pepper = real_backend_data->pepper;
3049 static void mock_retrieve_multiple(
const struct ast_sorcery *sorcery,
void *data,
3058 for (i = 0; i < real_backend_data->exists; ++i) {
3059 char uuid[AST_UUID_STR_LEN];
3067 b_data->salt = real_backend_data->salt;
3068 b_data->pepper = real_backend_data->pepper;
3081 .retrieve_multiple = mock_retrieve_multiple,
3099 static int wait_for_cache_update(
const struct ast_sorcery *sorcery,
3100 void *previous_object,
struct test_data **new_object)
3108 if (
object != previous_object) {
3109 *new_object = object;
3112 ao2_cleanup(
object);
3120 int res = AST_TEST_FAIL;
3123 struct backend_data iterations[] = {
3124 { .salt = 1, .pepper = 2, .exists = 1 },
3125 { .salt = 568729, .pepper = -234123, .exists = 1 },
3126 { .salt = 0, .pepper = 0, .exists = 0 },
3128 struct backend_data initial = {
3137 info->name =
"stale";
3138 info->category =
"/res/res_sorcery_memory_cache/";
3139 info->summary =
"Ensure that stale objects are replaced with updated objects";
3140 info->description =
"This test performs the following:\n"
3141 "\t* Create a sorcery instance with two wizards"
3142 "\t\t* The first is a memory cache that marks items stale after 3 seconds\n"
3143 "\t\t* The second is a mock of a back-end\n"
3144 "\t* Pre-populates the cache by retrieving some initial data from the backend.\n"
3145 "\t* Performs iterations of the following:\n"
3146 "\t\t* Update backend data with new values\n"
3147 "\t\t* Retrieve item from the cache\n"
3148 "\t\t* Ensure the retrieved item does not have the new backend values\n"
3149 "\t\t* Wait for cached object to become stale\n"
3150 "\t\t* Retrieve the stale cached object\n"
3151 "\t\t* Ensure that the stale object retrieved is the same as the fresh one from earlier\n"
3152 "\t\t* Wait for the cache to update with new data\n"
3153 "\t\t* Ensure that new data in the cache matches backend data";
3154 return AST_TEST_NOT_RUN;
3163 ast_test_status_update(test,
"Failed to create sorcery instance\n");
3168 "object_lifetime_stale=3", 1);
3173 real_backend_data = &initial;
3176 if (!backend_object) {
3177 ast_test_status_update(test,
"Unable to retrieve backend data and populate the cache\n");
3182 for (i = 0; i < ARRAY_LEN(iterations); ++i) {
3187 real_backend_data = &iterations[i];
3189 ast_test_status_update(test,
"Begininning iteration %d\n", i);
3193 ast_test_status_update(test,
"Unable to retrieve fresh cached object\n");
3197 if (cache_fresh->salt == iterations[i].salt || cache_fresh->pepper == iterations[i].pepper) {
3198 ast_test_status_update(test,
"Fresh cached object has unexpected values. Did we hit the backend?\n");
3206 ast_test_status_update(test,
"Unable to retrieve stale cached object\n");
3210 if (cache_stale != cache_fresh) {
3211 ast_test_status_update(test,
"Stale cache hit retrieved different object than fresh cache hit\n");
3215 if (wait_for_cache_update(sorcery, cache_stale, &cache_new)) {
3216 ast_test_status_update(test,
"Cache was not updated\n");
3220 if (iterations[i].exists) {
3222 ast_test_status_update(test,
"Failed to retrieve item from cache when there should be one present\n");
3224 }
else if (cache_new->salt != iterations[i].salt ||
3225 cache_new->pepper != iterations[i].pepper) {
3226 ast_test_status_update(test,
"New cached item has unexpected values\n");
3229 }
else if (cache_new) {
3230 ast_test_status_update(test,
"Retrieved a cached item when there should not have been one present\n");
3235 res = AST_TEST_PASS;
3247 int res = AST_TEST_FAIL;
3249 struct backend_data initial = {
3257 struct timeval start;
3258 struct timespec end;
3262 info->name =
"full_backend_cache_expiration";
3263 info->category =
"/res/res_sorcery_memory_cache/";
3264 info->summary =
"Ensure that the full backend cache actually caches the backend";
3265 info->description =
"This test performs the following:\n"
3266 "\t* Create a sorcery instance with two wizards"
3267 "\t\t* The first is a memory cache that expires objects after 3 seconds and does full backend caching\n"
3268 "\t\t* The second is a mock of a back-end\n"
3269 "\t* Populates the cache by requesting all objects which returns 4.\n"
3270 "\t* Updates the backend to contain a different number of objects, 8.\n"
3271 "\t* Requests all objects and confirms the number returned is only 4.\n"
3272 "\t* Wait for cached objects to expire.\n"
3273 "\t* Requests all objects and confirms the number returned is 8.";
3274 return AST_TEST_NOT_RUN;
3283 ast_test_status_update(test,
"Failed to create sorcery instance\n");
3288 "object_lifetime_maximum=3,full_backend_cache=yes", 1);
3295 real_backend_data = &initial;
3300 ast_test_status_update(test,
"Unable to retrieve all objects in backend and populate cache\n");
3311 ast_test_status_update(test,
"Unable to retrieve all objects in backend and populate cache\n");
3316 ast_test_status_update(test,
"Number of objects returned is of the current backend and not the cache\n");
3323 ast_mutex_init(&lock);
3324 ast_cond_init(&cond, NULL);
3327 end.tv_sec = start.tv_sec + 5;
3328 end.tv_nsec = start.tv_usec * 1000;
3330 ast_mutex_lock(&lock);
3331 while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
3333 ast_mutex_unlock(&lock);
3335 ast_mutex_destroy(&lock);
3336 ast_cond_destroy(&cond);
3341 ast_test_status_update(test,
"Unable to retrieve all objects in backend and populate cache\n");
3346 ast_test_status_update(test,
"Number of objects returned is NOT of the current backend when it should be\n");
3353 res = AST_TEST_PASS;
3365 int res = AST_TEST_FAIL;
3367 struct backend_data initial = {
3375 struct timeval start;
3376 struct timespec end;
3380 info->name =
"full_backend_cache_stale";
3381 info->category =
"/res/res_sorcery_memory_cache/";
3382 info->summary =
"Ensure that the full backend cache works with staleness";
3383 info->description =
"This test performs the following:\n"
3384 "\t* Create a sorcery instance with two wizards"
3385 "\t\t* The first is a memory cache that stales objects after 1 second and does full backend caching\n"
3386 "\t\t* The second is a mock of a back-end\n"
3387 "\t* Populates the cache by requesting all objects which returns 4.\n"
3388 "\t* Wait for objects to go stale.\n"
3389 "\t* Updates the backend to contain a different number of objects, 8.\""
3390 "\t* Requests all objects and confirms the number returned is only 4.\n"
3391 "\t* Wait for objects to be refreshed from backend.\n"
3392 "\t* Requests all objects and confirms the number returned is 8.";
3393 return AST_TEST_NOT_RUN;
3400 ast_mutex_init(&lock);
3401 ast_cond_init(&cond, NULL);
3405 ast_test_status_update(test,
"Failed to create sorcery instance\n");
3410 "object_lifetime_stale=1,full_backend_cache=yes", 1);
3417 real_backend_data = &initial;
3422 ast_test_status_update(test,
"Unable to retrieve all objects in backend and populate cache\n");
3428 end.tv_sec = start.tv_sec + 5;
3429 end.tv_nsec = start.tv_usec * 1000;
3431 ast_mutex_lock(&lock);
3432 while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
3434 ast_mutex_unlock(&lock);
3441 ast_test_status_update(test,
"Unable to retrieve all objects in backend and populate cache\n");
3446 ast_test_status_update(test,
"Number of objects returned is of the backend and not the cache\n");
3454 end.tv_sec = start.tv_sec + 5;
3455 end.tv_nsec = start.tv_usec * 1000;
3457 ast_mutex_lock(&lock);
3458 while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
3460 ast_mutex_unlock(&lock);
3465 ast_test_status_update(test,
"Unable to retrieve all objects in backend and populate cache\n");
3470 ast_test_status_update(test,
"Number of objects returned is not of backend\n");
3478 end.tv_sec = start.tv_sec + 5;
3479 end.tv_nsec = start.tv_usec * 1000;
3481 ast_mutex_lock(&lock);
3482 while (ast_cond_timedwait(&cond, &lock, &end) != ETIMEDOUT) {
3484 ast_mutex_unlock(&lock);
3486 res = AST_TEST_PASS;
3493 ast_mutex_destroy(&lock);
3494 ast_cond_destroy(&cond);
3500 static int unload_module(
void)
3502 AST_TEST_UNREGISTER(open_with_valid_options);
3503 AST_TEST_UNREGISTER(open_with_invalid_options);
3504 AST_TEST_UNREGISTER(create_and_retrieve);
3505 AST_TEST_UNREGISTER(update);
3506 AST_TEST_UNREGISTER(
delete);
3507 AST_TEST_UNREGISTER(maximum_objects);
3508 AST_TEST_UNREGISTER(expiration);
3509 AST_TEST_UNREGISTER(stale);
3510 AST_TEST_UNREGISTER(full_backend_cache_expiration);
3511 AST_TEST_UNREGISTER(full_backend_cache_stale);
3532 ao2_cleanup(caches);
3538 static int load_module(
void)
3544 sorcery_memory_cache_cmp);
3546 ast_log(LOG_ERROR,
"Failed to create container for configured caches\n");
3553 ast_log(LOG_ERROR,
"Failed to create scheduler for cache management\n");
3559 ast_log(LOG_ERROR,
"Failed to create scheduler thread for cache management\n");
3570 res |=
ast_manager_register_xml(
"SorceryMemoryCacheExpireObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire_object);
3572 res |=
ast_manager_register_xml(
"SorceryMemoryCacheStaleObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale_object);
3574 res |=
ast_manager_register_xml(
"SorceryMemoryCachePopulate", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_populate);
3584 AST_TEST_REGISTER(stale);
3585 AST_TEST_REGISTER(open_with_valid_options);
3586 AST_TEST_REGISTER(open_with_invalid_options);
3587 AST_TEST_REGISTER(create_and_retrieve);
3588 AST_TEST_REGISTER(update);
3589 AST_TEST_REGISTER(
delete);
3590 AST_TEST_REGISTER(maximum_objects);
3591 AST_TEST_REGISTER(expiration);
3592 AST_TEST_REGISTER(full_backend_cache_expiration);
3593 AST_TEST_REGISTER(full_backend_cache_stale);
3598 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"Sorcery Memory Cache Object Wizard",
3599 .support_level = AST_MODULE_SUPPORT_CORE,
3600 .load = load_module,
3601 .unload = unload_module,
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
#define CACHE_CONTAINER_BUCKET_SIZE
The default bucket size for the container of objects in the cache.
const struct ast_variable * fields
Pointer to the fields to check.
#define ast_sorcery_object_field_register_nodoc(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object without documentation.
unsigned int maximum_objects
The maximum number of objects permitted in the cache, 0 if no limit.
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.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
#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.
The arg parameter is a search key, but is not an object.
ssize_t __heap_index
index required by heap
const struct ast_sorcery * sorcery
An unreffed pointer to the sorcery instance, accessible only with lock held.
descriptor for a cli entry.
#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.
struct ao2_container * container
Optional container to put object into.
Structure for variables, used for configurations and for channel variables.
Perform no matching, return all objects.
Assume that the ao2_container is already locked.
Full structure for sorcery.
struct ast_heap * ast_heap_destroy(struct ast_heap *h)
Destroy a max heap.
#define AST_SCHED_DEL_UNREF(sched, id, refcall)
schedule task to get deleted and call unref function
#define ast_cli_register_multiple(e, len)
Register multiple commands.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Return all matching objects.
struct timeval created
The time at which the object was created.
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
unsigned int object_lifetime_maximum
The maximum time (in seconds) an object will stay in the cache, 0 if no limit.
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
#define ast_strdup(str)
A wrapper for strdup()
void * ast_heap_pop(struct ast_heap *h)
Pop the max element off of the heap.
const char * name
Name of the wizard.
struct ast_heap * object_heap
Heap of cached objects. Oldest object is at the top.
#define AST_CLI_ONOFF(x)
return On or Off depending on the argument. This is used in many places in CLI command, having a function to generate this helps maintaining a consistent output (and possibly emitting the output in other languages, at some point).
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
const char * ast_sorcery_object_get_type(const void *object)
Get the type of a sorcery object.
const char * prefix
Prefix for matching object id.
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
const char * ast_sorcery_get_module(const struct ast_sorcery *sorcery)
Get the module that has opened the provided sorcery instance.
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
static void cleanup(void)
Clean up any old apps that we don't need any more.
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
#define ast_heap_push(h, elm)
Push an element on to a heap.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
Structure for storing a memory cache.
Type for default option handler for unsigned integers.
static void * test_data_alloc(const char *id)
Allocation callback for test_data sorcery object.
#define ast_strdupa(s)
duplicate a string in memory from the stack
regex_t * regex
Regular expression for checking object id.
unsigned int full_backend_cache
Whether this is a cache of the entire backend, 0 if disabled.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
const size_t prefix_len
Prefix length in bytes for matching object id.
Scheduler Routines (derived from cheops)
struct sorcery_memory_cache * cache
The sorcery memory cache.
const struct ast_sorcery * sorcery
Pointer to the sorcery structure.
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.
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Structure for stored a cached object.
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
int stale_update_sched_id
scheduler id of stale update task
unsigned int expire_on_reload
Whether all objects are expired when the object type is reloaded, 0 if disabled.
#define ast_debug(level,...)
Log a DEBUG message.
int expire_id
Scheduler item for expiring oldest object.
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
#define ast_test_suite_event_notify(s, f,...)
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
static struct ast_sched_context * sched
Scheduler for cache management.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define ast_sorcery_wizard_register(interface)
See __ast_sorcery_wizard_register()
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compress two struct timeval instances returning -1, 0, 1 if the first arg is smaller, equal or greater to the second.
#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 void * test_sorcery_object_alloc(const char *id)
Internal function to allocate a test object.
#define ast_sorcery_internal_object_register(sorcery, type, alloc, transform, apply)
Register an internal, hidden object type.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Structure used to pass data for printing cached object information.
#define ao2_unlink(container, obj)
Remove an object from a container.
static struct ao2_container * caches
Container of created caches.
int ast_sorcery_wizard_unregister(const struct ast_sorcery_wizard *interface)
Unregister a sorcery wizard.
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Sorcery object created based on backend data.
userdata associated with baseline taskprocessor test
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
void * object
The cached object.
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Module has failed to load, may be in an inconsistent state.
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
#define ast_heap_create(init_height, cmp_fn, index_offset)
Create a max heap.
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Interface for a sorcery wizard.
The arg parameter is an object of the same type.
void * ast_heap_remove(struct ast_heap *h, void *elm)
Remove a specific element from a heap.
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
Tests 2 variable lists to see if they match.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Standard Command Line Interface.
char * object_type
The type of object we are caching.
#define CACHES_CONTAINER_BUCKET_SIZE
The bucket size for the container of caches.
struct ao2_container * objects
Objects in the cache.
struct ast_variable * objectset
Cached objectset for field and regex retrieval.
void * ast_heap_peek(struct ast_heap *h, unsigned int index)
Peek at an element on a heap.
#define ast_sorcery_open()
Open a new sorcery structure.
#define CACHE_HEAP_INIT_HEIGHT
Height of heap for cache object heap. Allows 31 initial objects.
#define AST_TEST_DEFINE(hdr)
struct timeval ast_tvsub(struct timeval a, struct timeval b)
Returns the difference of two timevals a - b.
static struct sorcery_test_caching cache
Global scope caching structure for testing.
Search option field mask.
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Structure used for fields comparison.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
#define ASTERISK_GPL_KEY
The text the key() function should return.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
struct ast_cli_args * a
The CLI arguments.
unsigned int object_lifetime_stale
The amount of time (in seconds) before an object is marked as stale, 0 if disabled.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
char * name
The name of the memory cache.
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
static struct ast_sorcery_wizard mock_wizard
A mock sorcery wizard used for the stale test.
Structure for mutex and tracking information.
Sorcery Data Access Layer API.
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
struct sorcery_memory_cache * cache
The sorcery memory cache.
#define ao2_link(container, obj)
Add an object to a container.
int stale_update_sched_id
scheduler id of stale update task