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