Merge "rtp_engine/res_rtp_asterisk: Fix RTP struct reentrancy crashes."
[asterisk/asterisk.git] / res / res_sorcery_memory_cache.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  *
22  * \brief Sorcery Memory Cache Object Wizard
23  *
24  * \author Joshua Colp <jcolp@digium.com>
25  */
26
27 /*** MODULEINFO
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 #include "asterisk/module.h"
34 #include "asterisk/sorcery.h"
35 #include "asterisk/astobj2.h"
36 #include "asterisk/sched.h"
37 #include "asterisk/test.h"
38 #include "asterisk/heap.h"
39 #include "asterisk/cli.h"
40 #include "asterisk/manager.h"
41
42 /*** DOCUMENTATION
43         <manager name="SorceryMemoryCacheExpireObject" language="en_US">
44                 <synopsis>
45                         Expire (remove) an object from a sorcery memory cache.
46                 </synopsis>
47                 <syntax>
48                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
49                         <parameter name="Cache" required="true">
50                                 <para>The name of the cache to expire the object from.</para>
51                         </parameter>
52                         <parameter name="Object" required="true">
53                                 <para>The name of the object to expire.</para>
54                         </parameter>
55                 </syntax>
56                 <description>
57                         <para>Expires (removes) an object from a sorcery memory cache.</para>
58                 </description>
59         </manager>
60         <manager name="SorceryMemoryCacheExpire" language="en_US">
61                 <synopsis>
62                         Expire (remove) ALL objects from a sorcery memory cache.
63                 </synopsis>
64                 <syntax>
65                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
66                         <parameter name="Cache" required="true">
67                                 <para>The name of the cache to expire all objects from.</para>
68                         </parameter>
69                 </syntax>
70                 <description>
71                         <para>Expires (removes) ALL objects from a sorcery memory cache.</para>
72                 </description>
73         </manager>
74         <manager name="SorceryMemoryCacheStaleObject" language="en_US">
75                 <synopsis>
76                         Mark an object in a sorcery memory cache as stale.
77                 </synopsis>
78                 <syntax>
79                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
80                         <parameter name="Cache" required="true">
81                                 <para>The name of the cache to mark the object as stale in.</para>
82                         </parameter>
83                         <parameter name="Object" required="true">
84                                 <para>The name of the object to mark as stale.</para>
85                         </parameter>
86                         <parameter name="Reload" required="false">
87                                 <para>If true, then immediately reload the object from the backend cache instead of waiting for the next retrieval</para>
88                         </parameter>
89                 </syntax>
90                 <description>
91                         <para>Marks an object as stale within a sorcery memory cache.</para>
92                 </description>
93         </manager>
94         <manager name="SorceryMemoryCacheStale" language="en_US">
95                 <synopsis>
96                         Marks ALL objects in a sorcery memory cache as stale.
97                 </synopsis>
98                 <syntax>
99                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
100                         <parameter name="Cache" required="true">
101                                 <para>The name of the cache to mark all object as stale in.</para>
102                         </parameter>
103                 </syntax>
104                 <description>
105                         <para>Marks ALL objects in a sorcery memory cache as stale.</para>
106                 </description>
107         </manager>
108         <manager name="SorceryMemoryCachePopulate" language="en_US">
109                 <synopsis>
110                         Expire all objects from a memory cache and populate it with all objects from the backend.
111                 </synopsis>
112                 <syntax>
113                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
114                         <parameter name="Cache" required="true">
115                                 <para>The name of the cache to populate.</para>
116                         </parameter>
117                 </syntax>
118                 <description>
119                         <para>Expires all objects from a memory cache and populate it with all objects from the backend.</para>
120                 </description>
121         </manager>
122  ***/
123
124 /*! \brief Structure for storing a memory cache */
125 struct sorcery_memory_cache {
126         /*! \brief The name of the memory cache */
127         char *name;
128         /*! \brief Objects in the cache */
129         struct ao2_container *objects;
130         /*! \brief The maximum number of objects permitted in the cache, 0 if no limit */
131         unsigned int maximum_objects;
132         /*! \brief The maximum time (in seconds) an object will stay in the cache, 0 if no limit */
133         unsigned int object_lifetime_maximum;
134         /*! \brief The amount of time (in seconds) before an object is marked as stale, 0 if disabled */
135         unsigned int object_lifetime_stale;
136         /*! \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
137         unsigned int expire_on_reload;
138         /*! \brief Whether this is a cache of the entire backend, 0 if disabled */
139         unsigned int full_backend_cache;
140         /*! \brief Heap of cached objects. Oldest object is at the top. */
141         struct ast_heap *object_heap;
142         /*! \brief Scheduler item for expiring oldest object. */
143         int expire_id;
144         /*! \brief scheduler id of stale update task */
145         int stale_update_sched_id;
146         /*! \brief An unreffed pointer to the sorcery instance, accessible only with lock held */
147         const struct ast_sorcery *sorcery;
148         /*! \brief The type of object we are caching */
149         char *object_type;
150         /*! TRUE if trying to stop the oldest object expiration scheduler item. */
151         unsigned int del_expire:1;
152 #ifdef TEST_FRAMEWORK
153         /*! \brief Variable used to indicate we should notify a test when we reach empty */
154         unsigned int cache_notify;
155         /*! \brief Mutex lock used for signaling when the cache has reached empty */
156         ast_mutex_t lock;
157         /*! \brief Condition used for signaling when the cache has reached empty */
158         ast_cond_t cond;
159         /*! \brief Variable that is set when the cache has reached empty */
160         unsigned int cache_completed;
161 #endif
162 };
163
164 /*! \brief Structure for stored a cached object */
165 struct sorcery_memory_cached_object {
166         /*! \brief The cached object */
167         void *object;
168         /*! \brief The time at which the object was created */
169         struct timeval created;
170         /*! \brief index required by heap */
171         ssize_t __heap_index;
172         /*! \brief scheduler id of stale update task */
173         int stale_update_sched_id;
174         /*! \brief Cached objectset for field and regex retrieval */
175         struct ast_variable *objectset;
176 };
177
178 /*! \brief Structure used for fields comparison */
179 struct sorcery_memory_cache_fields_cmp_params {
180         /*! \brief Pointer to the sorcery structure */
181         const struct ast_sorcery *sorcery;
182         /*! \brief The sorcery memory cache */
183         struct sorcery_memory_cache *cache;
184         /*! \brief Pointer to the fields to check */
185         const struct ast_variable *fields;
186         /*! \brief Regular expression for checking object id */
187         regex_t *regex;
188         /*! \brief Optional container to put object into */
189         struct ao2_container *container;
190 };
191
192 static void *sorcery_memory_cache_open(const char *data);
193 static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object);
194 static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type);
195 static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type);
196 static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
197         const char *id);
198 static void *sorcery_memory_cache_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type,
199         const struct ast_variable *fields);
200 static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type,
201         struct ao2_container *objects, const struct ast_variable *fields);
202 static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
203         struct ao2_container *objects, const char *regex);
204 static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
205 static void sorcery_memory_cache_close(void *data);
206
207 static struct ast_sorcery_wizard memory_cache_object_wizard = {
208         .name = "memory_cache",
209         .open = sorcery_memory_cache_open,
210         .create = sorcery_memory_cache_create,
211         .update = sorcery_memory_cache_create,
212         .delete = sorcery_memory_cache_delete,
213         .load = sorcery_memory_cache_load,
214         .reload = sorcery_memory_cache_reload,
215         .retrieve_id = sorcery_memory_cache_retrieve_id,
216         .retrieve_fields = sorcery_memory_cache_retrieve_fields,
217         .retrieve_multiple = sorcery_memory_cache_retrieve_multiple,
218         .retrieve_regex = sorcery_memory_cache_retrieve_regex,
219         .close = sorcery_memory_cache_close,
220 };
221
222 /*! \brief The bucket size for the container of caches */
223 #define CACHES_CONTAINER_BUCKET_SIZE 53
224
225 /*! \brief The default bucket size for the container of objects in the cache */
226 #define CACHE_CONTAINER_BUCKET_SIZE 53
227
228 /*! \brief Height of heap for cache object heap. Allows 31 initial objects */
229 #define CACHE_HEAP_INIT_HEIGHT 5
230
231 /*! \brief Container of created caches */
232 static struct ao2_container *caches;
233
234 /*! \brief Scheduler for cache management */
235 static struct ast_sched_context *sched;
236
237 #define PASSTHRU_UPDATE_THREAD_ID 0x5EED1E55
238 AST_THREADSTORAGE(passthru_update_id_storage);
239
240 static int is_passthru_update(void)
241 {
242         uint32_t *passthru_update_thread_id;
243
244         passthru_update_thread_id = ast_threadstorage_get(&passthru_update_id_storage,
245                 sizeof(*passthru_update_thread_id));
246         if (!passthru_update_thread_id) {
247                 return 0;
248         }
249
250         return *passthru_update_thread_id == PASSTHRU_UPDATE_THREAD_ID;
251 }
252
253 static void set_passthru_update(uint32_t value)
254 {
255         uint32_t *passthru_update_thread_id;
256
257         passthru_update_thread_id = ast_threadstorage_get(&passthru_update_id_storage,
258                 sizeof(*passthru_update_thread_id));
259         if (!passthru_update_thread_id) {
260                 ast_log(LOG_ERROR, "Could not set passthru update ID for sorcery memory cache thread\n");
261                 return;
262         }
263
264         *passthru_update_thread_id = value;
265 }
266
267 static void start_passthru_update(void)
268 {
269         set_passthru_update(PASSTHRU_UPDATE_THREAD_ID);
270 }
271
272 static void end_passthru_update(void)
273 {
274         set_passthru_update(0);
275 }
276
277 /*!
278  * \internal
279  * \brief Hashing function for the container holding caches
280  *
281  * \param obj A sorcery memory cache or name of one
282  * \param flags Hashing flags
283  *
284  * \return The hash of the memory cache name
285  */
286 static int sorcery_memory_cache_hash(const void *obj, int flags)
287 {
288         const struct sorcery_memory_cache *cache = obj;
289         const char *name = obj;
290         int hash;
291
292         switch (flags & OBJ_SEARCH_MASK) {
293         default:
294         case OBJ_SEARCH_OBJECT:
295                 name = cache->name;
296                 /* Fall through */
297         case OBJ_SEARCH_KEY:
298                 hash = ast_str_hash(name);
299                 break;
300         case OBJ_SEARCH_PARTIAL_KEY:
301                 /* Should never happen in hash callback. */
302                 ast_assert(0);
303                 hash = 0;
304                 break;
305         }
306         return hash;
307 }
308
309 /*!
310  * \internal
311  * \brief Comparison function for the container holding caches
312  *
313  * \param obj A sorcery memory cache
314  * \param arg A sorcery memory cache, or name of one
315  * \param flags Comparison flags
316  *
317  * \retval CMP_MATCH if the name is the same
318  * \retval 0 if the name does not match
319  */
320 static int sorcery_memory_cache_cmp(void *obj, void *arg, int flags)
321 {
322         const struct sorcery_memory_cache *left = obj;
323         const struct sorcery_memory_cache *right = arg;
324         const char *right_name = arg;
325         int cmp;
326
327         switch (flags & OBJ_SEARCH_MASK) {
328         default:
329         case OBJ_SEARCH_OBJECT:
330                 right_name = right->name;
331                 /* Fall through */
332         case OBJ_SEARCH_KEY:
333                 cmp = strcmp(left->name, right_name);
334                 break;
335         case OBJ_SEARCH_PARTIAL_KEY:
336                 cmp = strncmp(left->name, right_name, strlen(right_name));
337                 break;
338         }
339         return cmp ? 0 : CMP_MATCH;
340 }
341
342 /*!
343  * \internal
344  * \brief Hashing function for the container holding cached objects
345  *
346  * \param obj A cached object or id of one
347  * \param flags Hashing flags
348  *
349  * \return The hash of the cached object id
350  */
351 static int sorcery_memory_cached_object_hash(const void *obj, int flags)
352 {
353         const struct sorcery_memory_cached_object *cached = obj;
354         const char *name = obj;
355         int hash;
356
357         switch (flags & OBJ_SEARCH_MASK) {
358         default:
359         case OBJ_SEARCH_OBJECT:
360                 name = ast_sorcery_object_get_id(cached->object);
361                 /* Fall through */
362         case OBJ_SEARCH_KEY:
363                 hash = ast_str_hash(name);
364                 break;
365         case OBJ_SEARCH_PARTIAL_KEY:
366                 /* Should never happen in hash callback. */
367                 ast_assert(0);
368                 hash = 0;
369                 break;
370         }
371         return hash;
372 }
373
374 /*!
375  * \internal
376  * \brief Comparison function for the container holding cached objects
377  *
378  * \param obj A cached object
379  * \param arg A cached object, or id of one
380  * \param flags Comparison flags
381  *
382  * \retval CMP_MATCH if the id is the same
383  * \retval 0 if the id does not match
384  */
385 static int sorcery_memory_cached_object_cmp(void *obj, void *arg, int flags)
386 {
387         struct sorcery_memory_cached_object *left = obj;
388         struct sorcery_memory_cached_object *right = arg;
389         const char *right_name = arg;
390         int cmp;
391
392         switch (flags & OBJ_SEARCH_MASK) {
393         default:
394         case OBJ_SEARCH_OBJECT:
395                 right_name = ast_sorcery_object_get_id(right->object);
396                 /* Fall through */
397         case OBJ_SEARCH_KEY:
398                 cmp = strcmp(ast_sorcery_object_get_id(left->object), right_name);
399                 break;
400         case OBJ_SEARCH_PARTIAL_KEY:
401                 cmp = strncmp(ast_sorcery_object_get_id(left->object), right_name, strlen(right_name));
402                 break;
403         }
404         return cmp ? 0 : CMP_MATCH;
405 }
406
407 /*!
408  * \internal
409  * \brief Destructor function for a sorcery memory cache
410  *
411  * \param obj A sorcery memory cache
412  */
413 static void sorcery_memory_cache_destructor(void *obj)
414 {
415         struct sorcery_memory_cache *cache = obj;
416
417         ast_free(cache->name);
418         if (cache->object_heap) {
419                 ast_heap_destroy(cache->object_heap);
420         }
421         ao2_cleanup(cache->objects);
422         ast_free(cache->object_type);
423 }
424
425 /*!
426  * \internal
427  * \brief Destructor function for sorcery memory cached objects
428  *
429  * \param obj A sorcery memory cached object
430  */
431 static void sorcery_memory_cached_object_destructor(void *obj)
432 {
433         struct sorcery_memory_cached_object *cached = obj;
434
435         ao2_cleanup(cached->object);
436         ast_variables_destroy(cached->objectset);
437 }
438
439 static int schedule_cache_expiration(struct sorcery_memory_cache *cache);
440
441 /*!
442  * \internal
443  * \brief Remove an object from the cache.
444  *
445  * This removes the item from both the hashtable and the heap.
446  *
447  * \pre cache->objects is write-locked
448  *
449  * \param cache The cache from which the object is being removed.
450  * \param id The sorcery object id of the object to remove.
451  * \param reschedule Reschedule cache expiration if this was the oldest object.
452  *
453  * \retval 0 Success
454  * \retval non-zero Failure
455  */
456 static int remove_from_cache(struct sorcery_memory_cache *cache, const char *id, int reschedule)
457 {
458         struct sorcery_memory_cached_object *hash_object;
459         struct sorcery_memory_cached_object *oldest_object;
460         struct sorcery_memory_cached_object *heap_object;
461
462         hash_object = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NOLOCK);
463         if (!hash_object) {
464                 return -1;
465         }
466
467         ast_assert(!strcmp(ast_sorcery_object_get_id(hash_object->object), id));
468
469         oldest_object = ast_heap_peek(cache->object_heap, 1);
470         heap_object = ast_heap_remove(cache->object_heap, hash_object);
471
472         ast_assert(heap_object == hash_object);
473
474         ao2_ref(hash_object, -1);
475
476         if (reschedule && (oldest_object == heap_object)) {
477                 schedule_cache_expiration(cache);
478         }
479
480         return 0;
481 }
482
483 /*!
484  * \internal
485  * \brief Scheduler callback invoked to expire old objects
486  *
487  * \param data The opaque callback data (in our case, the memory cache)
488  */
489 static int expire_objects_from_cache(const void *data)
490 {
491         struct sorcery_memory_cache *cache = (struct sorcery_memory_cache *)data;
492         struct sorcery_memory_cached_object *cached;
493
494         /*
495          * We need to do deadlock avoidance between a non-scheduler thread
496          * blocking when trying to delete the scheduled entry for this
497          * callback because the scheduler thread is running this callback
498          * and this callback waiting for the cache->objects container lock
499          * that the blocked non-scheduler thread already holds.
500          */
501         while (ao2_trywrlock(cache->objects)) {
502                 if (cache->del_expire) {
503                         cache->expire_id = -1;
504                         ao2_ref(cache, -1);
505                         return 0;
506                 }
507                 sched_yield();
508         }
509
510         cache->expire_id = -1;
511
512         /* This is an optimization for objects which have been cached close to each other */
513         while ((cached = ast_heap_peek(cache->object_heap, 1))) {
514                 int expiration;
515
516                 expiration = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow());
517
518                 /* If the current oldest object has not yet expired stop and reschedule for it */
519                 if (expiration > 0) {
520                         break;
521                 }
522
523                 remove_from_cache(cache, ast_sorcery_object_get_id(cached->object), 0);
524         }
525
526         schedule_cache_expiration(cache);
527
528         ao2_unlock(cache->objects);
529
530         ao2_ref(cache, -1);
531
532         return 0;
533 }
534
535 /*!
536  * \internal
537  * \brief Remove all objects from the cache.
538  *
539  * This removes ALL objects from both the hash table and heap.
540  *
541  * \pre cache->objects is write-locked
542  *
543  * \param cache The cache to empty.
544  */
545 static void remove_all_from_cache(struct sorcery_memory_cache *cache)
546 {
547         while (ast_heap_pop(cache->object_heap)) {
548         }
549
550         ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
551                 NULL, NULL);
552
553         cache->del_expire = 1;
554         AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
555         cache->del_expire = 0;
556 }
557
558 /*!
559  * \internal
560  * \brief AO2 callback function for making an object stale immediately
561  *
562  * This changes the creation time of an object so it appears as though it is stale immediately.
563  *
564  * \param obj The cached object
565  * \param arg The cache itself
566  * \param flags Unused flags
567  */
568 static int object_stale_callback(void *obj, void *arg, int flags)
569 {
570         struct sorcery_memory_cached_object *cached = obj;
571         struct sorcery_memory_cache *cache = arg;
572
573         /* Since our granularity is seconds it's possible for something to retrieve us within a window
574          * where we wouldn't be treated as stale. To ensure that doesn't happen we use the configured stale
575          * time plus a second.
576          */
577         cached->created = ast_tvsub(cached->created, ast_samp2tv(cache->object_lifetime_stale + 1, 1));
578
579         return CMP_MATCH;
580 }
581
582 /*!
583  * \internal
584  * \brief Mark an object as stale explicitly.
585  *
586  * This changes the creation time of an object so it appears as though it is stale immediately.
587  *
588  * \pre cache->objects is read-locked
589  *
590  * \param cache The cache the object is in
591  * \param id The unique identifier of the object
592  *
593  * \retval 0 success
594  * \retval -1 failure
595  */
596 static int mark_object_as_stale_in_cache(struct sorcery_memory_cache *cache, const char *id)
597 {
598         struct sorcery_memory_cached_object *cached;
599
600         cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
601         if (!cached) {
602                 return -1;
603         }
604
605         ast_assert(!strcmp(ast_sorcery_object_get_id(cached->object), id));
606
607         object_stale_callback(cached, cache, 0);
608         ao2_ref(cached, -1);
609
610         return 0;
611 }
612
613 /*!
614  * \internal
615  * \brief Mark all objects as stale within a cache.
616  *
617  * This changes the creation time of ALL objects so they appear as though they are stale.
618  *
619  * \pre cache->objects is read-locked
620  *
621  * \param cache
622  */
623 static void mark_all_as_stale_in_cache(struct sorcery_memory_cache *cache)
624 {
625         ao2_callback(cache->objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_stale_callback, cache);
626 }
627
628 /*!
629  * \internal
630  * \brief Schedule a callback for cached object expiration.
631  *
632  * \pre cache->objects is write-locked
633  *
634  * \param cache The cache that is having its callback scheduled.
635  *
636  * \retval 0 success
637  * \retval -1 failure
638  */
639 static int schedule_cache_expiration(struct sorcery_memory_cache *cache)
640 {
641         struct sorcery_memory_cached_object *cached;
642         int expiration = 0;
643
644         if (!cache->object_lifetime_maximum) {
645                 return 0;
646         }
647
648         cache->del_expire = 1;
649         AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
650         cache->del_expire = 0;
651
652         cached = ast_heap_peek(cache->object_heap, 1);
653         if (!cached) {
654 #ifdef TEST_FRAMEWORK
655                 ast_mutex_lock(&cache->lock);
656                 cache->cache_completed = 1;
657                 ast_cond_signal(&cache->cond);
658                 ast_mutex_unlock(&cache->lock);
659 #endif
660                 return 0;
661         }
662
663         expiration = MAX(ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow()),
664                 1);
665
666         cache->expire_id = ast_sched_add(sched, expiration, expire_objects_from_cache, ao2_bump(cache));
667         if (cache->expire_id < 0) {
668                 ao2_ref(cache, -1);
669                 return -1;
670         }
671
672         return 0;
673 }
674
675 /*!
676  * \internal
677  * \brief Remove the oldest item from the cache.
678  *
679  * \pre cache->objects is write-locked
680  *
681  * \param cache The cache from which to remove the oldest object
682  *
683  * \retval 0 Success
684  * \retval non-zero Failure
685  */
686 static int remove_oldest_from_cache(struct sorcery_memory_cache *cache)
687 {
688         struct sorcery_memory_cached_object *heap_old_object;
689         struct sorcery_memory_cached_object *hash_old_object;
690
691         heap_old_object = ast_heap_pop(cache->object_heap);
692         if (!heap_old_object) {
693                 return -1;
694         }
695         hash_old_object = ao2_find(cache->objects, heap_old_object,
696                 OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NOLOCK);
697
698         ast_assert(heap_old_object == hash_old_object);
699
700         ao2_ref(hash_old_object, -1);
701
702         schedule_cache_expiration(cache);
703
704         return 0;
705 }
706
707 /*!
708  * \internal
709  * \brief Add a new object to the cache.
710  *
711  * \pre cache->objects is write-locked
712  *
713  * \param cache The cache in which to add the new object
714  * \param cached_object The object to add to the cache
715  *
716  * \retval 0 Success
717  * \retval non-zero Failure
718  */
719 static int add_to_cache(struct sorcery_memory_cache *cache,
720                 struct sorcery_memory_cached_object *cached_object)
721 {
722         struct sorcery_memory_cached_object *front;
723
724         if (!ao2_link_flags(cache->objects, cached_object, OBJ_NOLOCK)) {
725                 return -1;
726         }
727
728         if (cache->full_backend_cache && (front = ast_heap_peek(cache->object_heap, 1))) {
729                 /* For a full backend cache all objects share the same lifetime */
730                 cached_object->created = front->created;
731         }
732
733         if (ast_heap_push(cache->object_heap, cached_object)) {
734                 ao2_find(cache->objects, cached_object,
735                         OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NODATA | OBJ_NOLOCK);
736                 return -1;
737         }
738
739         if (cache->expire_id == -1) {
740                 schedule_cache_expiration(cache);
741         }
742
743         return 0;
744 }
745
746 /*!
747  * \internal
748  * \brief Allocate a cached object for caching an object
749  *
750  * \param sorcery The sorcery instance
751  * \param cache The sorcery memory cache
752  * \param object The object to cache
753  *
754  * \retval non-NULL success
755  * \retval NULL failure
756  */
757 static struct sorcery_memory_cached_object *sorcery_memory_cached_object_alloc(const struct ast_sorcery *sorcery,
758         const struct sorcery_memory_cache *cache, void *object)
759 {
760         struct sorcery_memory_cached_object *cached;
761
762         cached = ao2_alloc(sizeof(*cached), sorcery_memory_cached_object_destructor);
763         if (!cached) {
764                 return NULL;
765         }
766
767         cached->object = ao2_bump(object);
768         cached->created = ast_tvnow();
769         cached->stale_update_sched_id = -1;
770
771         if (cache->full_backend_cache) {
772                 /* A cached objectset allows us to easily perform all retrieval operations in a
773                  * minimal of time.
774                  */
775                 cached->objectset = ast_sorcery_objectset_create(sorcery, object);
776                 if (!cached->objectset) {
777                         ao2_ref(cached, -1);
778                         return NULL;
779                 }
780         }
781
782         return cached;
783 }
784
785 /*!
786  * \internal
787  * \brief Callback function to cache an object in a memory cache
788  *
789  * \param sorcery The sorcery instance
790  * \param data The sorcery memory cache
791  * \param object The object to cache
792  *
793  * \retval 0 success
794  * \retval -1 failure
795  */
796 static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object)
797 {
798         struct sorcery_memory_cache *cache = data;
799         struct sorcery_memory_cached_object *cached;
800
801         cached = sorcery_memory_cached_object_alloc(sorcery, cache, object);
802         if (!cached) {
803                 return -1;
804         }
805
806         /* As there is no guarantee that this won't be called by multiple threads wanting to cache
807          * the same object we remove any old ones, which turns this into a create/update function
808          * in reality. As well since there's no guarantee that the object in the cache is the same
809          * one here we remove any old objects using the object identifier.
810          */
811
812         ao2_wrlock(cache->objects);
813         remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
814         if (cache->maximum_objects && ao2_container_count(cache->objects) >= cache->maximum_objects) {
815                 if (remove_oldest_from_cache(cache)) {
816                         ast_log(LOG_ERROR, "Unable to make room in cache for sorcery object '%s'.\n",
817                                 ast_sorcery_object_get_id(object));
818                         ao2_unlock(cache->objects);
819                         ao2_ref(cached, -1);
820                         return -1;
821                 }
822                 ast_assert(ao2_container_count(cache->objects) != cache->maximum_objects);
823         }
824         if (add_to_cache(cache, cached)) {
825                 ast_log(LOG_ERROR, "Unable to add object '%s' to the cache\n",
826                         ast_sorcery_object_get_id(object));
827                 ao2_unlock(cache->objects);
828                 ao2_ref(cached, -1);
829                 return -1;
830         }
831         ao2_unlock(cache->objects);
832
833         ao2_ref(cached, -1);
834         return 0;
835 }
836
837 /*!
838  * \internal
839  * \brief AO2 callback function for adding an object to a memory cache
840  *
841  * \param obj The cached object
842  * \param arg The sorcery instance
843  * \param data The cache itself
844  * \param flags Unused flags
845  */
846 static int object_add_to_cache_callback(void *obj, void *arg, void *data, int flags)
847 {
848         struct sorcery_memory_cache *cache = data;
849         struct sorcery_memory_cached_object *cached;
850
851         cached = sorcery_memory_cached_object_alloc(arg, cache, obj);
852         if (!cached) {
853                 return CMP_STOP;
854         }
855
856         add_to_cache(cache, cached);
857         ao2_ref(cached, -1);
858
859         return 0;
860 }
861
862 struct stale_cache_update_task_data {
863         struct ast_sorcery *sorcery;
864         struct sorcery_memory_cache *cache;
865         char *type;
866 };
867
868 static void stale_cache_update_task_data_destructor(void *obj)
869 {
870         struct stale_cache_update_task_data *task_data = obj;
871
872         ao2_cleanup(task_data->cache);
873         ast_sorcery_unref(task_data->sorcery);
874         ast_free(task_data->type);
875 }
876
877 static struct stale_cache_update_task_data *stale_cache_update_task_data_alloc(struct ast_sorcery *sorcery,
878                 struct sorcery_memory_cache *cache, const char *type)
879 {
880         struct stale_cache_update_task_data *task_data;
881
882         task_data = ao2_alloc_options(sizeof(*task_data), stale_cache_update_task_data_destructor,
883                 AO2_ALLOC_OPT_LOCK_NOLOCK);
884         if (!task_data) {
885                 return NULL;
886         }
887
888         task_data->sorcery = ao2_bump(sorcery);
889         task_data->cache = ao2_bump(cache);
890         task_data->type = ast_strdup(type);
891         if (!task_data->type) {
892                 ao2_ref(task_data, -1);
893                 return NULL;
894         }
895
896         return task_data;
897 }
898
899 static int stale_cache_update(const void *data)
900 {
901         struct stale_cache_update_task_data *task_data = (struct stale_cache_update_task_data *) data;
902         struct ao2_container *backend_objects;
903
904         start_passthru_update();
905         backend_objects = ast_sorcery_retrieve_by_fields(task_data->sorcery, task_data->type,
906                 AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
907         end_passthru_update();
908
909         if (!backend_objects) {
910                 task_data->cache->stale_update_sched_id = -1;
911                 ao2_ref(task_data, -1);
912                 return 0;
913         }
914
915         if (task_data->cache->maximum_objects && ao2_container_count(backend_objects) >= task_data->cache->maximum_objects) {
916                 ast_log(LOG_ERROR, "The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
917                         ao2_container_count(backend_objects), task_data->cache->name, task_data->cache->maximum_objects);
918                 task_data->cache->stale_update_sched_id = -1;
919                 ao2_ref(task_data, -1);
920                 return 0;
921         }
922
923         ao2_wrlock(task_data->cache->objects);
924         remove_all_from_cache(task_data->cache);
925         ao2_callback_data(backend_objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_add_to_cache_callback,
926                 task_data->sorcery, task_data->cache);
927
928         /* If the number of cached objects does not match the number of backend objects we encountered a memory allocation
929          * failure and the cache is incomplete, so drop everything and fall back to querying the backend directly
930          * as it may be able to provide what is wanted.
931          */
932         if (ao2_container_count(task_data->cache->objects) != ao2_container_count(backend_objects)) {
933                 ast_log(LOG_WARNING, "The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
934                         ao2_container_count(backend_objects), ao2_container_count(task_data->cache->objects), task_data->cache->name);
935                 remove_all_from_cache(task_data->cache);
936         }
937
938         ao2_unlock(task_data->cache->objects);
939         ao2_ref(backend_objects, -1);
940
941         task_data->cache->stale_update_sched_id = -1;
942         ao2_ref(task_data, -1);
943
944         return 0;
945 }
946
947 struct stale_update_task_data {
948         struct ast_sorcery *sorcery;
949         struct sorcery_memory_cache *cache;
950         void *object;
951 };
952
953 static void stale_update_task_data_destructor(void *obj)
954 {
955         struct stale_update_task_data *task_data = obj;
956
957         ao2_cleanup(task_data->cache);
958         ao2_cleanup(task_data->object);
959         ast_sorcery_unref(task_data->sorcery);
960 }
961
962 static struct stale_update_task_data *stale_update_task_data_alloc(struct ast_sorcery *sorcery,
963                 struct sorcery_memory_cache *cache, const char *type, void *object)
964 {
965         struct stale_update_task_data *task_data;
966
967         task_data = ao2_alloc_options(sizeof(*task_data), stale_update_task_data_destructor,
968                 AO2_ALLOC_OPT_LOCK_NOLOCK);
969         if (!task_data) {
970                 return NULL;
971         }
972
973         task_data->sorcery = ao2_bump(sorcery);
974         task_data->cache = ao2_bump(cache);
975         task_data->object = ao2_bump(object);
976
977         return task_data;
978 }
979
980 static int stale_item_update(const void *data)
981 {
982         struct stale_update_task_data *task_data = (struct stale_update_task_data *) data;
983         void *object;
984
985         start_passthru_update();
986
987         object = ast_sorcery_retrieve_by_id(task_data->sorcery,
988                 ast_sorcery_object_get_type(task_data->object),
989                 ast_sorcery_object_get_id(task_data->object));
990         if (!object) {
991                 ast_debug(1, "Backend no longer has object type '%s' ID '%s'. Removing from cache\n",
992                         ast_sorcery_object_get_type(task_data->object),
993                         ast_sorcery_object_get_id(task_data->object));
994                 sorcery_memory_cache_delete(task_data->sorcery, task_data->cache,
995                         task_data->object);
996         } else {
997                 ast_debug(1, "Refreshing stale cache object type '%s' ID '%s'\n",
998                         ast_sorcery_object_get_type(task_data->object),
999                         ast_sorcery_object_get_id(task_data->object));
1000                 sorcery_memory_cache_create(task_data->sorcery, task_data->cache,
1001                         object);
1002         }
1003
1004         ast_test_suite_event_notify("SORCERY_MEMORY_CACHE_REFRESHED", "Cache: %s\r\nType: %s\r\nName: %s\r\n",
1005                 task_data->cache->name, ast_sorcery_object_get_type(task_data->object),
1006                 ast_sorcery_object_get_id(task_data->object));
1007
1008         ao2_ref(task_data, -1);
1009         end_passthru_update();
1010
1011         return 0;
1012 }
1013
1014 /*!
1015  * \internal
1016  * \brief Populate the cache with all objects from the backend
1017  *
1018  * \pre cache->objects is write-locked
1019  *
1020  * \param sorcery The sorcery instance
1021  * \param type The type of object
1022  * \param cache The sorcery memory cache
1023  */
1024 static void memory_cache_populate(const struct ast_sorcery *sorcery, const char *type, struct sorcery_memory_cache *cache)
1025 {
1026         struct ao2_container *backend_objects;
1027
1028         start_passthru_update();
1029         backend_objects = ast_sorcery_retrieve_by_fields(sorcery, type, AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
1030         end_passthru_update();
1031
1032         if (!backend_objects) {
1033                 /* This will occur in off-nominal memory allocation failure scenarios */
1034                 return;
1035         }
1036
1037         if (cache->maximum_objects && ao2_container_count(backend_objects) >= cache->maximum_objects) {
1038                 ast_log(LOG_ERROR, "The backend contains %d objects while the sorcery memory cache '%s' is explicitly configured to only allow %d\n",
1039                         ao2_container_count(backend_objects), cache->name, cache->maximum_objects);
1040                 return;
1041         }
1042
1043         ao2_callback_data(backend_objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_add_to_cache_callback,
1044                 (struct ast_sorcery*)sorcery, cache);
1045
1046         /* If the number of cached objects does not match the number of backend objects we encountered a memory allocation
1047          * failure and the cache is incomplete, so drop everything and fall back to querying the backend directly
1048          * as it may be able to provide what is wanted.
1049          */
1050         if (ao2_container_count(cache->objects) != ao2_container_count(backend_objects)) {
1051                 ast_log(LOG_WARNING, "The backend contains %d objects while only %d could be added to sorcery memory cache '%s'\n",
1052                         ao2_container_count(backend_objects), ao2_container_count(cache->objects), cache->name);
1053                 remove_all_from_cache(cache);
1054         }
1055
1056         ao2_ref(backend_objects, -1);
1057 }
1058
1059 /*!
1060  * \internal
1061  * \brief Determine if a full backend cache update is needed and do it
1062  *
1063  * \param sorcery The sorcery instance
1064  * \param type The type of object
1065  * \param cache The sorcery memory cache
1066  */
1067 static void memory_cache_full_update(const struct ast_sorcery *sorcery, const char *type, struct sorcery_memory_cache *cache)
1068 {
1069         if (!cache->full_backend_cache) {
1070                 return;
1071         }
1072
1073         ao2_wrlock(cache->objects);
1074         if (!ao2_container_count(cache->objects)) {
1075                 memory_cache_populate(sorcery, type, cache);
1076         }
1077         ao2_unlock(cache->objects);
1078 }
1079
1080 /*!
1081  * \internal
1082  * \brief Queue a full cache update
1083  *
1084  * \param sorcery The sorcery instance
1085  * \param cache The sorcery memory cache
1086  * \param type The type of object
1087  */
1088 static void memory_cache_stale_update_full(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
1089         const char *type)
1090 {
1091         ao2_wrlock(cache->objects);
1092         if (cache->stale_update_sched_id == -1) {
1093                 struct stale_cache_update_task_data *task_data;
1094
1095                 task_data = stale_cache_update_task_data_alloc((struct ast_sorcery *) sorcery,
1096                         cache, type);
1097                 if (task_data) {
1098                         cache->stale_update_sched_id = ast_sched_add(sched, 1,
1099                                 stale_cache_update, task_data);
1100                 }
1101                 if (cache->stale_update_sched_id < 0) {
1102                         ao2_cleanup(task_data);
1103                 }
1104         }
1105         ao2_unlock(cache->objects);
1106 }
1107
1108 /*!
1109  * \internal
1110  * \brief Queue a stale object update
1111  *
1112  * \param sorcery The sorcery instance
1113  * \param cache The sorcery memory cache
1114  * \param cached The cached object
1115  */
1116 static void memory_cache_stale_update_object(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
1117         struct sorcery_memory_cached_object *cached)
1118 {
1119         ao2_lock(cached);
1120         if (cached->stale_update_sched_id == -1) {
1121                 struct stale_update_task_data *task_data;
1122
1123                 task_data = stale_update_task_data_alloc((struct ast_sorcery *) sorcery,
1124                         cache, ast_sorcery_object_get_type(cached->object), cached->object);
1125                 if (task_data) {
1126                         ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
1127                                 ast_sorcery_object_get_type(cached->object), ast_sorcery_object_get_id(cached->object));
1128                         cached->stale_update_sched_id = ast_sched_add(sched, 1,
1129                                 stale_item_update, task_data);
1130                 }
1131                 if (cached->stale_update_sched_id < 0) {
1132                         ao2_cleanup(task_data);
1133                         ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n",
1134                                 ast_sorcery_object_get_type(cached->object), ast_sorcery_object_get_id(cached->object));
1135                 }
1136         }
1137         ao2_unlock(cached);
1138 }
1139
1140 /*!
1141  * \internal
1142  * \brief Check whether an object (or cache) is stale and queue an update
1143  *
1144  * \param sorcery The sorcery instance
1145  * \param cache The sorcery memory cache
1146  * \param cached The cached object
1147  */
1148 static void memory_cache_stale_check_object(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
1149         struct sorcery_memory_cached_object *cached)
1150 {
1151         struct timeval elapsed;
1152
1153         if (!cache->object_lifetime_stale) {
1154                 return;
1155         }
1156
1157         /* For a full cache as every object has the same expiration/staleness we can do the same check */
1158         elapsed = ast_tvsub(ast_tvnow(), cached->created);
1159
1160         if (elapsed.tv_sec < cache->object_lifetime_stale) {
1161                 return;
1162         }
1163
1164         if (cache->full_backend_cache) {
1165                 memory_cache_stale_update_full(sorcery, cache, ast_sorcery_object_get_type(cached->object));
1166         } else {
1167                 memory_cache_stale_update_object(sorcery, cache, cached);
1168         }
1169
1170 }
1171
1172 /*!
1173  * \internal
1174  * \brief Check whether the entire cache is stale or not and queue an update
1175  *
1176  * \param sorcery The sorcery instance
1177  * \param cache The sorcery memory cache
1178  *
1179  * \note Unlike \ref memory_cache_stale_check this does not require  an explicit object
1180  */
1181 static void memory_cache_stale_check(const struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache)
1182 {
1183         struct sorcery_memory_cached_object *cached;
1184
1185         ao2_rdlock(cache->objects);
1186         cached = ao2_bump(ast_heap_peek(cache->object_heap, 1));
1187         ao2_unlock(cache->objects);
1188
1189         if (!cached) {
1190                 return;
1191         }
1192
1193         memory_cache_stale_check_object(sorcery, cache, cached);
1194         ao2_ref(cached, -1);
1195 }
1196
1197 /*!
1198  * \internal
1199  * \brief Callback function to retrieve an object from a memory cache
1200  *
1201  * \param sorcery The sorcery instance
1202  * \param data The sorcery memory cache
1203  * \param type The type of the object to retrieve
1204  * \param id The id of the object to retrieve
1205  *
1206  * \retval non-NULL success
1207  * \retval NULL failure
1208  */
1209 static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
1210 {
1211         struct sorcery_memory_cache *cache = data;
1212         struct sorcery_memory_cached_object *cached;
1213         void *object;
1214
1215         if (is_passthru_update()) {
1216                 return NULL;
1217         }
1218
1219         memory_cache_full_update(sorcery, type, cache);
1220
1221         cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY);
1222         if (!cached) {
1223                 return NULL;
1224         }
1225
1226         ast_assert(!strcmp(ast_sorcery_object_get_id(cached->object), id));
1227
1228         memory_cache_stale_check_object(sorcery, cache, cached);
1229
1230         object = ao2_bump(cached->object);
1231         ao2_ref(cached, -1);
1232
1233         return object;
1234 }
1235
1236 /*!
1237  * \internal
1238  * \brief AO2 callback function for comparing a retrieval request and finding applicable objects
1239  *
1240  * \param obj The cached object
1241  * \param arg The comparison parameters
1242  * \param flags Unused flags
1243  */
1244 static int sorcery_memory_cache_fields_cmp(void *obj, void *arg, int flags)
1245 {
1246         struct sorcery_memory_cached_object *cached = obj;
1247         const struct sorcery_memory_cache_fields_cmp_params *params = arg;
1248         RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
1249
1250         if (params->regex) {
1251                 /* If a regular expression has been provided see if it matches, otherwise move on */
1252                 if (!regexec(params->regex, ast_sorcery_object_get_id(cached->object), 0, NULL, 0)) {
1253                         ao2_link(params->container, cached->object);
1254                 }
1255                 return 0;
1256         } else if (params->fields &&
1257              (!ast_variable_lists_match(cached->objectset, params->fields, 0))) {
1258                 /* If we can't turn the object into an object set OR if differences exist between the fields
1259                  * passed in and what are present on the object they are not a match.
1260                  */
1261                 return 0;
1262         }
1263
1264         if (params->container) {
1265                 ao2_link(params->container, cached->object);
1266
1267                 /* As multiple objects are being returned keep going */
1268                 return 0;
1269         } else {
1270                 /* Immediately stop and return, we only want a single object */
1271                 return CMP_MATCH | CMP_STOP;
1272         }
1273 }
1274
1275 /*!
1276  * \internal
1277  * \brief Callback function to retrieve a single object based on fields
1278  *
1279  * \param sorcery The sorcery instance
1280  * \param data The sorcery memory cache
1281  * \param type The type of the object to retrieve
1282  * \param fields Any explicit fields to search for
1283  */
1284 static void *sorcery_memory_cache_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type,
1285         const struct ast_variable *fields)
1286 {
1287         struct sorcery_memory_cache *cache = data;
1288         struct sorcery_memory_cache_fields_cmp_params params = {
1289                 .sorcery = sorcery,
1290                 .cache = cache,
1291                 .fields = fields,
1292         };
1293         struct sorcery_memory_cached_object *cached;
1294         void *object = NULL;
1295
1296         if (is_passthru_update() || !cache->full_backend_cache || !fields) {
1297                 return NULL;
1298         }
1299
1300         cached = ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
1301
1302         if (cached) {
1303                 memory_cache_stale_check_object(sorcery, cache, cached);
1304                 object = ao2_bump(cached->object);
1305                 ao2_ref(cached, -1);
1306         }
1307
1308         return object;
1309 }
1310
1311 /*!
1312  * \internal
1313  * \brief Callback function to retrieve multiple objects from a memory cache
1314  *
1315  * \param sorcery The sorcery instance
1316  * \param data The sorcery memory cache
1317  * \param type The type of the object to retrieve
1318  * \param objects Container to place the objects into
1319  * \param fields Any explicit fields to search for
1320  */
1321 static void sorcery_memory_cache_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type,
1322         struct ao2_container *objects, const struct ast_variable *fields)
1323 {
1324         struct sorcery_memory_cache *cache = data;
1325         struct sorcery_memory_cache_fields_cmp_params params = {
1326                 .sorcery = sorcery,
1327                 .cache = cache,
1328                 .fields = fields,
1329                 .container = objects,
1330         };
1331
1332         if (is_passthru_update() || !cache->full_backend_cache) {
1333                 return;
1334         }
1335
1336         memory_cache_full_update(sorcery, type, cache);
1337         ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
1338
1339         if (ao2_container_count(objects)) {
1340                 memory_cache_stale_check(sorcery, cache);
1341         }
1342 }
1343
1344 /*!
1345  * \internal
1346  * \brief Callback function to retrieve multiple objects using a regex on the object id
1347  *
1348  * \param sorcery The sorcery instance
1349  * \param data The sorcery memory cache
1350  * \param type The type of the object to retrieve
1351  * \param objects Container to place the objects into
1352  * \param regex Regular expression to apply to the object id
1353  */
1354 static void sorcery_memory_cache_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type,
1355         struct ao2_container *objects, const char *regex)
1356 {
1357         struct sorcery_memory_cache *cache = data;
1358         regex_t expression;
1359         struct sorcery_memory_cache_fields_cmp_params params = {
1360                 .sorcery = sorcery,
1361                 .cache = cache,
1362                 .container = objects,
1363                 .regex = &expression,
1364         };
1365
1366         if (is_passthru_update() || !cache->full_backend_cache || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) {
1367                 return;
1368         }
1369
1370         memory_cache_full_update(sorcery, type, cache);
1371         ao2_callback(cache->objects, 0, sorcery_memory_cache_fields_cmp, &params);
1372         regfree(&expression);
1373
1374         if (ao2_container_count(objects)) {
1375                 memory_cache_stale_check(sorcery, cache);
1376         }
1377 }
1378
1379 /*!
1380  * \internal
1381  * \brief Callback function to finish configuring the memory cache
1382  *
1383  * \param data The sorcery memory cache
1384  * \param sorcery The sorcery instance
1385  * \param type The type of object being loaded
1386  */
1387 static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type)
1388 {
1389         struct sorcery_memory_cache *cache = data;
1390
1391         /* If no name was explicitly specified generate one given the sorcery instance and object type */
1392         if (ast_strlen_zero(cache->name)) {
1393                 ast_asprintf(&cache->name, "%s/%s", ast_sorcery_get_module(sorcery), type);
1394         }
1395
1396         ao2_link(caches, cache);
1397         ast_debug(1, "Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
1398                 cache->name, sorcery, ast_sorcery_get_module(sorcery), type);
1399
1400         cache->sorcery = sorcery;
1401         cache->object_type = ast_strdup(type);
1402 }
1403
1404 /*!
1405  * \internal
1406  * \brief Callback function to expire objects from the memory cache on reload (if configured)
1407  *
1408  * \param data The sorcery memory cache
1409  * \param sorcery The sorcery instance
1410  * \param type The type of object being reloaded
1411  */
1412 static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type)
1413 {
1414         struct sorcery_memory_cache *cache = data;
1415
1416         if (!cache->expire_on_reload) {
1417                 return;
1418         }
1419
1420         ao2_wrlock(cache->objects);
1421         remove_all_from_cache(cache);
1422         ao2_unlock(cache->objects);
1423 }
1424
1425 /*!
1426  * \internal
1427  * \brief Function used to take an unsigned integer based configuration option and parse it
1428  *
1429  * \param value The string value of the configuration option
1430  * \param result The unsigned integer to place the result in
1431  *
1432  * \retval 0 failure
1433  * \retval 1 success
1434  */
1435 static int configuration_parse_unsigned_integer(const char *value, unsigned int *result)
1436 {
1437         if (ast_strlen_zero(value) || !strncmp(value, "-", 1)) {
1438                 return 0;
1439         }
1440
1441         return sscanf(value, "%30u", result);
1442 }
1443
1444 static int age_cmp(void *a, void *b)
1445 {
1446         return ast_tvcmp(((struct sorcery_memory_cached_object *) b)->created,
1447                         ((struct sorcery_memory_cached_object *) a)->created);
1448 }
1449
1450 /*!
1451  * \internal
1452  * \brief Callback function to create a new sorcery memory cache using provided configuration
1453  *
1454  * \param data A stringified configuration for the memory cache
1455  *
1456  * \retval non-NULL success
1457  * \retval NULL failure
1458  */
1459 static void *sorcery_memory_cache_open(const char *data)
1460 {
1461         char *options = ast_strdup(data), *option;
1462         RAII_VAR(struct sorcery_memory_cache *, cache, NULL, ao2_cleanup);
1463
1464         cache = ao2_alloc_options(sizeof(*cache), sorcery_memory_cache_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
1465         if (!cache) {
1466                 return NULL;
1467         }
1468
1469         cache->expire_id = -1;
1470         cache->stale_update_sched_id = -1;
1471
1472         /* If no configuration options have been provided this memory cache will operate in a default
1473          * configuration.
1474          */
1475         while (!ast_strlen_zero(options) && (option = strsep(&options, ","))) {
1476                 char *name = strsep(&option, "="), *value = option;
1477
1478                 if (!strcasecmp(name, "name")) {
1479                         if (ast_strlen_zero(value)) {
1480                                 ast_log(LOG_ERROR, "A name must be specified for the memory cache\n");
1481                                 return NULL;
1482                         }
1483                         ast_free(cache->name);
1484                         cache->name = ast_strdup(value);
1485                 } else if (!strcasecmp(name, "maximum_objects")) {
1486                         if (configuration_parse_unsigned_integer(value, &cache->maximum_objects) != 1) {
1487                                 ast_log(LOG_ERROR, "Unsupported maximum objects value of '%s' used for memory cache\n",
1488                                         value);
1489                                 return NULL;
1490                         }
1491                 } else if (!strcasecmp(name, "object_lifetime_maximum")) {
1492                         if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_maximum) != 1) {
1493                                 ast_log(LOG_ERROR, "Unsupported object maximum lifetime value of '%s' used for memory cache\n",
1494                                         value);
1495                                 return NULL;
1496                         }
1497                 } else if (!strcasecmp(name, "object_lifetime_stale")) {
1498                         if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_stale) != 1) {
1499                                 ast_log(LOG_ERROR, "Unsupported object stale lifetime value of '%s' used for memory cache\n",
1500                                         value);
1501                                 return NULL;
1502                         }
1503                 } else if (!strcasecmp(name, "expire_on_reload")) {
1504                         cache->expire_on_reload = ast_true(value);
1505                 } else if (!strcasecmp(name, "full_backend_cache")) {
1506                         cache->full_backend_cache = ast_true(value);
1507                 } else {
1508                         ast_log(LOG_ERROR, "Unsupported option '%s' used for memory cache\n", name);
1509                         return NULL;
1510                 }
1511         }
1512
1513         cache->objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
1514                 cache->maximum_objects ? cache->maximum_objects : CACHE_CONTAINER_BUCKET_SIZE,
1515                 sorcery_memory_cached_object_hash, sorcery_memory_cached_object_cmp);
1516         if (!cache->objects) {
1517                 ast_log(LOG_ERROR, "Could not create a container to hold cached objects for memory cache\n");
1518                 return NULL;
1519         }
1520
1521         cache->object_heap = ast_heap_create(CACHE_HEAP_INIT_HEIGHT, age_cmp,
1522                 offsetof(struct sorcery_memory_cached_object, __heap_index));
1523         if (!cache->object_heap) {
1524                 ast_log(LOG_ERROR, "Could not create heap to hold cached objects\n");
1525                 return NULL;
1526         }
1527
1528         /* The memory cache is not linked to the caches container until the load callback is invoked.
1529          * Linking occurs there so an intelligent cache name can be constructed using the module of
1530          * the sorcery instance and the specific object type if no cache name was specified as part
1531          * of the configuration.
1532          */
1533
1534         /* This is done as RAII_VAR will drop the reference */
1535         return ao2_bump(cache);
1536 }
1537
1538 /*!
1539  * \internal
1540  * \brief Callback function to delete an object from a memory cache
1541  *
1542  * \param sorcery The sorcery instance
1543  * \param data The sorcery memory cache
1544  * \param object The object to cache
1545  *
1546  * \retval 0 success
1547  * \retval -1 failure
1548  */
1549 static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object)
1550 {
1551         struct sorcery_memory_cache *cache = data;
1552         int res;
1553
1554         ao2_wrlock(cache->objects);
1555         res = remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
1556         ao2_unlock(cache->objects);
1557
1558         if (res) {
1559                 ast_debug(1, "Unable to delete object '%s' from sorcery cache\n", ast_sorcery_object_get_id(object));
1560         }
1561
1562         return res;
1563 }
1564
1565 /*!
1566  * \internal
1567  * \brief Callback function to terminate a memory cache
1568  *
1569  * \param data The sorcery memory cache
1570  */
1571 static void sorcery_memory_cache_close(void *data)
1572 {
1573         struct sorcery_memory_cache *cache = data;
1574
1575         /* This can occur if a cache is created but never loaded */
1576         if (!ast_strlen_zero(cache->name)) {
1577                 ao2_unlink(caches, cache);
1578         }
1579
1580         if (cache->object_lifetime_maximum) {
1581                 /* If object lifetime support is enabled we need to explicitly drop all cached objects here
1582                  * and stop the scheduled task. Failure to do so could potentially keep the cache around for
1583                  * a prolonged period of time.
1584                  */
1585                 ao2_wrlock(cache->objects);
1586                 remove_all_from_cache(cache);
1587                 ao2_unlock(cache->objects);
1588         }
1589
1590         if (cache->full_backend_cache) {
1591                 ao2_wrlock(cache->objects);
1592                 cache->sorcery = NULL;
1593                 ao2_unlock(cache->objects);
1594         }
1595
1596         ao2_ref(cache, -1);
1597 }
1598
1599 /*!
1600  * \internal
1601  * \brief CLI tab completion for cache names
1602  */
1603 static char *sorcery_memory_cache_complete_name(const char *word, int state)
1604 {
1605         struct sorcery_memory_cache *cache;
1606         struct ao2_iterator it_caches;
1607         int wordlen = strlen(word);
1608         int which = 0;
1609         char *result = NULL;
1610
1611         it_caches = ao2_iterator_init(caches, 0);
1612         while ((cache = ao2_iterator_next(&it_caches))) {
1613                 if (!strncasecmp(word, cache->name, wordlen)
1614                         && ++which > state) {
1615                         result = ast_strdup(cache->name);
1616                 }
1617                 ao2_ref(cache, -1);
1618                 if (result) {
1619                         break;
1620                 }
1621         }
1622         ao2_iterator_destroy(&it_caches);
1623         return result;
1624 }
1625
1626 /*!
1627  * \internal
1628  * \brief CLI command implementation for 'sorcery memory cache show'
1629  */
1630 static char *sorcery_memory_cache_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1631 {
1632         struct sorcery_memory_cache *cache;
1633
1634         switch (cmd) {
1635         case CLI_INIT:
1636                 e->command = "sorcery memory cache show";
1637                 e->usage =
1638                     "Usage: sorcery memory cache show <name>\n"
1639                     "       Show sorcery memory cache configuration and statistics.\n";
1640                 return NULL;
1641         case CLI_GENERATE:
1642                 if (a->pos == 4) {
1643                         return sorcery_memory_cache_complete_name(a->word, a->n);
1644                 } else {
1645                         return NULL;
1646                 }
1647         }
1648
1649         if (a->argc != 5) {
1650                 return CLI_SHOWUSAGE;
1651         }
1652
1653         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1654         if (!cache) {
1655                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1656                 return CLI_FAILURE;
1657         }
1658
1659         ast_cli(a->fd, "Sorcery memory cache: %s\n", cache->name);
1660         ast_cli(a->fd, "Number of objects within cache: %d\n", ao2_container_count(cache->objects));
1661         if (cache->maximum_objects) {
1662                 ast_cli(a->fd, "Maximum allowed objects: %d\n", cache->maximum_objects);
1663         } else {
1664                 ast_cli(a->fd, "There is no limit on the maximum number of objects in the cache\n");
1665         }
1666         if (cache->object_lifetime_maximum) {
1667                 ast_cli(a->fd, "Number of seconds before object expires: %d\n", cache->object_lifetime_maximum);
1668         } else {
1669                 ast_cli(a->fd, "Object expiration is not enabled - cached objects will not expire\n");
1670         }
1671         if (cache->object_lifetime_stale) {
1672                 ast_cli(a->fd, "Number of seconds before object becomes stale: %d\n", cache->object_lifetime_stale);
1673         } else {
1674                 ast_cli(a->fd, "Object staleness is not enabled - cached objects will not go stale\n");
1675         }
1676         ast_cli(a->fd, "Expire all objects on reload: %s\n", AST_CLI_ONOFF(cache->expire_on_reload));
1677
1678         ao2_ref(cache, -1);
1679
1680         return CLI_SUCCESS;
1681 }
1682
1683 /*! \brief Structure used to pass data for printing cached object information */
1684 struct print_object_details {
1685         /*! \brief The sorcery memory cache */
1686         struct sorcery_memory_cache *cache;
1687         /*! \brief The CLI arguments */
1688         struct ast_cli_args *a;
1689 };
1690
1691 /*!
1692  * \internal
1693  * \brief Callback function for displaying object within the cache
1694  */
1695 static int sorcery_memory_cache_print_object(void *obj, void *arg, int flags)
1696 {
1697 #define FORMAT "%-25.25s %-15u %-15u \n"
1698         struct sorcery_memory_cached_object *cached = obj;
1699         struct print_object_details *details = arg;
1700         int seconds_until_expire = 0, seconds_until_stale = 0;
1701
1702         if (details->cache->object_lifetime_maximum) {
1703                 seconds_until_expire = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_maximum, 1)), ast_tvnow()) / 1000;
1704         }
1705         if (details->cache->object_lifetime_stale) {
1706                 seconds_until_stale = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_stale, 1)), ast_tvnow()) / 1000;
1707         }
1708
1709         ast_cli(details->a->fd, FORMAT, ast_sorcery_object_get_id(cached->object), MAX(seconds_until_stale, 0), MAX(seconds_until_expire, 0));
1710
1711         return CMP_MATCH;
1712 #undef FORMAT
1713 }
1714
1715 /*!
1716  * \internal
1717  * \brief CLI command implementation for 'sorcery memory cache dump'
1718  */
1719 static char *sorcery_memory_cache_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1720 {
1721 #define FORMAT "%-25.25s %-15.15s %-15.15s \n"
1722         struct sorcery_memory_cache *cache;
1723         struct print_object_details details;
1724
1725         switch (cmd) {
1726         case CLI_INIT:
1727                 e->command = "sorcery memory cache dump";
1728                 e->usage =
1729                     "Usage: sorcery memory cache dump <name>\n"
1730                     "       Dump a list of the objects within the cache, listed by object identifier.\n";
1731                 return NULL;
1732         case CLI_GENERATE:
1733                 if (a->pos == 4) {
1734                         return sorcery_memory_cache_complete_name(a->word, a->n);
1735                 } else {
1736                         return NULL;
1737                 }
1738         }
1739
1740         if (a->argc != 5) {
1741                 return CLI_SHOWUSAGE;
1742         }
1743
1744         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1745         if (!cache) {
1746                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1747                 return CLI_FAILURE;
1748         }
1749
1750         details.cache = cache;
1751         details.a = a;
1752
1753         ast_cli(a->fd, "Dumping sorcery memory cache '%s':\n", cache->name);
1754         if (!cache->object_lifetime_stale) {
1755                 ast_cli(a->fd, " * Staleness is not enabled - objects will not go stale\n");
1756         }
1757         if (!cache->object_lifetime_maximum) {
1758                 ast_cli(a->fd, " * Object lifetime is not enabled - objects will not expire\n");
1759         }
1760         ast_cli(a->fd, FORMAT, "Object Name", "Stale In", "Expires In");
1761         ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
1762         ao2_callback(cache->objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_memory_cache_print_object, &details);
1763         ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
1764         ast_cli(a->fd, "Total number of objects cached: %d\n", ao2_container_count(cache->objects));
1765
1766         ao2_ref(cache, -1);
1767
1768         return CLI_SUCCESS;
1769 #undef FORMAT
1770 }
1771
1772 /*!
1773  * \internal
1774  * \brief CLI tab completion for cached object names
1775  */
1776 static char *sorcery_memory_cache_complete_object_name(const char *cache_name, const char *word, int state)
1777 {
1778         struct sorcery_memory_cache *cache;
1779         struct sorcery_memory_cached_object *cached;
1780         struct ao2_iterator it_cached;
1781         int wordlen = strlen(word);
1782         int which = 0;
1783         char *result = NULL;
1784
1785         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
1786         if (!cache) {
1787                 return NULL;
1788         }
1789
1790         it_cached = ao2_iterator_init(cache->objects, 0);
1791         while ((cached = ao2_iterator_next(&it_cached))) {
1792                 if (!strncasecmp(word, ast_sorcery_object_get_id(cached->object), wordlen)
1793                         && ++which > state) {
1794                         result = ast_strdup(ast_sorcery_object_get_id(cached->object));
1795                 }
1796                 ao2_ref(cached, -1);
1797                 if (result) {
1798                         break;
1799                 }
1800         }
1801         ao2_iterator_destroy(&it_cached);
1802
1803         ao2_ref(cache, -1);
1804
1805         return result;
1806 }
1807
1808 /*!
1809  * \internal
1810  * \brief CLI command implementation for 'sorcery memory cache expire'
1811  */
1812 static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1813 {
1814         struct sorcery_memory_cache *cache;
1815
1816         switch (cmd) {
1817         case CLI_INIT:
1818                 e->command = "sorcery memory cache expire";
1819                 e->usage =
1820                     "Usage: sorcery memory cache expire <cache name> [object name]\n"
1821                     "       Expire a specific object or ALL objects within a sorcery memory cache.\n";
1822                 return NULL;
1823         case CLI_GENERATE:
1824                 if (a->pos == 4) {
1825                         return sorcery_memory_cache_complete_name(a->word, a->n);
1826                 } else if (a->pos == 5) {
1827                         return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
1828                 } else {
1829                         return NULL;
1830                 }
1831         }
1832
1833         if (a->argc < 5 || a->argc > 6) {
1834                 return CLI_SHOWUSAGE;
1835         }
1836
1837         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1838         if (!cache) {
1839                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1840                 return CLI_FAILURE;
1841         }
1842
1843         ao2_wrlock(cache->objects);
1844         if (a->argc == 5) {
1845                 remove_all_from_cache(cache);
1846                 ast_cli(a->fd, "All objects have been removed from cache '%s'\n", a->argv[4]);
1847         } else {
1848                 if (!remove_from_cache(cache, a->argv[5], 1)) {
1849                         ast_cli(a->fd, "Successfully expired object '%s' from cache '%s'\n", a->argv[5], a->argv[4]);
1850                 } else {
1851                         ast_cli(a->fd, "Object '%s' was not expired from cache '%s' as it was not found\n", a->argv[5],
1852                                 a->argv[4]);
1853                 }
1854         }
1855         ao2_unlock(cache->objects);
1856
1857         ao2_ref(cache, -1);
1858
1859         return CLI_SUCCESS;
1860 }
1861
1862 /*!
1863  * \internal
1864  * \brief CLI command implementation for 'sorcery memory cache stale'
1865  */
1866 static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1867 {
1868         struct sorcery_memory_cache *cache;
1869         int reload = 0;
1870
1871         switch (cmd) {
1872         case CLI_INIT:
1873                 e->command = "sorcery memory cache stale";
1874                 e->usage =
1875                     "Usage: sorcery memory cache stale <cache name> [object name [reload]]\n"
1876                     "       Mark a specific object or ALL objects as stale in a sorcery memory cache.\n"
1877                     "       If \"reload\" is specified, then the object is marked stale and immediately\n"
1878                     "       retrieved from backend storage to repopulate the cache\n";
1879                 return NULL;
1880         case CLI_GENERATE:
1881                 if (a->pos == 4) {
1882                         return sorcery_memory_cache_complete_name(a->word, a->n);
1883                 } else if (a->pos == 5) {
1884                         return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
1885                 } else if (a->pos == 6) {
1886                         static const char * const completions[] = { "reload", NULL };
1887                         return ast_cli_complete(a->word, completions, a->n);
1888                 } else {
1889                         return NULL;
1890                 }
1891         }
1892
1893         if (a->argc < 5 || a->argc > 7) {
1894                 return CLI_SHOWUSAGE;
1895         }
1896
1897         if (a->argc == 7) {
1898                 if (!strcasecmp(a->argv[6], "reload")) {
1899                         reload = 1;
1900                 } else {
1901                         return CLI_SHOWUSAGE;
1902                 }
1903         }
1904
1905         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1906         if (!cache) {
1907                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1908                 return CLI_FAILURE;
1909         }
1910
1911         if (!cache->object_lifetime_stale) {
1912                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have staleness enabled\n", a->argv[4]);
1913                 ao2_ref(cache, -1);
1914                 return CLI_FAILURE;
1915         }
1916
1917         ao2_rdlock(cache->objects);
1918         if (a->argc == 5) {
1919                 mark_all_as_stale_in_cache(cache);
1920                 ast_cli(a->fd, "Marked all objects in sorcery memory cache '%s' as stale\n", a->argv[4]);
1921         } else {
1922                 if (!mark_object_as_stale_in_cache(cache, a->argv[5])) {
1923                         ast_cli(a->fd, "Successfully marked object '%s' in memory cache '%s' as stale\n",
1924                                 a->argv[5], a->argv[4]);
1925                         if (reload) {
1926                                 struct sorcery_memory_cached_object *cached;
1927
1928                                 cached = ao2_find(cache->objects, a->argv[5], OBJ_SEARCH_KEY | OBJ_NOLOCK);
1929                                 if (cached) {
1930                                         memory_cache_stale_update_object(cache->sorcery, cache, cached);
1931                                         ao2_ref(cached, -1);
1932                                 }
1933                         }
1934                 } else {
1935                         ast_cli(a->fd, "Object '%s' in sorcery memory cache '%s' could not be marked as stale as it was not found\n",
1936                                 a->argv[5], a->argv[4]);
1937                 }
1938         }
1939         ao2_unlock(cache->objects);
1940
1941         ao2_ref(cache, -1);
1942
1943         return CLI_SUCCESS;
1944 }
1945
1946 /*!
1947  * \internal
1948  * \brief CLI command implementation for 'sorcery memory cache populate'
1949  */
1950 static char *sorcery_memory_cache_populate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1951 {
1952         struct sorcery_memory_cache *cache;
1953
1954         switch (cmd) {
1955         case CLI_INIT:
1956                 e->command = "sorcery memory cache populate";
1957                 e->usage =
1958                     "Usage: sorcery memory cache populate <cache name>\n"
1959                     "       Expire all objects in the cache and populate it with ALL objects from backend.\n";
1960                 return NULL;
1961         case CLI_GENERATE:
1962                 if (a->pos == 4) {
1963                         return sorcery_memory_cache_complete_name(a->word, a->n);
1964                 } else {
1965                         return NULL;
1966                 }
1967         }
1968
1969         if (a->argc != 5) {
1970                 return CLI_SHOWUSAGE;
1971         }
1972
1973         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1974         if (!cache) {
1975                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1976                 return CLI_FAILURE;
1977         }
1978
1979         if (!cache->full_backend_cache) {
1980                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have full backend caching enabled\n", a->argv[4]);
1981                 ao2_ref(cache, -1);
1982                 return CLI_FAILURE;
1983         }
1984
1985         ao2_wrlock(cache->objects);
1986         if (!cache->sorcery) {
1987                 ast_cli(a->fd, "Specified sorcery memory cache '%s' is no longer active\n", a->argv[4]);
1988                 ao2_unlock(cache->objects);
1989                 ao2_ref(cache, -1);
1990                 return CLI_FAILURE;
1991         }
1992
1993         remove_all_from_cache(cache);
1994         memory_cache_populate(cache->sorcery, cache->object_type, cache);
1995
1996         ast_cli(a->fd, "Specified sorcery memory cache '%s' has been populated with '%d' objects from the backend\n",
1997                 a->argv[4], ao2_container_count(cache->objects));
1998
1999         ao2_unlock(cache->objects);
2000
2001         ao2_ref(cache, -1);
2002
2003         return CLI_SUCCESS;
2004 }
2005
2006 static struct ast_cli_entry cli_memory_cache[] = {
2007         AST_CLI_DEFINE(sorcery_memory_cache_show, "Show sorcery memory cache information"),
2008         AST_CLI_DEFINE(sorcery_memory_cache_dump, "Dump all objects within a sorcery memory cache"),
2009         AST_CLI_DEFINE(sorcery_memory_cache_expire, "Expire a specific object or ALL objects within a sorcery memory cache"),
2010         AST_CLI_DEFINE(sorcery_memory_cache_stale, "Mark a specific object or ALL objects as stale within a sorcery memory cache"),
2011         AST_CLI_DEFINE(sorcery_memory_cache_populate, "Clear and populate the sorcery memory cache with objects from the backend"),
2012 };
2013
2014 /*!
2015  * \internal
2016  * \brief AMI command implementation for 'SorceryMemoryCacheExpireObject'
2017  */
2018 static int sorcery_memory_cache_ami_expire_object(struct mansession *s, const struct message *m)
2019 {
2020         const char *cache_name = astman_get_header(m, "Cache");
2021         const char *object_name = astman_get_header(m, "Object");
2022         struct sorcery_memory_cache *cache;
2023         int res;
2024
2025         if (ast_strlen_zero(cache_name)) {
2026                 astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that a cache name be provided.\n");
2027                 return 0;
2028         } else if (ast_strlen_zero(object_name)) {
2029                 astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that an object name be provided\n");
2030                 return 0;
2031         }
2032
2033         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
2034         if (!cache) {
2035                 astman_send_error(s, m, "The provided cache does not exist\n");
2036                 return 0;
2037         }
2038
2039         ao2_wrlock(cache->objects);
2040         res = remove_from_cache(cache, object_name, 1);
2041         ao2_unlock(cache->objects);
2042
2043         ao2_ref(cache, -1);
2044
2045         if (!res) {
2046                 astman_send_ack(s, m, "The provided object was expired from the cache\n");
2047         } else {
2048                 astman_send_error(s, m, "The provided object could not be expired from the cache\n");
2049         }
2050
2051         return 0;
2052 }
2053
2054 /*!
2055  * \internal
2056  * \brief AMI command implementation for 'SorceryMemoryCacheExpire'
2057  */
2058 static int sorcery_memory_cache_ami_expire(struct mansession *s, const struct message *m)
2059 {
2060         const char *cache_name = astman_get_header(m, "Cache");
2061         struct sorcery_memory_cache *cache;
2062
2063         if (ast_strlen_zero(cache_name)) {
2064                 astman_send_error(s, m, "SorceryMemoryCacheExpire requires that a cache name be provided.\n");
2065                 return 0;
2066         }
2067
2068         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
2069         if (!cache) {
2070                 astman_send_error(s, m, "The provided cache does not exist\n");
2071                 return 0;
2072         }
2073
2074         ao2_wrlock(cache->objects);
2075         remove_all_from_cache(cache);
2076         ao2_unlock(cache->objects);
2077
2078         ao2_ref(cache, -1);
2079
2080         astman_send_ack(s, m, "All objects were expired from the cache\n");
2081
2082         return 0;
2083 }
2084
2085 /*!
2086  * \internal
2087  * \brief AMI command implementation for 'SorceryMemoryCacheStaleObject'
2088  */
2089 static int sorcery_memory_cache_ami_stale_object(struct mansession *s, const struct message *m)
2090 {
2091         const char *cache_name = astman_get_header(m, "Cache");
2092         const char *object_name = astman_get_header(m, "Object");
2093         const char *reload = astman_get_header(m, "Reload");
2094         struct sorcery_memory_cache *cache;
2095         int res;
2096
2097         if (ast_strlen_zero(cache_name)) {
2098                 astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that a cache name be provided.\n");
2099                 return 0;
2100         } else if (ast_strlen_zero(object_name)) {
2101                 astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that an object name be provided\n");
2102                 return 0;
2103         }
2104
2105         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
2106         if (!cache) {
2107                 astman_send_error(s, m, "The provided cache does not exist\n");
2108                 return 0;
2109         }
2110
2111         ao2_rdlock(cache->objects);
2112
2113         res = mark_object_as_stale_in_cache(cache, object_name);
2114
2115         if (ast_true(reload)) {
2116                 struct sorcery_memory_cached_object *cached;
2117
2118                 cached = ao2_find(cache->objects, object_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
2119                 if (cached) {
2120                         memory_cache_stale_update_object(cache->sorcery, cache, cached);
2121                         ao2_ref(cached, -1);
2122                 }
2123         }
2124
2125         ao2_unlock(cache->objects);
2126
2127         ao2_ref(cache, -1);
2128
2129         if (!res) {
2130                 astman_send_ack(s, m, "The provided object was marked as stale in the cache\n");
2131         } else {
2132                 astman_send_error(s, m, "The provided object could not be marked as stale in the cache\n");
2133         }
2134
2135         return 0;
2136 }
2137
2138 /*!
2139  * \internal
2140  * \brief AMI command implementation for 'SorceryMemoryCacheStale'
2141  */
2142 static int sorcery_memory_cache_ami_stale(struct mansession *s, const struct message *m)
2143 {
2144         const char *cache_name = astman_get_header(m, "Cache");
2145         struct sorcery_memory_cache *cache;
2146
2147         if (ast_strlen_zero(cache_name)) {
2148                 astman_send_error(s, m, "SorceryMemoryCacheStale requires that a cache name be provided.\n");
2149                 return 0;
2150         }
2151
2152         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
2153         if (!cache) {
2154                 astman_send_error(s, m, "The provided cache does not exist\n");
2155                 return 0;
2156         }
2157
2158         ao2_rdlock(cache->objects);
2159         mark_all_as_stale_in_cache(cache);
2160         ao2_unlock(cache->objects);
2161
2162         ao2_ref(cache, -1);
2163
2164         astman_send_ack(s, m, "All objects were marked as stale in the cache\n");
2165
2166         return 0;
2167 }
2168
2169 /*!
2170  * \internal
2171  * \brief AMI command implementation for 'SorceryMemoryCachePopulate'
2172  */
2173 static int sorcery_memory_cache_ami_populate(struct mansession *s, const struct message *m)
2174 {
2175         const char *cache_name = astman_get_header(m, "Cache");
2176         struct sorcery_memory_cache *cache;
2177
2178         if (ast_strlen_zero(cache_name)) {
2179                 astman_send_error(s, m, "SorceryMemoryCachePopulate requires that a cache name be provided.\n");
2180                 return 0;
2181         }
2182
2183         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
2184         if (!cache) {
2185                 astman_send_error(s, m, "The provided cache does not exist\n");
2186                 return 0;
2187         }
2188
2189         if (!cache->full_backend_cache) {
2190                 astman_send_error(s, m, "The provided cache does not have full backend caching enabled\n");
2191                 ao2_ref(cache, -1);
2192                 return 0;
2193         }
2194
2195         ao2_wrlock(cache->objects);
2196         if (!cache->sorcery) {
2197                 astman_send_error(s, m, "The provided cache is no longer active\n");
2198                 ao2_unlock(cache->objects);
2199                 ao2_ref(cache, -1);
2200                 return 0;
2201         }
2202
2203         remove_all_from_cache(cache);
2204         memory_cache_populate(cache->sorcery, cache->object_type, cache);
2205
2206         ao2_unlock(cache->objects);
2207
2208         ao2_ref(cache, -1);
2209
2210         astman_send_ack(s, m, "Cache has been expired and populated\n");
2211
2212         return 0;
2213 }
2214
2215 #ifdef TEST_FRAMEWORK
2216
2217 /*! \brief Dummy sorcery object */
2218 struct test_sorcery_object {
2219         SORCERY_OBJECT(details);
2220 };
2221
2222 /*!
2223  * \internal
2224  * \brief Allocator for test object
2225  *
2226  * \param id The identifier for the object
2227  *
2228  * \retval non-NULL success
2229  * \retval NULL failure
2230  */
2231 static void *test_sorcery_object_alloc(const char *id)
2232 {
2233         return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
2234 }
2235
2236 /*!
2237  * \internal
2238  * \brief Allocator for test sorcery instance
2239  *
2240  * \retval non-NULL success
2241  * \retval NULL failure
2242  */
2243 static struct ast_sorcery *alloc_and_initialize_sorcery(void)
2244 {
2245         struct ast_sorcery *sorcery;
2246
2247         if (!(sorcery = ast_sorcery_open())) {
2248                 return NULL;
2249         }
2250
2251         if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
2252                 ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2253                 ast_sorcery_unref(sorcery);
2254                 return NULL;
2255         }
2256
2257         return sorcery;
2258 }
2259
2260 AST_TEST_DEFINE(open_with_valid_options)
2261 {
2262         int res = AST_TEST_PASS;
2263         struct sorcery_memory_cache *cache;
2264
2265         switch (cmd) {
2266         case TEST_INIT:
2267                 info->name = "open_with_valid_options";
2268                 info->category = "/res/res_sorcery_memory_cache/";
2269                 info->summary = "Attempt to create sorcery memory caches using valid options";
2270                 info->description = "This test performs the following:\n"
2271                         "\t* Creates a memory cache with default configuration\n"
2272                         "\t* Creates a memory cache with a maximum object count of 10 and verifies it\n"
2273                         "\t* Creates a memory cache with a maximum object lifetime of 60 and verifies it\n"
2274                         "\t* Creates a memory cache with a stale object lifetime of 90 and verifies it";
2275                 return AST_TEST_NOT_RUN;
2276         case TEST_EXECUTE:
2277                 break;
2278         }
2279
2280         cache = sorcery_memory_cache_open("");
2281         if (!cache) {
2282                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default configuration\n");
2283                 res = AST_TEST_FAIL;
2284         } else {
2285                 sorcery_memory_cache_close(cache);
2286         }
2287
2288         cache = sorcery_memory_cache_open("maximum_objects=10");
2289         if (!cache) {
2290                 ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object count of 10\n");
2291                 res = AST_TEST_FAIL;
2292         } else {
2293                 if (cache->maximum_objects != 10) {
2294                         ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of 10 but it has '%u'\n",
2295                                 cache->maximum_objects);
2296                 }
2297                 sorcery_memory_cache_close(cache);
2298         }
2299
2300         cache = sorcery_memory_cache_open("object_lifetime_maximum=60");
2301         if (!cache) {
2302                 ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object lifetime of 60\n");
2303                 res = AST_TEST_FAIL;
2304         } else {
2305                 if (cache->object_lifetime_maximum != 60) {
2306                         ast_test_status_update(test, "Created a sorcery memory cache with a maximum object lifetime of 60 but it has '%u'\n",
2307                                 cache->object_lifetime_maximum);
2308                 }
2309                 sorcery_memory_cache_close(cache);
2310         }
2311
2312         cache = sorcery_memory_cache_open("object_lifetime_stale=90");
2313         if (!cache) {
2314                 ast_test_status_update(test, "Failed to create a sorcery memory cache with a stale object lifetime of 90\n");
2315                 res = AST_TEST_FAIL;
2316         } else {
2317                 if (cache->object_lifetime_stale != 90) {
2318                         ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of 90 but it has '%u'\n",
2319                                 cache->object_lifetime_stale);
2320                 }
2321                 sorcery_memory_cache_close(cache);
2322         }
2323
2324
2325         return res;
2326 }
2327
2328 AST_TEST_DEFINE(open_with_invalid_options)
2329 {
2330         int res = AST_TEST_PASS;
2331         struct sorcery_memory_cache *cache;
2332
2333         switch (cmd) {
2334         case TEST_INIT:
2335                 info->name = "open_with_invalid_options";
2336                 info->category = "/res/res_sorcery_memory_cache/";
2337                 info->summary = "Attempt to create sorcery memory caches using invalid options";
2338                 info->description = "This test attempts to perform the following:\n"
2339                         "\t* Create a memory cache with an empty name\n"
2340                         "\t* Create a memory cache with a maximum object count of -1\n"
2341                         "\t* Create a memory cache with a maximum object count of toast\n"
2342                         "\t* Create a memory cache with a maximum object lifetime of -1\n"
2343                         "\t* Create a memory cache with a maximum object lifetime of toast\n"
2344                         "\t* Create a memory cache with a stale object lifetime of -1\n"
2345                         "\t* Create a memory cache with a stale object lifetime of toast";
2346                 return AST_TEST_NOT_RUN;
2347         case TEST_EXECUTE:
2348                 break;
2349         }
2350
2351         cache = sorcery_memory_cache_open("name=");
2352         if (cache) {
2353                 ast_test_status_update(test, "Created a sorcery memory cache with an empty name\n");
2354                 sorcery_memory_cache_close(cache);
2355                 res = AST_TEST_FAIL;
2356         }
2357
2358         cache = sorcery_memory_cache_open("maximum_objects=-1");
2359         if (cache) {
2360                 ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of -1\n");
2361                 sorcery_memory_cache_close(cache);
2362                 res = AST_TEST_FAIL;
2363         }
2364
2365         cache = sorcery_memory_cache_open("maximum_objects=toast");
2366         if (cache) {
2367                 ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of toast\n");
2368                 sorcery_memory_cache_close(cache);
2369                 res = AST_TEST_FAIL;
2370         }
2371
2372         cache = sorcery_memory_cache_open("object_lifetime_maximum=-1");
2373         if (cache) {
2374                 ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of -1\n");
2375                 sorcery_memory_cache_close(cache);
2376                 res = AST_TEST_FAIL;
2377         }
2378
2379         cache = sorcery_memory_cache_open("object_lifetime_maximum=toast");
2380         if (cache) {
2381                 ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of toast\n");
2382                 sorcery_memory_cache_close(cache);
2383                 res = AST_TEST_FAIL;
2384         }
2385
2386         cache = sorcery_memory_cache_open("object_lifetime_stale=-1");
2387         if (cache) {
2388                 ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of -1\n");
2389                 sorcery_memory_cache_close(cache);
2390                 res = AST_TEST_FAIL;
2391         }
2392
2393         cache = sorcery_memory_cache_open("object_lifetime_stale=toast");
2394         if (cache) {
2395                 ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of toast\n");
2396                 sorcery_memory_cache_close(cache);
2397                 res = AST_TEST_FAIL;
2398         }
2399
2400         cache = sorcery_memory_cache_open("tacos");
2401         if (cache) {
2402                 ast_test_status_update(test, "Created a sorcery memory cache with an invalid configuration option 'tacos'\n");
2403                 sorcery_memory_cache_close(cache);
2404                 res = AST_TEST_FAIL;
2405         }
2406
2407         return res;
2408 }
2409
2410 AST_TEST_DEFINE(create_and_retrieve)
2411 {
2412         int res = AST_TEST_FAIL;
2413         struct ast_sorcery *sorcery = NULL;
2414         struct sorcery_memory_cache *cache = NULL;
2415         RAII_VAR(void *, object, NULL, ao2_cleanup);
2416         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
2417
2418         switch (cmd) {
2419         case TEST_INIT:
2420                 info->name = "create";
2421                 info->category = "/res/res_sorcery_memory_cache/";
2422                 info->summary = "Attempt to create an object in the cache";
2423                 info->description = "This test performs the following:\n"
2424                         "\t* Creates a memory cache with default options\n"
2425                         "\t* Creates a sorcery instance with a test object\n"
2426                         "\t* Creates a test object with an id of test\n"
2427                         "\t* Pushes the test object into the memory cache\n"
2428                         "\t* Confirms that the test object is in the cache";
2429                 return AST_TEST_NOT_RUN;
2430         case TEST_EXECUTE:
2431                 break;
2432         }
2433
2434         cache = sorcery_memory_cache_open("");
2435         if (!cache) {
2436                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
2437                 goto cleanup;
2438         }
2439
2440         if (ao2_container_count(cache->objects)) {
2441                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
2442                 goto cleanup;
2443         }
2444
2445         sorcery = alloc_and_initialize_sorcery();
2446         if (!sorcery) {
2447                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2448                 goto cleanup;
2449         }
2450
2451         object = ast_sorcery_alloc(sorcery, "test", "test");
2452         if (!object) {
2453                 ast_test_status_update(test, "Failed to allocate a test object\n");
2454                 goto cleanup;
2455         }
2456
2457         sorcery_memory_cache_create(sorcery, cache, object);
2458
2459         if (!ao2_container_count(cache->objects)) {
2460                 ast_test_status_update(test, "Added test object to memory cache but cache remains empty\n");
2461                 goto cleanup;
2462         }
2463
2464         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
2465         if (!cached_object) {
2466                 ast_test_status_update(test, "Object placed into memory cache could not be retrieved\n");
2467                 goto cleanup;
2468         }
2469
2470         if (cached_object != object) {
2471                 ast_test_status_update(test, "Object retrieved from memory cached is not the one we cached\n");
2472                 goto cleanup;
2473         }
2474
2475         res = AST_TEST_PASS;
2476
2477 cleanup:
2478         if (cache) {
2479                 sorcery_memory_cache_close(cache);
2480         }
2481         if (sorcery) {
2482                 ast_sorcery_unref(sorcery);
2483         }
2484
2485         return res;
2486 }
2487
2488 AST_TEST_DEFINE(update)
2489 {
2490         int res = AST_TEST_FAIL;
2491         struct ast_sorcery *sorcery = NULL;
2492         struct sorcery_memory_cache *cache = NULL;
2493         RAII_VAR(void *, original_object, NULL, ao2_cleanup);
2494         RAII_VAR(void *, updated_object, NULL, ao2_cleanup);
2495         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
2496
2497         switch (cmd) {
2498         case TEST_INIT:
2499                 info->name = "create";
2500                 info->category = "/res/res_sorcery_memory_cache/";
2501                 info->summary = "Attempt to create and then update an object in the cache";
2502                 info->description = "This test performs the following:\n"
2503                         "\t* Creates a memory cache with default options\n"
2504                         "\t* Creates a sorcery instance with a test object\n"
2505                         "\t* Creates a test object with an id of test\n"
2506                         "\t* Pushes the test object into the memory cache\n"
2507                         "\t* Confirms that the test object is in the cache\n"
2508                         "\t* Creates a new test object with the same id of test\n"
2509                         "\t* Pushes the new test object into the memory cache\n"
2510                         "\t* Confirms that the new test object has replaced the old one";
2511                 return AST_TEST_NOT_RUN;
2512         case TEST_EXECUTE:
2513                 break;
2514         }
2515
2516         cache = sorcery_memory_cache_open("");
2517         if (!cache) {
2518                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
2519                 goto cleanup;
2520         }
2521
2522         if (ao2_container_count(cache->objects)) {
2523                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
2524                 goto cleanup;
2525         }
2526
2527         sorcery = alloc_and_initialize_sorcery();
2528         if (!sorcery) {
2529                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2530                 goto cleanup;
2531         }
2532
2533         original_object = ast_sorcery_alloc(sorcery, "test", "test");
2534         if (!original_object) {
2535                 ast_test_status_update(test, "Failed to allocate a test object\n");
2536                 goto cleanup;
2537         }
2538
2539         sorcery_memory_cache_create(sorcery, cache, original_object);
2540
2541         updated_object = ast_sorcery_alloc(sorcery, "test", "test");
2542         if (!updated_object) {
2543                 ast_test_status_update(test, "Failed to allocate an updated test object\n");
2544                 goto cleanup;
2545         }
2546
2547         sorcery_memory_cache_create(sorcery, cache, updated_object);
2548
2549         if (ao2_container_count(cache->objects) != 1) {
2550                 ast_test_status_update(test, "Added updated test object to memory cache but cache now contains %d objects instead of 1\n",
2551                         ao2_container_count(cache->objects));
2552                 goto cleanup;
2553         }
2554
2555         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
2556         if (!cached_object) {
2557                 ast_test_status_update(test, "Updated object placed into memory cache could not be retrieved\n");
2558                 goto cleanup;
2559         }
2560
2561         if (cached_object == original_object) {
2562                 ast_test_status_update(test, "Updated object placed into memory cache but old one is being retrieved\n");
2563                 goto cleanup;
2564         } else if (cached_object != updated_object) {
2565                 ast_test_status_update(test, "Updated object placed into memory cache but different one is being retrieved\n");
2566                 goto cleanup;
2567         }
2568
2569         res = AST_TEST_PASS;
2570
2571 cleanup:
2572         if (cache) {
2573                 sorcery_memory_cache_close(cache);
2574         }
2575         if (sorcery) {
2576                 ast_sorcery_unref(sorcery);
2577         }
2578
2579         return res;
2580 }
2581
2582 AST_TEST_DEFINE(delete)
2583 {
2584         int res = AST_TEST_FAIL;
2585         struct ast_sorcery *sorcery = NULL;
2586         struct sorcery_memory_cache *cache = NULL;
2587         RAII_VAR(void *, object, NULL, ao2_cleanup);
2588         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
2589
2590         switch (cmd) {
2591         case TEST_INIT:
2592                 info->name = "delete";
2593                 info->category = "/res/res_sorcery_memory_cache/";
2594                 info->summary = "Attempt to create and then delete an object in the cache";
2595                 info->description = "This test performs the following:\n"
2596                         "\t* Creates a memory cache with default options\n"
2597                         "\t* Creates a sorcery instance with a test object\n"
2598                         "\t* Creates a test object with an id of test\n"
2599                         "\t* Pushes the test object into the memory cache\n"
2600                         "\t* Confirms that the test object is in the cache\n"
2601                         "\t* Deletes the test object from the cache\n"
2602                         "\t* Confirms that the test object is no longer in the cache";
2603                 return AST_TEST_NOT_RUN;
2604         case TEST_EXECUTE:
2605                 break;
2606         }
2607
2608         cache = sorcery_memory_cache_open("");
2609         if (!cache) {
2610                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
2611                 goto cleanup;
2612         }
2613
2614         if (ao2_container_count(cache->objects)) {
2615                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
2616                 goto cleanup;
2617         }
2618
2619         sorcery = alloc_and_initialize_sorcery();
2620         if (!sorcery) {
2621                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2622                 goto cleanup;
2623         }
2624
2625         object = ast_sorcery_alloc(sorcery, "test", "test");
2626         if (!object) {
2627                 ast_test_status_update(test, "Failed to allocate a test object\n");
2628                 goto cleanup;
2629         }
2630
2631         sorcery_memory_cache_create(sorcery, cache, object);
2632
2633         if (!ao2_container_count(cache->objects)) {
2634                 ast_test_status_update(test, "Added test object to memory cache but cache contains no objects\n");
2635                 goto cleanup;
2636         }
2637
2638         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
2639         if (!cached_object) {
2640                 ast_test_status_update(test, "Test object placed into memory cache could not be retrieved\n");
2641                 goto cleanup;
2642         }
2643
2644         ao2_ref(cached_object, -1);
2645         cached_object = NULL;
2646
2647         sorcery_memory_cache_delete(sorcery, cache, object);
2648
2649         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
2650         if (cached_object) {
2651                 ast_test_status_update(test, "Test object deleted from memory cache can still be retrieved\n");
2652                 goto cleanup;
2653         }
2654
2655         res = AST_TEST_PASS;
2656
2657 cleanup:
2658         if (cache) {
2659                 sorcery_memory_cache_close(cache);
2660         }
2661         if (sorcery) {
2662                 ast_sorcery_unref(sorcery);
2663         }
2664
2665         return res;
2666 }
2667
2668 static int check_cache_content(struct ast_test *test, struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
2669                 const char **in_cache, size_t num_in_cache, const char **not_in_cache, size_t num_not_in_cache)
2670 {
2671         int i;
2672         int res = 0;
2673         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
2674
2675         for (i = 0; i < num_in_cache; ++i) {
2676                 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", in_cache[i]);
2677                 if (!cached_object) {
2678                         ast_test_status_update(test, "Failed to retrieve '%s' object from the cache\n",
2679                                         in_cache[i]);
2680                         res = -1;
2681                 }
2682                 ao2_ref(cached_object, -1);
2683         }
2684
2685         for (i = 0; i < num_not_in_cache; ++i) {
2686                 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", not_in_cache[i]);
2687                 if (cached_object) {
2688                         ast_test_status_update(test, "Retrieved '%s' object from the cache unexpectedly\n",
2689                                         not_in_cache[i]);
2690                         ao2_ref(cached_object, -1);
2691                         res = -1;
2692                 }
2693         }
2694
2695         return res;
2696 }
2697
2698 AST_TEST_DEFINE(maximum_objects)
2699 {
2700         int res = AST_TEST_FAIL;
2701         struct ast_sorcery *sorcery = NULL;
2702         struct sorcery_memory_cache *cache = NULL;
2703         RAII_VAR(void *, alice, NULL, ao2_cleanup);
2704         RAII_VAR(void *, bob, NULL, ao2_cleanup);
2705         RAII_VAR(void *, charlie, NULL, ao2_cleanup);
2706         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
2707         const char *in_cache[2];
2708         const char *not_in_cache[2];
2709
2710         switch (cmd) {
2711         case TEST_INIT:
2712                 info->name = "maximum_objects";
2713                 info->category = "/res/res_sorcery_memory_cache/";
2714                 info->summary = "Ensure that the 'maximum_objects' option works as expected";
2715                 info->description = "This test performs the following:\n"
2716                         "\t* Creates a memory cache with maximum_objects=2\n"
2717                         "\t* Creates a sorcery instance\n"
2718                         "\t* Creates a three test objects: alice, bob, charlie, and david\n"
2719                         "\t* Pushes alice and bob into the memory cache\n"
2720                         "\t* Confirms that alice and bob are in the memory cache\n"
2721                         "\t* Pushes charlie into the memory cache\n"
2722                         "\t* Confirms that bob and charlie are in the memory cache\n"
2723                         "\t* Deletes charlie from the memory cache\n"
2724                         "\t* Confirms that only bob is in the memory cache\n"
2725                         "\t* Pushes alice into the memory cache\n"
2726                         "\t* Confirms that bob and alice are in the memory cache";
2727                 return AST_TEST_NOT_RUN;
2728         case TEST_EXECUTE:
2729                 break;
2730         }
2731
2732         cache = sorcery_memory_cache_open("maximum_objects=2");
2733         if (!cache) {
2734                 ast_test_status_update(test, "Failed to create a sorcery memory cache with maximum_objects=2\n");
2735                 goto cleanup;
2736         }
2737
2738         if (ao2_container_count(cache->objects)) {
2739                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
2740                 goto cleanup;
2741         }
2742
2743         sorcery = alloc_and_initialize_sorcery();
2744         if (!sorcery) {
2745                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2746                 goto cleanup;
2747         }
2748
2749         alice = ast_sorcery_alloc(sorcery, "test", "alice");
2750         bob = ast_sorcery_alloc(sorcery, "test", "bob");
2751         charlie = ast_sorcery_alloc(sorcery, "test", "charlie");
2752
2753         if (!alice || !bob || !charlie) {
2754                 ast_test_status_update(test, "Failed to allocate sorcery object(s)\n");
2755                 goto cleanup;
2756         }
2757
2758         sorcery_memory_cache_create(sorcery, cache, alice);
2759         in_cache[0] = "alice";
2760         in_cache[1] = NULL;
2761         not_in_cache[0] = "bob";
2762         not_in_cache[1] = "charlie";
2763         if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
2764                 goto cleanup;
2765         }
2766
2767         /* Delays are added to ensure that we are not adding cache entries within the
2768          * same microsecond
2769          */
2770         usleep(1000);
2771
2772         sorcery_memory_cache_create(sorcery, cache, bob);
2773         in_cache[0] = "alice";
2774         in_cache[1] = "bob";
2775         not_in_cache[0] = "charlie";
2776         not_in_cache[1] = NULL;
2777         if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2778                 goto cleanup;
2779         }
2780
2781         usleep(1000);
2782
2783         sorcery_memory_cache_create(sorcery, cache, charlie);
2784         in_cache[0] = "bob";
2785         in_cache[1] = "charlie";
2786         not_in_cache[0] = "alice";
2787         not_in_cache[1] = NULL;
2788         if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2789                 goto cleanup;
2790         }
2791         usleep(1000);
2792
2793         sorcery_memory_cache_delete(sorcery, cache, charlie);
2794         in_cache[0] = "bob";
2795         in_cache[1] = NULL;
2796         not_in_cache[0] = "alice";
2797         not_in_cache[1] = "charlie";
2798         if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
2799                 goto cleanup;
2800         }
2801         usleep(1000);
2802
2803         sorcery_memory_cache_create(sorcery, cache, alice);
2804         in_cache[0] = "bob";
2805         in_cache[1] = "alice";
2806         not_in_cache[0] = "charlie";
2807         not_in_cache[1] = NULL;
2808         if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2809                 goto cleanup;
2810         }
2811
2812         res = AST_TEST_PASS;
2813
2814 cleanup:
2815         if (cache) {
2816                 sorcery_memory_cache_close(cache);
2817         }
2818         if (sorcery) {
2819                 ast_sorcery_unref(sorcery);
2820         }
2821
2822         return res;
2823 }
2824
2825 AST_TEST_DEFINE(expiration)
2826 {
2827         int res = AST_TEST_FAIL;
2828         struct ast_sorcery *sorcery = NULL;
2829         struct sorcery_memory_cache *cache = NULL;
2830         int i;
2831
2832         switch (cmd) {
2833         case TEST_INIT:
2834                 info->name = "expiration";
2835                 info->category = "/res/res_sorcery_memory_cache/";
2836                 info->summary = "Add objects to a cache configured with maximum lifetime, confirm they are removed";
2837                 info->description = "This test performs the following:\n"
2838                         "\t* Creates a memory cache with a maximum object lifetime of 5 seconds\n"
2839                         "\t* Pushes 10 objects into the memory cache\n"
2840                         "\t* Waits (up to) 10 seconds for expiration to occur\n"
2841                         "\t* Confirms that the objects have been removed from the cache";
2842                 return AST_TEST_NOT_RUN;
2843         case TEST_EXECUTE:
2844                 break;
2845         }
2846
2847         cache = sorcery_memory_cache_open("object_lifetime_maximum=5");
2848         if (!cache) {
2849                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
2850                 goto cleanup;
2851         }
2852
2853         sorcery = alloc_and_initialize_sorcery();
2854         if (!sorcery) {
2855                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2856                 goto cleanup;
2857         }
2858
2859         cache->cache_notify = 1;
2860         ast_mutex_init(&cache->lock);
2861         ast_cond_init(&cache->cond, NULL);
2862
2863         for (i = 0; i < 5; ++i) {
2864                 char uuid[AST_UUID_STR_LEN];
2865                 void *object;
2866
2867                 object = ast_sorcery_alloc(sorcery, "test", ast_uuid_generate_str(uuid, sizeof(uuid)));
2868                 if (!object) {
2869                         ast_test_status_update(test, "Failed to allocate test object for expiration\n");
2870                         goto cleanup;
2871                 }
2872
2873                 sorcery_memory_cache_create(sorcery, cache, object);
2874
2875                 ao2_ref(object, -1);
2876         }
2877
2878         ast_mutex_lock(&cache->lock);
2879         while (!cache->cache_completed) {
2880                 struct timeval start = ast_tvnow();
2881                 struct timespec end = {
2882                         .tv_sec = start.tv_sec + 10,
2883                         .tv_nsec = start.tv_usec * 1000,
2884                 };
2885
2886                 if (ast_cond_timedwait(&cache->cond, &cache->lock, &end) == ETIMEDOUT) {
2887                         break;
2888                 }
2889         }
2890         ast_mutex_unlock(&cache->lock);
2891
2892         if (ao2_container_count(cache->objects)) {
2893                 ast_test_status_update(test, "Objects placed into the memory cache did not expire and get removed\n");
2894                 goto cleanup;
2895         }
2896
2897         res = AST_TEST_PASS;
2898
2899 cleanup:
2900         if (cache) {
2901                 if (cache->cache_notify) {
2902                         ast_cond_destroy(&cache->cond);
2903                         ast_mutex_destroy(&cache->lock);
2904                 }
2905                 sorcery_memory_cache_close(cache);
2906         }
2907         if (sorcery) {
2908                 ast_sorcery_unref(sorcery);
2909         }
2910
2911         return res;
2912 }
2913
2914 /*!
2915  * \brief Backend data that the mock sorcery wizard uses to create objects
2916  */
2917 static struct backend_data {
2918         /*! An arbitrary data field */
2919         int salt;
2920         /*! Another arbitrary data field */
2921         int pepper;
2922         /*! Indicates whether the backend has data */
2923         int exists;
2924 } *real_backend_data;
2925
2926 /*!
2927  * \brief Sorcery object created based on backend data
2928  */
2929 struct test_data {
2930         SORCERY_OBJECT(details);
2931         /*! Mirrors the backend data's salt field */
2932         int salt;
2933         /*! Mirrors the backend data's pepper field */
2934         int pepper;
2935 };
2936
2937 /*!
2938  * \brief Allocation callback for test_data sorcery object
2939  */
2940 static void *test_data_alloc(const char *id) {
2941         return ast_sorcery_generic_alloc(sizeof(struct test_data), NULL);
2942 }
2943
2944 /*!
2945  * \brief Callback for retrieving sorcery object by ID
2946  *
2947  * The mock wizard uses the \ref real_backend_data in order to construct
2948  * objects. If the backend data is "nonexisent" then no object is returned.
2949  * Otherwise, an object is created that has the backend data's salt and
2950  * pepper values copied.
2951  *
2952  * \param sorcery The sorcery instance
2953  * \param data Unused
2954  * \param type The object type. Will always be "test".
2955  * \param id The object id. Will always be "test".
2956  *
2957  * \retval NULL Backend data does not exist
2958  * \retval non-NULL An object representing the backend data
2959  */
2960 static void *mock_retrieve_id(const struct ast_sorcery *sorcery, void *data,
2961                 const char *type, const char *id)
2962 {
2963         struct test_data *b_data;
2964
2965         if (!real_backend_data->exists) {
2966                 return NULL;
2967         }
2968
2969         b_data = ast_sorcery_alloc(sorcery, type, id);
2970         if (!b_data) {
2971                 return NULL;
2972         }
2973
2974         b_data->salt = real_backend_data->salt;
2975         b_data->pepper = real_backend_data->pepper;
2976         return b_data;
2977 }
2978
2979 /*!
2980  * \brief Callback for retrieving multiple sorcery objects
2981  *
2982  * The mock wizard uses the \ref real_backend_data in order to construct
2983  * objects. If the backend data is "nonexisent" then no object is returned.
2984  * Otherwise, the number of objects matching the exists value will be returned.
2985  *
2986  * \param sorcery The sorcery instance
2987  * \param data Unused
2988  * \param type The object type. Will always be "test".
2989  * \param objects Container to place objects into.
2990  * \param fields Fields to search for.
2991  */
2992 static void mock_retrieve_multiple(const struct ast_sorcery *sorcery, void *data,
2993                 const char *type, struct ao2_container *objects, const struct ast_variable *fields)
2994 {
2995         int i;
2996
2997         if (fields) {
2998                 return;
2999         }
3000
3001         for (i = 0; i < real_backend_data->exists; ++i) {
3002                 char uuid[AST_UUID_STR_LEN];
3003                 struct test_data *b_data;
3004
3005                 b_data = ast_sorcery_alloc(sorcery, type, ast_uuid_generate_str(uuid, sizeof(uuid)));
3006                 if (!b_data) {
3007                         continue;
3008                 }
3009
3010                 b_data->salt = real_backend_data->salt;
3011                 b_data->pepper = real_backend_data->pepper;
3012
3013                 ao2_link(objects, b_data);
3014                 ao2_ref(b_data, -1);
3015         }
3016 }
3017
3018 /*!
3019  * \brief A mock sorcery wizard used for the stale test
3020  */
3021 static struct ast_sorcery_wizard mock_wizard = {
3022         .name = "mock",
3023         .retrieve_id = mock_retrieve_id,
3024         .retrieve_multiple = mock_retrieve_multiple,
3025 };
3026
3027 /*!
3028  * \brief Wait for the cache to be updated after a stale object is retrieved.
3029  *
3030  * Since the cache does not know what type of objects it is dealing with, and
3031  * since we do not have the internals of the cache, the only way to make this
3032  * determination is to continuously retrieve an object from the cache until
3033  * we retrieve a different object than we had previously retrieved.
3034  *
3035  * \param sorcery The sorcery instance
3036  * \param previous_object The object we had previously retrieved from the cache
3037  * \param[out] new_object The new object we retrieve from the cache
3038  *
3039  * \retval 0 Successfully retrieved a new object from the cache
3040  * \retval non-zero Failed to retrieve a new object from the cache
3041  */
3042 static int wait_for_cache_update(const struct ast_sorcery *sorcery,
3043                 void *previous_object, struct test_data **new_object)
3044 {
3045         struct timeval start = ast_tvnow();
3046
3047         while (ast_remaining_ms(start, 5000) > 0) {
3048                 void *object;
3049
3050                 object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
3051                 if (object != previous_object) {
3052                         *new_object = object;
3053                         return 0;
3054                 }
3055                 ao2_cleanup(object);
3056         }
3057
3058         return -1;
3059 }
3060
3061 AST_TEST_DEFINE(stale)
3062 {
3063         int res = AST_TEST_FAIL;
3064         struct ast_sorcery *sorcery = NULL;
3065         struct test_data *backend_object;
3066         struct backend_data iterations[] = {
3067                 { .salt = 1,      .pepper = 2,       .exists = 1 },
3068                 { .salt = 568729, .pepper = -234123, .exists = 1 },
3069                 { .salt = 0,      .pepper = 0,       .exists = 0 },
3070         };
3071         struct backend_data initial = {
3072                 .salt = 0,
3073                 .pepper = 0,
3074                 .exists = 1,
3075         };
3076         int i;
3077
3078         switch (cmd) {
3079         case TEST_INIT:
3080                 info->name = "stale";
3081                 info->category = "/res/res_sorcery_memory_cache/";
3082                 info->summary = "Ensure that stale objects are replaced with updated objects";
3083                 info->description = "This test performs the following:\n"
3084                         "\t* Create a sorcery instance with two wizards"
3085                         "\t\t* The first is a memory cache that marks items stale after 3 seconds\n"
3086                         "\t\t* The second is a mock of a back-end\n"
3087                         "\t* Pre-populates the cache by retrieving some initial data from the backend.\n"
3088                         "\t* Performs iterations of the following:\n"
3089                         "\t\t* Update backend data with new values\n"
3090                         "\t\t* Retrieve item from the cache\n"
3091                         "\t\t* Ensure the retrieved item does not have the new backend values\n"
3092                         "\t\t* Wait for cached object to become stale\n"
3093                         "\t\t* Retrieve the stale cached object\n"
3094                         "\t\t* Ensure that the stale object retrieved is the same as the fresh one from earlier\n"
3095                         "\t\t* Wait for the cache to update with new data\n"
3096                         "\t\t* Ensure that new data in the cache matches backend data";
3097                 return AST_TEST_NOT_RUN;
3098         case TEST_EXECUTE:
3099                 break;
3100         }
3101
3102         ast_sorcery_wizard_register(&mock_wizard);
3103
3104         sorcery = ast_sorcery_open();
3105         if (!sorcery) {
3106                 ast_test_status_update(test, "Failed to create sorcery instance\n");
3107                 goto cleanup;
3108         }
3109
3110         ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
3111                         "object_lifetime_stale=3", 1);
3112         ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
3113         ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
3114
3115         /* Prepopulate the cache */
3116         real_backend_data = &initial;
3117
3118         backend_object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
3119         if (!backend_object) {
3120                 ast_test_status_update(test, "Unable to retrieve backend data and populate the cache\n");
3121                 goto cleanup;
3122         }
3123         ao2_ref(backend_object, -1);
3124
3125         for (i = 0; i < ARRAY_LEN(iterations); ++i) {
3126                 RAII_VAR(struct test_data *, cache_fresh, NULL, ao2_cleanup);
3127                 RAII_VAR(struct test_data *, cache_stale, NULL, ao2_cleanup);
3128                 RAII_VAR(struct test_data *, cache_new, NULL, ao2_cleanup);
3129
3130                 real_backend_data = &iterations[i];
3131
3132                 ast_test_status_update(test, "Begininning iteration %d\n", i);
3133
3134                 cache_fresh = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
3135                 if (!cache_fresh) {
3136                         ast_test_status_update(test, "Unable to retrieve fresh cached object\n");
3137                         goto cleanup;
3138                 }
3139
3140                 if (cache_fresh->salt == iterations[i].salt || cache_fresh->pepper == iterations[i].pepper) {
3141                         ast_test_status_update(test, "Fresh cached object has unexpected values. Did we hit the backend?\n");
3142                         goto cleanup;
3143                 }
3144
3145                 sleep(5);
3146
3147                 cache_stale = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
3148                 if (!cache_stale) {
3149                         ast_test_status_update(test, "Unable to retrieve stale cached object\n");
3150                         goto cleanup;
3151                 }
3152
3153                 if (cache_stale != cache_fresh) {
3154                         ast_test_status_update(test, "Stale cache hit retrieved different object than fresh cache hit\n");
3155                         goto cleanup;
3156                 }
3157
3158                 if (wait_for_cache_update(sorcery, cache_stale, &cache_new)) {
3159                         ast_test_status_update(test, "Cache was not updated\n");
3160                         goto cleanup;
3161                 }
3162
3163                 if (iterations[i].exists) {
3164                         if (!cache_new) {
3165                                 ast_test_status_update(test, "Failed to retrieve item from cache when there should be one present\n");
3166                                 goto cleanup;
3167                         } else if (cache_new->salt != iterations[i].salt ||
3168                                         cache_new->pepper != iterations[i].pepper) {
3169                                 ast_test_status_update(test, "New cached item has unexpected values\n");
3170                                 goto cleanup;
3171                         }
3172                 } else if (cache_new) {
3173                         ast_test_status_update(test, "Retrieved a cached item when there should not have been one present\n");
3174                         goto cleanup;
3175                 }
3176         }
3177
3178         res = AST_TEST_PASS;
3179
3180 cleanup:
3181         if (sorcery) {
3182                 ast_sorcery_unref(sorcery);
3183         }
3184         ast_sorcery_wizard_unregister(&mock_wizard);
3185         return res;
3186 }
3187
3188 AST_TEST_DEFINE(full_backend_cache_expiration)
3189 {
3190         int res = AST_TEST_FAIL;
3191         struct ast_sorcery *sorcery = NULL;
3192         struct backend_data initial = {
3193                 .salt = 0,
3194                 .pepper = 0,
3195                 .exists = 4,
3196         };
3197         struct ao2_container *objects;
3198         ast_mutex_t lock;
3199         ast_cond_t cond;
3200         struct timeval start;
3201         struct timespec end;
3202
3203         switch (cmd) {
3204         case TEST_INIT:
3205                 info->name = "full_backend_cache_expiration";
3206                 info->category = "/res/res_sorcery_memory_cache/";
3207                 info->summary = "Ensure that the full backend cache actually caches the backend";
3208                 info->description = "This test performs the following:\n"
3209                         "\t* Create a sorcery instance with two wizards"
3210      &