57feb8f4f131db77d31f43dad3b3419781711ce1
[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 ASTERISK_REGISTER_FILE()
34
35 #include "asterisk/module.h"
36 #include "asterisk/sorcery.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/sched.h"
39 #include "asterisk/test.h"
40 #include "asterisk/heap.h"
41 #include "asterisk/cli.h"
42 #include "asterisk/manager.h"
43
44 /*** DOCUMENTATION
45         <manager name="SorceryMemoryCacheExpireObject" language="en_US">
46                 <synopsis>
47                         Expire (remove) an object from a sorcery memory cache.
48                 </synopsis>
49                 <syntax>
50                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
51                         <parameter name="Cache" required="true">
52                                 <para>The name of the cache to expire the object from.</para>
53                         </parameter>
54                         <parameter name="Object" required="true">
55                                 <para>The name of the object to expire.</para>
56                         </parameter>
57                 </syntax>
58                 <description>
59                         <para>Expires (removes) an object from a sorcery memory cache.</para>
60                 </description>
61         </manager>
62         <manager name="SorceryMemoryCacheExpire" language="en_US">
63                 <synopsis>
64                         Expire (remove) ALL objects from a sorcery memory cache.
65                 </synopsis>
66                 <syntax>
67                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
68                         <parameter name="Cache" required="true">
69                                 <para>The name of the cache to expire all objects from.</para>
70                         </parameter>
71                 </syntax>
72                 <description>
73                         <para>Expires (removes) ALL objects from a sorcery memory cache.</para>
74                 </description>
75         </manager>
76         <manager name="SorceryMemoryCacheStaleObject" language="en_US">
77                 <synopsis>
78                         Mark an object in a sorcery memory cache as stale.
79                 </synopsis>
80                 <syntax>
81                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
82                         <parameter name="Cache" required="true">
83                                 <para>The name of the cache to mark the object as stale in.</para>
84                         </parameter>
85                         <parameter name="Object" required="true">
86                                 <para>The name of the object to mark as stale.</para>
87                         </parameter>
88                 </syntax>
89                 <description>
90                         <para>Marks an object as stale within a sorcery memory cache.</para>
91                 </description>
92         </manager>
93         <manager name="SorceryMemoryCacheStale" language="en_US">
94                 <synopsis>
95                         Marks ALL objects in a sorcery memory cache as stale.
96                 </synopsis>
97                 <syntax>
98                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
99                         <parameter name="Cache" required="true">
100                                 <para>The name of the cache to mark all object as stale in.</para>
101                         </parameter>
102                 </syntax>
103                 <description>
104                         <para>Marks ALL objects in a sorcery memory cache as stale.</para>
105                 </description>
106         </manager>
107  ***/
108
109 /*! \brief Structure for storing a memory cache */
110 struct sorcery_memory_cache {
111         /*! \brief The name of the memory cache */
112         char *name;
113         /*! \brief Objects in the cache */
114         struct ao2_container *objects;
115         /*! \brief The maximum number of objects permitted in the cache, 0 if no limit */
116         unsigned int maximum_objects;
117         /*! \brief The maximum time (in seconds) an object will stay in the cache, 0 if no limit */
118         unsigned int object_lifetime_maximum;
119         /*! \brief The amount of time (in seconds) before an object is marked as stale, 0 if disabled */
120         unsigned int object_lifetime_stale;
121         /*! \brief Whether objects are prefetched from normal storage at load time, 0 if disabled */
122         unsigned int prefetch;
123         /** \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
124         unsigned int expire_on_reload;
125         /*! \brief Heap of cached objects. Oldest object is at the top. */
126         struct ast_heap *object_heap;
127         /*! \brief Scheduler item for expiring oldest object. */
128         int expire_id;
129 #ifdef TEST_FRAMEWORK
130         /*! \brief Variable used to indicate we should notify a test when we reach empty */
131         unsigned int cache_notify;
132         /*! \brief Mutex lock used for signaling when the cache has reached empty */
133         ast_mutex_t lock;
134         /*! \brief Condition used for signaling when the cache has reached empty */
135         ast_cond_t cond;
136         /*! \brief Variable that is set when the cache has reached empty */
137         unsigned int cache_completed;
138 #endif
139 };
140
141 /*! \brief Structure for stored a cached object */
142 struct sorcery_memory_cached_object {
143         /*! \brief The cached object */
144         void *object;
145         /*! \brief The time at which the object was created */
146         struct timeval created;
147         /*! \brief index required by heap */
148         ssize_t __heap_index;
149         /*! \brief scheduler id of stale update task */
150         int stale_update_sched_id;
151 };
152
153 static void *sorcery_memory_cache_open(const char *data);
154 static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object);
155 static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type);
156 static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type);
157 static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
158         const char *id);
159 static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
160 static void sorcery_memory_cache_close(void *data);
161
162 static struct ast_sorcery_wizard memory_cache_object_wizard = {
163         .name = "memory_cache",
164         .open = sorcery_memory_cache_open,
165         .create = sorcery_memory_cache_create,
166         .update = sorcery_memory_cache_create,
167         .delete = sorcery_memory_cache_delete,
168         .load = sorcery_memory_cache_load,
169         .reload = sorcery_memory_cache_reload,
170         .retrieve_id = sorcery_memory_cache_retrieve_id,
171         .close = sorcery_memory_cache_close,
172 };
173
174 /*! \brief The bucket size for the container of caches */
175 #define CACHES_CONTAINER_BUCKET_SIZE 53
176
177 /*! \brief The default bucket size for the container of objects in the cache */
178 #define CACHE_CONTAINER_BUCKET_SIZE 53
179
180 /*! \brief Height of heap for cache object heap. Allows 31 initial objects */
181 #define CACHE_HEAP_INIT_HEIGHT 5
182
183 /*! \brief Container of created caches */
184 static struct ao2_container *caches;
185
186 /*! \brief Scheduler for cache management */
187 static struct ast_sched_context *sched;
188
189 #define STALE_UPDATE_THREAD_ID 0x5EED1E55
190 AST_THREADSTORAGE(stale_update_id_storage);
191
192 static int is_stale_update(void)
193 {
194         uint32_t *stale_update_thread_id;
195
196         stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
197                 sizeof(*stale_update_thread_id));
198         if (!stale_update_thread_id) {
199                 return 0;
200         }
201
202         return *stale_update_thread_id == STALE_UPDATE_THREAD_ID;
203 }
204
205 static void start_stale_update(void)
206 {
207         uint32_t *stale_update_thread_id;
208
209         stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
210                 sizeof(*stale_update_thread_id));
211         if (!stale_update_thread_id) {
212                 ast_log(LOG_ERROR, "Could not set stale update ID for sorcery memory cache thread\n");
213                 return;
214         }
215
216         *stale_update_thread_id = STALE_UPDATE_THREAD_ID;
217 }
218
219 static void end_stale_update(void)
220 {
221         uint32_t *stale_update_thread_id;
222
223         stale_update_thread_id = ast_threadstorage_get(&stale_update_id_storage,
224                 sizeof(*stale_update_thread_id));
225         if (!stale_update_thread_id) {
226                 ast_log(LOG_ERROR, "Could not set stale update ID for sorcery memory cache thread\n");
227                 return;
228         }
229
230         *stale_update_thread_id = 0;
231 }
232
233 /*!
234  * \internal
235  * \brief Hashing function for the container holding caches
236  *
237  * \param obj A sorcery memory cache or name of one
238  * \param flags Hashing flags
239  *
240  * \return The hash of the memory cache name
241  */
242 static int sorcery_memory_cache_hash(const void *obj, int flags)
243 {
244         const struct sorcery_memory_cache *cache = obj;
245         const char *name = obj;
246         int hash;
247
248         switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
249         default:
250         case OBJ_SEARCH_OBJECT:
251                 name = cache->name;
252                 /* Fall through */
253         case OBJ_SEARCH_KEY:
254                 hash = ast_str_hash(name);
255                 break;
256         case OBJ_SEARCH_PARTIAL_KEY:
257                 /* Should never happen in hash callback. */
258                 ast_assert(0);
259                 hash = 0;
260                 break;
261         }
262         return hash;
263 }
264
265 /*!
266  * \internal
267  * \brief Comparison function for the container holding caches
268  *
269  * \param obj A sorcery memory cache
270  * \param arg A sorcery memory cache, or name of one
271  * \param flags Comparison flags
272  *
273  * \retval CMP_MATCH if the name is the same
274  * \retval 0 if the name does not match
275  */
276 static int sorcery_memory_cache_cmp(void *obj, void *arg, int flags)
277 {
278         const struct sorcery_memory_cache *left = obj;
279         const struct sorcery_memory_cache *right = arg;
280         const char *right_name = arg;
281         int cmp;
282
283         switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
284         default:
285         case OBJ_SEARCH_OBJECT:
286                 right_name = right->name;
287                 /* Fall through */
288         case OBJ_SEARCH_KEY:
289                 cmp = strcmp(left->name, right_name);
290                 break;
291         case OBJ_SEARCH_PARTIAL_KEY:
292                 cmp = strncmp(left->name, right_name, strlen(right_name));
293                 break;
294         }
295         return cmp ? 0 : CMP_MATCH;
296 }
297
298 /*!
299  * \internal
300  * \brief Hashing function for the container holding cached objects
301  *
302  * \param obj A cached object or id of one
303  * \param flags Hashing flags
304  *
305  * \return The hash of the cached object id
306  */
307 static int sorcery_memory_cached_object_hash(const void *obj, int flags)
308 {
309         const struct sorcery_memory_cached_object *cached = obj;
310         const char *name = obj;
311         int hash;
312
313         switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
314         default:
315         case OBJ_SEARCH_OBJECT:
316                 name = ast_sorcery_object_get_id(cached->object);
317                 /* Fall through */
318         case OBJ_SEARCH_KEY:
319                 hash = ast_str_hash(name);
320                 break;
321         case OBJ_SEARCH_PARTIAL_KEY:
322                 /* Should never happen in hash callback. */
323                 ast_assert(0);
324                 hash = 0;
325                 break;
326         }
327         return hash;
328 }
329
330 /*!
331  * \internal
332  * \brief Comparison function for the container holding cached objects
333  *
334  * \param obj A cached object
335  * \param arg A cached object, or id of one
336  * \param flags Comparison flags
337  *
338  * \retval CMP_MATCH if the id is the same
339  * \retval 0 if the id does not match
340  */
341 static int sorcery_memory_cached_object_cmp(void *obj, void *arg, int flags)
342 {
343         struct sorcery_memory_cached_object *left = obj;
344         struct sorcery_memory_cached_object *right = arg;
345         const char *right_name = arg;
346         int cmp;
347
348         switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
349         default:
350         case OBJ_SEARCH_OBJECT:
351                 right_name = ast_sorcery_object_get_id(right->object);
352                 /* Fall through */
353         case OBJ_SEARCH_KEY:
354                 cmp = strcmp(ast_sorcery_object_get_id(left->object), right_name);
355                 break;
356         case OBJ_SEARCH_PARTIAL_KEY:
357                 cmp = strncmp(ast_sorcery_object_get_id(left->object), right_name, strlen(right_name));
358                 break;
359         }
360         return cmp ? 0 : CMP_MATCH;
361 }
362
363 /*!
364  * \internal
365  * \brief Destructor function for a sorcery memory cache
366  *
367  * \param obj A sorcery memory cache
368  */
369 static void sorcery_memory_cache_destructor(void *obj)
370 {
371         struct sorcery_memory_cache *cache = obj;
372
373         ast_free(cache->name);
374         ao2_cleanup(cache->objects);
375         if (cache->object_heap) {
376                 ast_heap_destroy(cache->object_heap);
377         }
378 }
379
380 /*!
381  * \internal
382  * \brief Destructor function for sorcery memory cached objects
383  *
384  * \param obj A sorcery memory cached object
385  */
386 static void sorcery_memory_cached_object_destructor(void *obj)
387 {
388         struct sorcery_memory_cached_object *cached = obj;
389
390         ao2_cleanup(cached->object);
391 }
392
393 static int schedule_cache_expiration(struct sorcery_memory_cache *cache);
394
395 /*!
396  * \internal
397  * \brief Remove an object from the cache.
398  *
399  * This removes the item from both the hashtable and the heap.
400  *
401  * \pre cache->objects is write-locked
402  *
403  * \param cache The cache from which the object is being removed.
404  * \param id The sorcery object id of the object to remove.
405  * \param reschedule Reschedule cache expiration if this was the oldest object.
406  *
407  * \retval 0 Success
408  * \retval non-zero Failure
409  */
410 static int remove_from_cache(struct sorcery_memory_cache *cache, const char *id, int reschedule)
411 {
412         struct sorcery_memory_cached_object *hash_object;
413         struct sorcery_memory_cached_object *oldest_object;
414         struct sorcery_memory_cached_object *heap_object;
415
416         hash_object = ao2_find(cache->objects, id,
417                 OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NOLOCK);
418         if (!hash_object) {
419                 return -1;
420         }
421         oldest_object = ast_heap_peek(cache->object_heap, 1);
422         heap_object = ast_heap_remove(cache->object_heap, hash_object);
423
424         ast_assert(heap_object == hash_object);
425
426         ao2_ref(hash_object, -1);
427
428         if (reschedule && (oldest_object == heap_object)) {
429                 schedule_cache_expiration(cache);
430         }
431
432         return 0;
433 }
434
435 /*!
436  * \internal
437  * \brief Scheduler callback invoked to expire old objects
438  *
439  * \param data The opaque callback data (in our case, the memory cache)
440  */
441 static int expire_objects_from_cache(const void *data)
442 {
443         struct sorcery_memory_cache *cache = (struct sorcery_memory_cache *)data;
444         struct sorcery_memory_cached_object *cached;
445
446         ao2_wrlock(cache->objects);
447
448         cache->expire_id = -1;
449
450         /* This is an optimization for objects which have been cached close to eachother */
451         while ((cached = ast_heap_peek(cache->object_heap, 1))) {
452                 int expiration;
453
454                 expiration = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow());
455
456                 /* If the current oldest object has not yet expired stop and reschedule for it */
457                 if (expiration > 0) {
458                         break;
459                 }
460
461                 remove_from_cache(cache, ast_sorcery_object_get_id(cached->object), 0);
462         }
463
464         schedule_cache_expiration(cache);
465
466         ao2_unlock(cache->objects);
467
468         ao2_ref(cache, -1);
469
470         return 0;
471 }
472
473 /*!
474  * \internal
475  * \brief Remove all objects from the cache.
476  *
477  * This removes ALL objects from both the hash table and heap.
478  *
479  * \pre cache->objects is write-locked
480  *
481  * \param cache The cache to empty.
482  */
483 static void remove_all_from_cache(struct sorcery_memory_cache *cache)
484 {
485         while (ast_heap_pop(cache->object_heap));
486
487         ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
488                 NULL, NULL);
489
490         AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
491 }
492
493 /*!
494  * \internal
495  * \brief AO2 callback function for making an object stale immediately
496  *
497  * This changes the creation time of an object so it appears as though it is stale immediately.
498  *
499  * \param obj The cached object
500  * \param arg The cache itself
501  * \param flags Unused flags
502  */
503 static int object_stale_callback(void *obj, void *arg, int flags)
504 {
505         struct sorcery_memory_cached_object *cached = obj;
506         struct sorcery_memory_cache *cache = arg;
507
508         /* Since our granularity is seconds it's possible for something to retrieve us within a window
509          * where we wouldn't be treated as stale. To ensure that doesn't happen we use the configured stale
510          * time plus a second.
511          */
512         cached->created = ast_tvsub(cached->created, ast_samp2tv(cache->object_lifetime_stale + 1, 1));
513
514         return CMP_MATCH;
515 }
516
517 /*!
518  * \internal
519  * \brief Mark an object as stale explicitly.
520  *
521  * This changes the creation time of an object so it appears as though it is stale immediately.
522  *
523  * \pre cache->objects is read-locked
524  *
525  * \param cache The cache the object is in
526  * \param id The unique identifier of the object
527  *
528  * \retval 0 success
529  * \retval -1 failure
530  */
531 static int mark_object_as_stale_in_cache(struct sorcery_memory_cache *cache, const char *id)
532 {
533         struct sorcery_memory_cached_object *cached;
534
535         cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY | OBJ_NOLOCK);
536         if (!cached) {
537                 return -1;
538         }
539
540         object_stale_callback(cached, cache, 0);
541         ao2_ref(cached, -1);
542
543         return 0;
544 }
545
546 /*!
547  * \internal
548  * \brief Mark all objects as stale within a cache.
549  *
550  * This changes the creation time of ALL objects so they appear as though they are stale.
551  *
552  * \pre cache->objects is read-locked
553  *
554  * \param cache
555  */
556 static void mark_all_as_stale_in_cache(struct sorcery_memory_cache *cache)
557 {
558         ao2_callback(cache->objects, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE, object_stale_callback, cache);
559 }
560
561 /*!
562  * \internal
563  * \brief Schedule a callback for cached object expiration.
564  *
565  * \pre cache->objects is write-locked
566  *
567  * \param cache The cache that is having its callback scheduled.
568  *
569  * \retval 0 success
570  * \retval -1 failure
571  */
572 static int schedule_cache_expiration(struct sorcery_memory_cache *cache)
573 {
574         struct sorcery_memory_cached_object *cached;
575         int expiration = 0;
576
577         if (!cache->object_lifetime_maximum) {
578                 return 0;
579         }
580
581         if (cache->expire_id != -1) {
582                 /* If we can't unschedule this expiration then it is currently attempting to run,
583                  * so let it run - it just means that it'll be the one scheduling instead of us.
584                  */
585                 if (ast_sched_del(sched, cache->expire_id)) {
586                         return 0;
587                 }
588
589                 /* Since it successfully cancelled we need to drop the ref to the cache it had */
590                 ao2_ref(cache, -1);
591                 cache->expire_id = -1;
592         }
593
594         cached = ast_heap_peek(cache->object_heap, 1);
595         if (!cached) {
596 #ifdef TEST_FRAMEWORK
597                 ast_mutex_lock(&cache->lock);
598                 cache->cache_completed = 1;
599                 ast_cond_signal(&cache->cond);
600                 ast_mutex_unlock(&cache->lock);
601 #endif
602                 return 0;
603         }
604
605         expiration = MAX(ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(cache->object_lifetime_maximum, 1)), ast_tvnow()),
606                 1);
607
608         cache->expire_id = ast_sched_add(sched, expiration, expire_objects_from_cache, ao2_bump(cache));
609         if (cache->expire_id < 0) {
610                 ao2_ref(cache, -1);
611                 return -1;
612         }
613
614         return 0;
615 }
616
617 /*!
618  * \internal
619  * \brief Remove the oldest item from the cache.
620  *
621  * \pre cache->objects is write-locked
622  *
623  * \param cache The cache from which to remove the oldest object
624  *
625  * \retval 0 Success
626  * \retval non-zero Failure
627  */
628 static int remove_oldest_from_cache(struct sorcery_memory_cache *cache)
629 {
630         struct sorcery_memory_cached_object *heap_old_object;
631         struct sorcery_memory_cached_object *hash_old_object;
632
633         heap_old_object = ast_heap_pop(cache->object_heap);
634         if (!heap_old_object) {
635                 return -1;
636         }
637         hash_old_object = ao2_find(cache->objects, heap_old_object,
638                 OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NOLOCK);
639
640         ast_assert(heap_old_object == hash_old_object);
641
642         ao2_ref(hash_old_object, -1);
643
644         schedule_cache_expiration(cache);
645
646         return 0;
647 }
648
649 /*!
650  * \internal
651  * \brief Add a new object to the cache.
652  *
653  * \pre cache->objects is write-locked
654  *
655  * \param cache The cache in which to add the new object
656  * \param cached_object The object to add to the cache
657  *
658  * \retval 0 Success
659  * \retval non-zero Failure
660  */
661 static int add_to_cache(struct sorcery_memory_cache *cache,
662                 struct sorcery_memory_cached_object *cached_object)
663 {
664         if (!ao2_link_flags(cache->objects, cached_object, OBJ_NOLOCK)) {
665                 return -1;
666         }
667
668         if (ast_heap_push(cache->object_heap, cached_object)) {
669                 ao2_find(cache->objects, cached_object,
670                         OBJ_SEARCH_OBJECT | OBJ_UNLINK | OBJ_NODATA | OBJ_NOLOCK);
671                 return -1;
672         }
673
674         if (cache->expire_id == -1) {
675                 schedule_cache_expiration(cache);
676         }
677
678         return 0;
679 }
680
681 /*!
682  * \internal
683  * \brief Callback function to cache an object in a memory cache
684  *
685  * \param sorcery The sorcery instance
686  * \param data The sorcery memory cache
687  * \param object The object to cache
688  *
689  * \retval 0 success
690  * \retval -1 failure
691  */
692 static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object)
693 {
694         struct sorcery_memory_cache *cache = data;
695         struct sorcery_memory_cached_object *cached;
696
697         cached = ao2_alloc(sizeof(*cached), sorcery_memory_cached_object_destructor);
698         if (!cached) {
699                 return -1;
700         }
701         cached->object = ao2_bump(object);
702         cached->created = ast_tvnow();
703         cached->stale_update_sched_id = -1;
704
705         /* As there is no guarantee that this won't be called by multiple threads wanting to cache
706          * the same object we remove any old ones, which turns this into a create/update function
707          * in reality. As well since there's no guarantee that the object in the cache is the same
708          * one here we remove any old objects using the object identifier.
709          */
710
711         ao2_wrlock(cache->objects);
712         remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
713         if (cache->maximum_objects && ao2_container_count(cache->objects) >= cache->maximum_objects) {
714                 if (remove_oldest_from_cache(cache)) {
715                         ast_log(LOG_ERROR, "Unable to make room in cache for sorcery object '%s'.\n",
716                                 ast_sorcery_object_get_id(object));
717                         ao2_ref(cached, -1);
718                         ao2_unlock(cache->objects);
719                         return -1;
720                 }
721         }
722         if (add_to_cache(cache, cached)) {
723                 ast_log(LOG_ERROR, "Unable to add object '%s' to the cache\n",
724                         ast_sorcery_object_get_id(object));
725                 ao2_ref(cached, -1);
726                 ao2_unlock(cache->objects);
727                 return -1;
728         }
729         ao2_unlock(cache->objects);
730
731         ao2_ref(cached, -1);
732         return 0;
733 }
734
735 struct stale_update_task_data {
736         struct ast_sorcery *sorcery;
737         struct sorcery_memory_cache *cache;
738         void *object;
739 };
740
741 static void stale_update_task_data_destructor(void *obj)
742 {
743         struct stale_update_task_data *task_data = obj;
744
745         ao2_cleanup(task_data->cache);
746         ao2_cleanup(task_data->object);
747         ast_sorcery_unref(task_data->sorcery);
748 }
749
750 static struct stale_update_task_data *stale_update_task_data_alloc(struct ast_sorcery *sorcery,
751                 struct sorcery_memory_cache *cache, const char *type, void *object)
752 {
753         struct stale_update_task_data *task_data;
754
755         task_data = ao2_alloc_options(sizeof(*task_data), stale_update_task_data_destructor,
756                 AO2_ALLOC_OPT_LOCK_NOLOCK);
757         if (!task_data) {
758                 return NULL;
759         }
760
761         task_data->sorcery = ao2_bump(sorcery);
762         task_data->cache = ao2_bump(cache);
763         task_data->object = ao2_bump(object);
764
765         return task_data;
766 }
767
768 static int stale_item_update(const void *data)
769 {
770         struct stale_update_task_data *task_data = (struct stale_update_task_data *) data;
771         void *object;
772
773         start_stale_update();
774
775         object = ast_sorcery_retrieve_by_id(task_data->sorcery,
776                 ast_sorcery_object_get_type(task_data->object),
777                 ast_sorcery_object_get_id(task_data->object));
778         if (!object) {
779                 ast_debug(1, "Backend no longer has object type '%s' ID '%s'. Removing from cache\n",
780                         ast_sorcery_object_get_type(task_data->object),
781                         ast_sorcery_object_get_id(task_data->object));
782                 sorcery_memory_cache_delete(task_data->sorcery, task_data->cache,
783                         task_data->object);
784         } else {
785                 ast_debug(1, "Refreshing stale cache object type '%s' ID '%s'\n",
786                         ast_sorcery_object_get_type(task_data->object),
787                         ast_sorcery_object_get_id(task_data->object));
788                 sorcery_memory_cache_create(task_data->sorcery, task_data->cache,
789                         object);
790         }
791
792         ast_test_suite_event_notify("SORCERY_MEMORY_CACHE_REFRESHED", "Cache: %s\r\nType: %s\r\nName: %s\r\n",
793                 task_data->cache->name, ast_sorcery_object_get_type(task_data->object),
794                 ast_sorcery_object_get_id(task_data->object));
795
796         ao2_ref(task_data, -1);
797         end_stale_update();
798
799         return 0;
800 }
801
802 /*!
803  * \internal
804  * \brief Callback function to retrieve an object from a memory cache
805  *
806  * \param sorcery The sorcery instance
807  * \param data The sorcery memory cache
808  * \param type The type of the object to retrieve
809  * \param id The id of the object to retrieve
810  *
811  * \retval non-NULL success
812  * \retval NULL failure
813  */
814 static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
815 {
816         struct sorcery_memory_cache *cache = data;
817         struct sorcery_memory_cached_object *cached;
818         void *object;
819
820         if (is_stale_update()) {
821                 return NULL;
822         }
823
824         cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY);
825         if (!cached) {
826                 return NULL;
827         }
828
829         if (cache->object_lifetime_stale) {
830                 struct timeval elapsed;
831
832                 elapsed = ast_tvsub(ast_tvnow(), cached->created);
833                 if (elapsed.tv_sec > cache->object_lifetime_stale) {
834                         ao2_lock(cached);
835                         if (cached->stale_update_sched_id == -1) {
836                                 struct stale_update_task_data *task_data;
837
838                                 task_data = stale_update_task_data_alloc((struct ast_sorcery *)sorcery, cache,
839                                         type, cached->object);
840                                 if (task_data) {
841                                         ast_debug(1, "Cached sorcery object type '%s' ID '%s' is stale. Refreshing\n",
842                                                 type, id);
843                                         cached->stale_update_sched_id = ast_sched_add(sched, 1, stale_item_update, task_data);
844                                 } else {
845                                         ast_log(LOG_ERROR, "Unable to update stale cached object type '%s', ID '%s'.\n",
846                                                 type, id);
847                                 }
848                         }
849                         ao2_unlock(cached);
850                 }
851         }
852
853         object = ao2_bump(cached->object);
854         ao2_ref(cached, -1);
855
856         return object;
857 }
858
859 /*!
860  * \internal
861  * \brief Callback function to finish configuring the memory cache and to prefetch objects
862  *
863  * \param data The sorcery memory cache
864  * \param sorcery The sorcery instance
865  * \param type The type of object being loaded
866  */
867 static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type)
868 {
869         struct sorcery_memory_cache *cache = data;
870
871         /* If no name was explicitly specified generate one given the sorcery instance and object type */
872         if (ast_strlen_zero(cache->name)) {
873                 ast_asprintf(&cache->name, "%s/%s", ast_sorcery_get_module(sorcery), type);
874         }
875
876         ao2_link(caches, cache);
877         ast_debug(1, "Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
878                 cache->name, sorcery, ast_sorcery_get_module(sorcery), type);
879 }
880
881 /*!
882  * \internal
883  * \brief Callback function to expire objects from the memory cache on reload (if configured)
884  *
885  * \param data The sorcery memory cache
886  * \param sorcery The sorcery instance
887  * \param type The type of object being reloaded
888  */
889 static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type)
890 {
891 }
892
893 /*!
894  * \internal
895  * \brief Function used to take an unsigned integer based configuration option and parse it
896  *
897  * \param value The string value of the configuration option
898  * \param result The unsigned integer to place the result in
899  *
900  * \retval 0 failure
901  * \retval 1 success
902  */
903 static int configuration_parse_unsigned_integer(const char *value, unsigned int *result)
904 {
905         if (ast_strlen_zero(value) || !strncmp(value, "-", 1)) {
906                 return 0;
907         }
908
909         return sscanf(value, "%30u", result);
910 }
911
912 static int age_cmp(void *a, void *b)
913 {
914         return ast_tvcmp(((struct sorcery_memory_cached_object *) b)->created,
915                         ((struct sorcery_memory_cached_object *) a)->created);
916 }
917
918 /*!
919  * \internal
920  * \brief Callback function to create a new sorcery memory cache using provided configuration
921  *
922  * \param data A stringified configuration for the memory cache
923  *
924  * \retval non-NULL success
925  * \retval NULL failure
926  */
927 static void *sorcery_memory_cache_open(const char *data)
928 {
929         char *options = ast_strdup(data), *option;
930         RAII_VAR(struct sorcery_memory_cache *, cache, NULL, ao2_cleanup);
931
932         cache = ao2_alloc_options(sizeof(*cache), sorcery_memory_cache_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
933         if (!cache) {
934                 return NULL;
935         }
936
937         cache->expire_id = -1;
938
939         /* If no configuration options have been provided this memory cache will operate in a default
940          * configuration.
941          */
942         while (!ast_strlen_zero(options) && (option = strsep(&options, ","))) {
943                 char *name = strsep(&option, "="), *value = option;
944
945                 if (!strcasecmp(name, "name")) {
946                         if (ast_strlen_zero(value)) {
947                                 ast_log(LOG_ERROR, "A name must be specified for the memory cache\n");
948                                 return NULL;
949                         }
950                         ast_free(cache->name);
951                         cache->name = ast_strdup(value);
952                 } else if (!strcasecmp(name, "maximum_objects")) {
953                         if (configuration_parse_unsigned_integer(value, &cache->maximum_objects) != 1) {
954                                 ast_log(LOG_ERROR, "Unsupported maximum objects value of '%s' used for memory cache\n",
955                                         value);
956                                 return NULL;
957                         }
958                 } else if (!strcasecmp(name, "object_lifetime_maximum")) {
959                         if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_maximum) != 1) {
960                                 ast_log(LOG_ERROR, "Unsupported object maximum lifetime value of '%s' used for memory cache\n",
961                                         value);
962                                 return NULL;
963                         }
964                 } else if (!strcasecmp(name, "object_lifetime_stale")) {
965                         if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_stale) != 1) {
966                                 ast_log(LOG_ERROR, "Unsupported object stale lifetime value of '%s' used for memory cache\n",
967                                         value);
968                                 return NULL;
969                         }
970                 } else if (!strcasecmp(name, "prefetch")) {
971                         cache->prefetch = ast_true(value);
972                 } else if (!strcasecmp(name, "expire_on_reload")) {
973                         cache->expire_on_reload = ast_true(value);
974                 } else {
975                         ast_log(LOG_ERROR, "Unsupported option '%s' used for memory cache\n", name);
976                         return NULL;
977                 }
978         }
979
980         cache->objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
981                 cache->maximum_objects ? cache->maximum_objects : CACHE_CONTAINER_BUCKET_SIZE,
982                 sorcery_memory_cached_object_hash, sorcery_memory_cached_object_cmp);
983         if (!cache->objects) {
984                 ast_log(LOG_ERROR, "Could not create a container to hold cached objects for memory cache\n");
985                 return NULL;
986         }
987
988         cache->object_heap = ast_heap_create(CACHE_HEAP_INIT_HEIGHT, age_cmp,
989                 offsetof(struct sorcery_memory_cached_object, __heap_index));
990         if (!cache->object_heap) {
991                 ast_log(LOG_ERROR, "Could not create heap to hold cached objects\n");
992                 return NULL;
993         }
994
995         /* The memory cache is not linked to the caches container until the load callback is invoked.
996          * Linking occurs there so an intelligent cache name can be constructed using the module of
997          * the sorcery instance and the specific object type if no cache name was specified as part
998          * of the configuration.
999          */
1000
1001         /* This is done as RAII_VAR will drop the reference */
1002         return ao2_bump(cache);
1003 }
1004
1005 /*!
1006  * \internal
1007  * \brief Callback function to delete an object from a memory cache
1008  *
1009  * \param sorcery The sorcery instance
1010  * \param data The sorcery memory cache
1011  * \param object The object to cache
1012  *
1013  * \retval 0 success
1014  * \retval -1 failure
1015  */
1016 static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object)
1017 {
1018         struct sorcery_memory_cache *cache = data;
1019         int res;
1020
1021         ao2_wrlock(cache->objects);
1022         res = remove_from_cache(cache, ast_sorcery_object_get_id(object), 1);
1023         ao2_unlock(cache->objects);
1024
1025         if (res) {
1026                 ast_log(LOG_ERROR, "Unable to delete object '%s' from sorcery cache\n", ast_sorcery_object_get_id(object));
1027         }
1028
1029         return res;
1030 }
1031
1032 /*!
1033  * \internal
1034  * \brief Callback function to terminate a memory cache
1035  *
1036  * \param data The sorcery memory cache
1037  */
1038 static void sorcery_memory_cache_close(void *data)
1039 {
1040         struct sorcery_memory_cache *cache = data;
1041
1042         /* This can occur if a cache is created but never loaded */
1043         if (!ast_strlen_zero(cache->name)) {
1044                 ao2_unlink(caches, cache);
1045         }
1046
1047         if (cache->object_lifetime_maximum) {
1048                 /* If object lifetime support is enabled we need to explicitly drop all cached objects here
1049                  * and stop the scheduled task. Failure to do so could potentially keep the cache around for
1050                  * a prolonged period of time.
1051                  */
1052                 ao2_wrlock(cache->objects);
1053                 ao2_callback(cache->objects, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
1054                         NULL, NULL);
1055                 AST_SCHED_DEL_UNREF(sched, cache->expire_id, ao2_ref(cache, -1));
1056                 ao2_unlock(cache->objects);
1057         }
1058
1059         ao2_ref(cache, -1);
1060 }
1061
1062 /*!
1063  * \internal
1064  * \brief CLI tab completion for cache names
1065  */
1066 static char *sorcery_memory_cache_complete_name(const char *word, int state)
1067 {
1068         struct sorcery_memory_cache *cache;
1069         struct ao2_iterator it_caches;
1070         int wordlen = strlen(word);
1071         int which = 0;
1072         char *result = NULL;
1073
1074         it_caches = ao2_iterator_init(caches, 0);
1075         while ((cache = ao2_iterator_next(&it_caches))) {
1076                 if (!strncasecmp(word, cache->name, wordlen)
1077                         && ++which > state) {
1078                         result = ast_strdup(cache->name);
1079                 }
1080                 ao2_ref(cache, -1);
1081                 if (result) {
1082                         break;
1083                 }
1084         }
1085         ao2_iterator_destroy(&it_caches);
1086         return result;
1087 }
1088
1089 /*!
1090  * \internal
1091  * \brief CLI command implementation for 'sorcery memory cache show'
1092  */
1093 static char *sorcery_memory_cache_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1094 {
1095         struct sorcery_memory_cache *cache;
1096
1097         switch (cmd) {
1098         case CLI_INIT:
1099                 e->command = "sorcery memory cache show";
1100                 e->usage =
1101                     "Usage: sorcery memory cache show <name>\n"
1102                     "       Show sorcery memory cache configuration and statistics.\n";
1103                 return NULL;
1104         case CLI_GENERATE:
1105                 if (a->pos == 4) {
1106                         return sorcery_memory_cache_complete_name(a->word, a->n);
1107                 } else {
1108                         return NULL;
1109                 }
1110         }
1111
1112         if (a->argc != 5) {
1113                 return CLI_SHOWUSAGE;
1114         }
1115
1116         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1117         if (!cache) {
1118                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1119                 return CLI_FAILURE;
1120         }
1121
1122         ast_cli(a->fd, "Sorcery memory cache: %s\n", cache->name);
1123         ast_cli(a->fd, "Number of objects within cache: %d\n", ao2_container_count(cache->objects));
1124         if (cache->maximum_objects) {
1125                 ast_cli(a->fd, "Maximum allowed objects: %d\n", cache->maximum_objects);
1126         } else {
1127                 ast_cli(a->fd, "There is no limit on the maximum number of objects in the cache\n");
1128         }
1129         if (cache->object_lifetime_maximum) {
1130                 ast_cli(a->fd, "Number of seconds before object expires: %d\n", cache->object_lifetime_maximum);
1131         } else {
1132                 ast_cli(a->fd, "Object expiration is not enabled - cached objects will not expire\n");
1133         }
1134         if (cache->object_lifetime_stale) {
1135                 ast_cli(a->fd, "Number of seconds before object becomes stale: %d\n", cache->object_lifetime_stale);
1136         } else {
1137                 ast_cli(a->fd, "Object staleness is not enabled - cached objects will not go stale\n");
1138         }
1139         ast_cli(a->fd, "Prefetch: %s\n", AST_CLI_ONOFF(cache->prefetch));
1140         ast_cli(a->fd, "Expire all objects on reload: %s\n", AST_CLI_ONOFF(cache->expire_on_reload));
1141
1142         ao2_ref(cache, -1);
1143
1144         return CLI_SUCCESS;
1145 }
1146
1147 /*! \brief Structure used to pass data for printing cached object information */
1148 struct print_object_details {
1149         /*! \brief The sorcery memory cache */
1150         struct sorcery_memory_cache *cache;
1151         /*! \brief The CLI arguments */
1152         struct ast_cli_args *a;
1153 };
1154
1155 /*!
1156  * \internal
1157  * \brief Callback function for displaying object within the cache
1158  */
1159 static int sorcery_memory_cache_print_object(void *obj, void *arg, int flags)
1160 {
1161 #define FORMAT "%-25.25s %-15u %-15u \n"
1162         struct sorcery_memory_cached_object *cached = obj;
1163         struct print_object_details *details = arg;
1164         int seconds_until_expire = 0, seconds_until_stale = 0;
1165
1166         if (details->cache->object_lifetime_maximum) {
1167                 seconds_until_expire = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_maximum, 1)), ast_tvnow()) / 1000;
1168         }
1169         if (details->cache->object_lifetime_stale) {
1170                 seconds_until_stale = ast_tvdiff_ms(ast_tvadd(cached->created, ast_samp2tv(details->cache->object_lifetime_stale, 1)), ast_tvnow()) / 1000;
1171         }
1172
1173         ast_cli(details->a->fd, FORMAT, ast_sorcery_object_get_id(cached->object), MAX(seconds_until_stale, 0), MAX(seconds_until_expire, 0));
1174
1175         return CMP_MATCH;
1176 #undef FORMAT
1177 }
1178
1179 /*!
1180  * \internal
1181  * \brief CLI command implementation for 'sorcery memory cache dump'
1182  */
1183 static char *sorcery_memory_cache_dump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1184 {
1185 #define FORMAT "%-25.25s %-15.15s %-15.15s \n"
1186         struct sorcery_memory_cache *cache;
1187         struct print_object_details details;
1188
1189         switch (cmd) {
1190         case CLI_INIT:
1191                 e->command = "sorcery memory cache dump";
1192                 e->usage =
1193                     "Usage: sorcery memory cache dump <name>\n"
1194                     "       Dump a list of the objects within the cache, listed by object identifier.\n";
1195                 return NULL;
1196         case CLI_GENERATE:
1197                 if (a->pos == 4) {
1198                         return sorcery_memory_cache_complete_name(a->word, a->n);
1199                 } else {
1200                         return NULL;
1201                 }
1202         }
1203
1204         if (a->argc != 5) {
1205                 return CLI_SHOWUSAGE;
1206         }
1207
1208         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1209         if (!cache) {
1210                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1211                 return CLI_FAILURE;
1212         }
1213
1214         details.cache = cache;
1215         details.a = a;
1216
1217         ast_cli(a->fd, "Dumping sorcery memory cache '%s':\n", cache->name);
1218         if (!cache->object_lifetime_stale) {
1219                 ast_cli(a->fd, " * Staleness is not enabled - objects will not go stale\n");
1220         }
1221         if (!cache->object_lifetime_maximum) {
1222                 ast_cli(a->fd, " * Object lifetime is not enabled - objects will not expire\n");
1223         }
1224         ast_cli(a->fd, FORMAT, "Object Name", "Stale In", "Expires In");
1225         ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
1226         ao2_callback(cache->objects, OBJ_NODATA | OBJ_MULTIPLE, sorcery_memory_cache_print_object, &details);
1227         ast_cli(a->fd, FORMAT, "-------------------------", "---------------", "---------------");
1228         ast_cli(a->fd, "Total number of objects cached: %d\n", ao2_container_count(cache->objects));
1229
1230         ao2_ref(cache, -1);
1231
1232         return CLI_SUCCESS;
1233 #undef FORMAT
1234 }
1235
1236 /*!
1237  * \internal
1238  * \brief CLI tab completion for cached object names
1239  */
1240 static char *sorcery_memory_cache_complete_object_name(const char *cache_name, const char *word, int state)
1241 {
1242         struct sorcery_memory_cache *cache;
1243         struct sorcery_memory_cached_object *cached;
1244         struct ao2_iterator it_cached;
1245         int wordlen = strlen(word);
1246         int which = 0;
1247         char *result = NULL;
1248
1249         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
1250         if (!cache) {
1251                 return NULL;
1252         }
1253
1254         it_cached = ao2_iterator_init(cache->objects, 0);
1255         while ((cached = ao2_iterator_next(&it_cached))) {
1256                 if (!strncasecmp(word, ast_sorcery_object_get_id(cached->object), wordlen)
1257                         && ++which > state) {
1258                         result = ast_strdup(ast_sorcery_object_get_id(cached->object));
1259                 }
1260                 ao2_ref(cached, -1);
1261                 if (result) {
1262                         break;
1263                 }
1264         }
1265         ao2_iterator_destroy(&it_cached);
1266
1267         ao2_ref(cache, -1);
1268
1269         return result;
1270 }
1271
1272 /*!
1273  * \internal
1274  * \brief CLI command implementation for 'sorcery memory cache expire'
1275  */
1276 static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1277 {
1278         struct sorcery_memory_cache *cache;
1279
1280         switch (cmd) {
1281         case CLI_INIT:
1282                 e->command = "sorcery memory cache expire";
1283                 e->usage =
1284                     "Usage: sorcery memory cache expire <cache name> [object name]\n"
1285                     "       Expire a specific object or ALL objects within a sorcery memory cache.\n";
1286                 return NULL;
1287         case CLI_GENERATE:
1288                 if (a->pos == 4) {
1289                         return sorcery_memory_cache_complete_name(a->word, a->n);
1290                 } else if (a->pos == 5) {
1291                         return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
1292                 } else {
1293                         return NULL;
1294                 }
1295         }
1296
1297         if (a->argc > 6) {
1298                 return CLI_SHOWUSAGE;
1299         }
1300
1301         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1302         if (!cache) {
1303                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1304                 return CLI_FAILURE;
1305         }
1306
1307         ao2_wrlock(cache->objects);
1308         if (a->argc == 5) {
1309                 remove_all_from_cache(cache);
1310                 ast_cli(a->fd, "All objects have been removed from cache '%s'\n", a->argv[4]);
1311         } else {
1312                 if (!remove_from_cache(cache, a->argv[5], 1)) {
1313                         ast_cli(a->fd, "Successfully expired object '%s' from cache '%s'\n", a->argv[5], a->argv[4]);
1314                 } else {
1315                         ast_cli(a->fd, "Object '%s' was not expired from cache '%s' as it was not found\n", a->argv[5],
1316                                 a->argv[4]);
1317                 }
1318         }
1319         ao2_unlock(cache->objects);
1320
1321         ao2_ref(cache, -1);
1322
1323         return CLI_SUCCESS;
1324 }
1325
1326 /*!
1327  * \internal
1328  * \brief CLI command implementation for 'sorcery memory cache stale'
1329  */
1330 static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1331 {
1332         struct sorcery_memory_cache *cache;
1333
1334         switch (cmd) {
1335         case CLI_INIT:
1336                 e->command = "sorcery memory cache stale";
1337                 e->usage =
1338                     "Usage: sorcery memory cache stale <cache name> [object name]\n"
1339                     "       Mark a specific object or ALL objects as stale in a sorcery memory cache.\n";
1340                 return NULL;
1341         case CLI_GENERATE:
1342                 if (a->pos == 4) {
1343                         return sorcery_memory_cache_complete_name(a->word, a->n);
1344                 } else if (a->pos == 5) {
1345                         return sorcery_memory_cache_complete_object_name(a->argv[4], a->word, a->n);
1346                 } else {
1347                         return NULL;
1348                 }
1349         }
1350
1351         if (a->argc > 6) {
1352                 return CLI_SHOWUSAGE;
1353         }
1354
1355         cache = ao2_find(caches, a->argv[4], OBJ_SEARCH_KEY);
1356         if (!cache) {
1357                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not exist\n", a->argv[4]);
1358                 return CLI_FAILURE;
1359         }
1360
1361         if (!cache->object_lifetime_stale) {
1362                 ast_cli(a->fd, "Specified sorcery memory cache '%s' does not have staleness enabled\n", a->argv[4]);
1363                 ao2_ref(cache, -1);
1364                 return CLI_FAILURE;
1365         }
1366
1367         ao2_rdlock(cache->objects);
1368         if (a->argc == 5) {
1369                 mark_all_as_stale_in_cache(cache);
1370                 ast_cli(a->fd, "Marked all objects in sorcery memory cache '%s' as stale\n", a->argv[4]);
1371         } else {
1372                 if (!mark_object_as_stale_in_cache(cache, a->argv[5])) {
1373                         ast_cli(a->fd, "Successfully marked object '%s' in memory cache '%s' as stale\n",
1374                                 a->argv[5], a->argv[4]);
1375                 } else {
1376                         ast_cli(a->fd, "Object '%s' in sorcery memory cache '%s' could not be marked as stale as it was not found\n",
1377                                 a->argv[5], a->argv[4]);
1378                 }
1379         }
1380         ao2_unlock(cache->objects);
1381
1382         ao2_ref(cache, -1);
1383
1384         return CLI_SUCCESS;
1385 }
1386
1387 static struct ast_cli_entry cli_memory_cache[] = {
1388         AST_CLI_DEFINE(sorcery_memory_cache_show, "Show sorcery memory cache information"),
1389         AST_CLI_DEFINE(sorcery_memory_cache_dump, "Dump all objects within a sorcery memory cache"),
1390         AST_CLI_DEFINE(sorcery_memory_cache_expire, "Expire a specific object or ALL objects within a sorcery memory cache"),
1391         AST_CLI_DEFINE(sorcery_memory_cache_stale, "Mark a specific object or ALL objects as stale within a sorcery memory cache"),
1392 };
1393
1394 /*!
1395  * \internal
1396  * \brief AMI command implementation for 'SorceryMemoryCacheExpireObject'
1397  */
1398 static int sorcery_memory_cache_ami_expire_object(struct mansession *s, const struct message *m)
1399 {
1400         const char *cache_name = astman_get_header(m, "Cache");
1401         const char *object_name = astman_get_header(m, "Object");
1402         struct sorcery_memory_cache *cache;
1403         int res;
1404
1405         if (ast_strlen_zero(cache_name)) {
1406                 astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that a cache name be provided.\n");
1407                 return 0;
1408         } else if (ast_strlen_zero(object_name)) {
1409                 astman_send_error(s, m, "SorceryMemoryCacheExpireObject requires that an object name be provided\n");
1410                 return 0;
1411         }
1412
1413         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
1414         if (!cache) {
1415                 astman_send_error(s, m, "The provided cache does not exist\n");
1416                 return 0;
1417         }
1418
1419         ao2_wrlock(cache->objects);
1420         res = remove_from_cache(cache, object_name, 1);
1421         ao2_unlock(cache->objects);
1422
1423         ao2_ref(cache, -1);
1424
1425         if (!res) {
1426                 astman_send_ack(s, m, "The provided object was expired from the cache\n");
1427         } else {
1428                 astman_send_error(s, m, "The provided object could not be expired from the cache\n");
1429         }
1430
1431         return 0;
1432 }
1433
1434 /*!
1435  * \internal
1436  * \brief AMI command implementation for 'SorceryMemoryCacheExpire'
1437  */
1438 static int sorcery_memory_cache_ami_expire(struct mansession *s, const struct message *m)
1439 {
1440         const char *cache_name = astman_get_header(m, "Cache");
1441         struct sorcery_memory_cache *cache;
1442
1443         if (ast_strlen_zero(cache_name)) {
1444                 astman_send_error(s, m, "SorceryMemoryCacheExpire requires that a cache name be provided.\n");
1445                 return 0;
1446         }
1447
1448         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
1449         if (!cache) {
1450                 astman_send_error(s, m, "The provided cache does not exist\n");
1451                 return 0;
1452         }
1453
1454         ao2_wrlock(cache->objects);
1455         remove_all_from_cache(cache);
1456         ao2_unlock(cache->objects);
1457
1458         ao2_ref(cache, -1);
1459
1460         astman_send_ack(s, m, "All objects were expired from the cache\n");
1461
1462         return 0;
1463 }
1464
1465 /*!
1466  * \internal
1467  * \brief AMI command implementation for 'SorceryMemoryCacheStaleObject'
1468  */
1469 static int sorcery_memory_cache_ami_stale_object(struct mansession *s, const struct message *m)
1470 {
1471         const char *cache_name = astman_get_header(m, "Cache");
1472         const char *object_name = astman_get_header(m, "Object");
1473         struct sorcery_memory_cache *cache;
1474         int res;
1475
1476         if (ast_strlen_zero(cache_name)) {
1477                 astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that a cache name be provided.\n");
1478                 return 0;
1479         } else if (ast_strlen_zero(object_name)) {
1480                 astman_send_error(s, m, "SorceryMemoryCacheStaleObject requires that an object name be provided\n");
1481                 return 0;
1482         }
1483
1484         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
1485         if (!cache) {
1486                 astman_send_error(s, m, "The provided cache does not exist\n");
1487                 return 0;
1488         }
1489
1490         ao2_rdlock(cache->objects);
1491         res = mark_object_as_stale_in_cache(cache, object_name);
1492         ao2_unlock(cache->objects);
1493
1494         ao2_ref(cache, -1);
1495
1496         if (!res) {
1497                 astman_send_ack(s, m, "The provided object was marked as stale in the cache\n");
1498         } else {
1499                 astman_send_error(s, m, "The provided object could not be marked as stale in the cache\n");
1500         }
1501
1502         return 0;
1503 }
1504
1505 /*!
1506  * \internal
1507  * \brief AMI command implementation for 'SorceryMemoryCacheStale'
1508  */
1509 static int sorcery_memory_cache_ami_stale(struct mansession *s, const struct message *m)
1510 {
1511         const char *cache_name = astman_get_header(m, "Cache");
1512         struct sorcery_memory_cache *cache;
1513
1514         if (ast_strlen_zero(cache_name)) {
1515                 astman_send_error(s, m, "SorceryMemoryCacheStale requires that a cache name be provided.\n");
1516                 return 0;
1517         }
1518
1519         cache = ao2_find(caches, cache_name, OBJ_SEARCH_KEY);
1520         if (!cache) {
1521                 astman_send_error(s, m, "The provided cache does not exist\n");
1522                 return 0;
1523         }
1524
1525         ao2_rdlock(cache->objects);
1526         mark_all_as_stale_in_cache(cache);
1527         ao2_unlock(cache->objects);
1528
1529         ao2_ref(cache, -1);
1530
1531         astman_send_ack(s, m, "All objects were marked as stale in the cache\n");
1532
1533         return 0;
1534 }
1535
1536 #ifdef TEST_FRAMEWORK
1537
1538 /*! \brief Dummy sorcery object */
1539 struct test_sorcery_object {
1540         SORCERY_OBJECT(details);
1541 };
1542
1543 /*!
1544  * \internal
1545  * \brief Allocator for test object
1546  *
1547  * \param id The identifier for the object
1548  *
1549  * \retval non-NULL success
1550  * \retval NULL failure
1551  */
1552 static void *test_sorcery_object_alloc(const char *id)
1553 {
1554         return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
1555 }
1556
1557 /*!
1558  * \internal
1559  * \brief Allocator for test sorcery instance
1560  *
1561  * \retval non-NULL success
1562  * \retval NULL failure
1563  */
1564 static struct ast_sorcery *alloc_and_initialize_sorcery(void)
1565 {
1566         struct ast_sorcery *sorcery;
1567
1568         if (!(sorcery = ast_sorcery_open())) {
1569                 return NULL;
1570         }
1571
1572         if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
1573                 ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1574                 ast_sorcery_unref(sorcery);
1575                 return NULL;
1576         }
1577
1578         return sorcery;
1579 }
1580
1581 AST_TEST_DEFINE(open_with_valid_options)
1582 {
1583         int res = AST_TEST_PASS;
1584         struct sorcery_memory_cache *cache;
1585
1586         switch (cmd) {
1587         case TEST_INIT:
1588                 info->name = "open_with_valid_options";
1589                 info->category = "/res/res_sorcery_memory_cache/";
1590                 info->summary = "Attempt to create sorcery memory caches using valid options";
1591                 info->description = "This test performs the following:\n"
1592                         "\t* Creates a memory cache with default configuration\n"
1593                         "\t* Creates a memory cache with a maximum object count of 10 and verifies it\n"
1594                         "\t* Creates a memory cache with a maximum object lifetime of 60 and verifies it\n"
1595                         "\t* Creates a memory cache with a stale object lifetime of 90 and verifies it\n";
1596                 return AST_TEST_NOT_RUN;
1597         case TEST_EXECUTE:
1598                 break;
1599         }
1600
1601         cache = sorcery_memory_cache_open("");
1602         if (!cache) {
1603                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default configuration\n");
1604                 res = AST_TEST_FAIL;
1605         } else {
1606                 sorcery_memory_cache_close(cache);
1607         }
1608
1609         cache = sorcery_memory_cache_open("maximum_objects=10");
1610         if (!cache) {
1611                 ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object count of 10\n");
1612                 res = AST_TEST_FAIL;
1613         } else {
1614                 if (cache->maximum_objects != 10) {
1615                         ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of 10 but it has '%u'\n",
1616                                 cache->maximum_objects);
1617                 }
1618                 sorcery_memory_cache_close(cache);
1619         }
1620
1621         cache = sorcery_memory_cache_open("object_lifetime_maximum=60");
1622         if (!cache) {
1623                 ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object lifetime of 60\n");
1624                 res = AST_TEST_FAIL;
1625         } else {
1626                 if (cache->object_lifetime_maximum != 60) {
1627                         ast_test_status_update(test, "Created a sorcery memory cache with a maximum object lifetime of 60 but it has '%u'\n",
1628                                 cache->object_lifetime_maximum);
1629                 }
1630                 sorcery_memory_cache_close(cache);
1631         }
1632
1633         cache = sorcery_memory_cache_open("object_lifetime_stale=90");
1634         if (!cache) {
1635                 ast_test_status_update(test, "Failed to create a sorcery memory cache with a stale object lifetime of 90\n");
1636                 res = AST_TEST_FAIL;
1637         } else {
1638                 if (cache->object_lifetime_stale != 90) {
1639                         ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of 90 but it has '%u'\n",
1640                                 cache->object_lifetime_stale);
1641                 }
1642                 sorcery_memory_cache_close(cache);
1643         }
1644
1645
1646         return res;
1647 }
1648
1649 AST_TEST_DEFINE(open_with_invalid_options)
1650 {
1651         int res = AST_TEST_PASS;
1652         struct sorcery_memory_cache *cache;
1653
1654         switch (cmd) {
1655         case TEST_INIT:
1656                 info->name = "open_with_invalid_options";
1657                 info->category = "/res/res_sorcery_memory_cache/";
1658                 info->summary = "Attempt to create sorcery memory caches using invalid options";
1659                 info->description = "This test attempts to perform the following:\n"
1660                         "\t* Create a memory cache with an empty name\n"
1661                         "\t* Create a memory cache with a maximum object count of -1\n"
1662                         "\t* Create a memory cache with a maximum object count of toast\n"
1663                         "\t* Create a memory cache with a maximum object lifetime of -1\n"
1664                         "\t* Create a memory cache with a maximum object lifetime of toast\n"
1665                         "\t* Create a memory cache with a stale object lifetime of -1\n"
1666                         "\t* Create a memory cache with a stale object lifetime of toast\n";
1667                 return AST_TEST_NOT_RUN;
1668         case TEST_EXECUTE:
1669                 break;
1670         }
1671
1672         cache = sorcery_memory_cache_open("name=");
1673         if (cache) {
1674                 ast_test_status_update(test, "Created a sorcery memory cache with an empty name\n");
1675                 sorcery_memory_cache_close(cache);
1676                 res = AST_TEST_FAIL;
1677         }
1678
1679         cache = sorcery_memory_cache_open("maximum_objects=-1");
1680         if (cache) {
1681                 ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of -1\n");
1682                 sorcery_memory_cache_close(cache);
1683                 res = AST_TEST_FAIL;
1684         }
1685
1686         cache = sorcery_memory_cache_open("maximum_objects=toast");
1687         if (cache) {
1688                 ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of toast\n");
1689                 sorcery_memory_cache_close(cache);
1690                 res = AST_TEST_FAIL;
1691         }
1692
1693         cache = sorcery_memory_cache_open("object_lifetime_maximum=-1");
1694         if (cache) {
1695                 ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of -1\n");
1696                 sorcery_memory_cache_close(cache);
1697                 res = AST_TEST_FAIL;
1698         }
1699
1700         cache = sorcery_memory_cache_open("object_lifetime_maximum=toast");
1701         if (cache) {
1702                 ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of toast\n");
1703                 sorcery_memory_cache_close(cache);
1704                 res = AST_TEST_FAIL;
1705         }
1706
1707         cache = sorcery_memory_cache_open("object_lifetime_stale=-1");
1708         if (cache) {
1709                 ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of -1\n");
1710                 sorcery_memory_cache_close(cache);
1711                 res = AST_TEST_FAIL;
1712         }
1713
1714         cache = sorcery_memory_cache_open("object_lifetime_stale=toast");
1715         if (cache) {
1716                 ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of toast\n");
1717                 sorcery_memory_cache_close(cache);
1718                 res = AST_TEST_FAIL;
1719         }
1720
1721         cache = sorcery_memory_cache_open("tacos");
1722         if (cache) {
1723                 ast_test_status_update(test, "Created a sorcery memory cache with an invalid configuration option 'tacos'\n");
1724                 sorcery_memory_cache_close(cache);
1725                 res = AST_TEST_FAIL;
1726         }
1727
1728         return res;
1729 }
1730
1731 AST_TEST_DEFINE(create_and_retrieve)
1732 {
1733         int res = AST_TEST_FAIL;
1734         struct ast_sorcery *sorcery = NULL;
1735         struct sorcery_memory_cache *cache = NULL;
1736         RAII_VAR(void *, object, NULL, ao2_cleanup);
1737         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
1738
1739         switch (cmd) {
1740         case TEST_INIT:
1741                 info->name = "create";
1742                 info->category = "/res/res_sorcery_memory_cache/";
1743                 info->summary = "Attempt to create an object in the cache";
1744                 info->description = "This test performs the following:\n"
1745                         "\t* Creates a memory cache with default options\n"
1746                         "\t* Creates a sorcery instance with a test object\n"
1747                         "\t* Creates a test object with an id of test\n"
1748                         "\t* Pushes the test object into the memory cache\n"
1749                         "\t* Confirms that the test object is in the cache\n";
1750                 return AST_TEST_NOT_RUN;
1751         case TEST_EXECUTE:
1752                 break;
1753         }
1754
1755         cache = sorcery_memory_cache_open("");
1756         if (!cache) {
1757                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
1758                 goto cleanup;
1759         }
1760
1761         if (ao2_container_count(cache->objects)) {
1762                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
1763                 goto cleanup;
1764         }
1765
1766         sorcery = alloc_and_initialize_sorcery();
1767         if (!sorcery) {
1768                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
1769                 goto cleanup;
1770         }
1771
1772         object = ast_sorcery_alloc(sorcery, "test", "test");
1773         if (!object) {
1774                 ast_test_status_update(test, "Failed to allocate a test object\n");
1775                 goto cleanup;
1776         }
1777
1778         sorcery_memory_cache_create(sorcery, cache, object);
1779
1780         if (!ao2_container_count(cache->objects)) {
1781                 ast_test_status_update(test, "Added test object to memory cache but cache remains empty\n");
1782                 goto cleanup;
1783         }
1784
1785         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
1786         if (!cached_object) {
1787                 ast_test_status_update(test, "Object placed into memory cache could not be retrieved\n");
1788                 goto cleanup;
1789         }
1790
1791         if (cached_object != object) {
1792                 ast_test_status_update(test, "Object retrieved from memory cached is not the one we cached\n");
1793                 goto cleanup;
1794         }
1795
1796         res = AST_TEST_PASS;
1797
1798 cleanup:
1799         if (cache) {
1800                 sorcery_memory_cache_close(cache);
1801         }
1802         if (sorcery) {
1803                 ast_sorcery_unref(sorcery);
1804         }
1805
1806         return res;
1807 }
1808
1809 AST_TEST_DEFINE(update)
1810 {
1811         int res = AST_TEST_FAIL;
1812         struct ast_sorcery *sorcery = NULL;
1813         struct sorcery_memory_cache *cache = NULL;
1814         RAII_VAR(void *, original_object, NULL, ao2_cleanup);
1815         RAII_VAR(void *, updated_object, NULL, ao2_cleanup);
1816         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
1817
1818         switch (cmd) {
1819         case TEST_INIT:
1820                 info->name = "create";
1821                 info->category = "/res/res_sorcery_memory_cache/";
1822                 info->summary = "Attempt to create and then update an object in the cache";
1823                 info->description = "This test performs the following:\n"
1824                         "\t* Creates a memory cache with default options\n"
1825                         "\t* Creates a sorcery instance with a test object\n"
1826                         "\t* Creates a test object with an id of test\n"
1827                         "\t* Pushes the test object into the memory cache\n"
1828                         "\t* Confirms that the test object is in the cache\n"
1829                         "\t* Creates a new test object with the same id of test\n"
1830                         "\t* Pushes the new test object into the memory cache\n"
1831                         "\t* Confirms that the new test object has replaced the old one\n";
1832                 return AST_TEST_NOT_RUN;
1833         case TEST_EXECUTE:
1834                 break;
1835         }
1836
1837         cache = sorcery_memory_cache_open("");
1838         if (!cache) {
1839                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
1840                 goto cleanup;
1841         }
1842
1843         if (ao2_container_count(cache->objects)) {
1844                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
1845                 goto cleanup;
1846         }
1847
1848         sorcery = alloc_and_initialize_sorcery();
1849         if (!sorcery) {
1850                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
1851                 goto cleanup;
1852         }
1853
1854         original_object = ast_sorcery_alloc(sorcery, "test", "test");
1855         if (!original_object) {
1856                 ast_test_status_update(test, "Failed to allocate a test object\n");
1857                 goto cleanup;
1858         }
1859
1860         sorcery_memory_cache_create(sorcery, cache, original_object);
1861
1862         updated_object = ast_sorcery_alloc(sorcery, "test", "test");
1863         if (!updated_object) {
1864                 ast_test_status_update(test, "Failed to allocate an updated test object\n");
1865                 goto cleanup;
1866         }
1867
1868         sorcery_memory_cache_create(sorcery, cache, updated_object);
1869
1870         if (ao2_container_count(cache->objects) != 1) {
1871                 ast_test_status_update(test, "Added updated test object to memory cache but cache now contains %d objects instead of 1\n",
1872                         ao2_container_count(cache->objects));
1873                 goto cleanup;
1874         }
1875
1876         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
1877         if (!cached_object) {
1878                 ast_test_status_update(test, "Updated object placed into memory cache could not be retrieved\n");
1879                 goto cleanup;
1880         }
1881
1882         if (cached_object == original_object) {
1883                 ast_test_status_update(test, "Updated object placed into memory cache but old one is being retrieved\n");
1884                 goto cleanup;
1885         } else if (cached_object != updated_object) {
1886                 ast_test_status_update(test, "Updated object placed into memory cache but different one is being retrieved\n");
1887                 goto cleanup;
1888         }
1889
1890         res = AST_TEST_PASS;
1891
1892 cleanup:
1893         if (cache) {
1894                 sorcery_memory_cache_close(cache);
1895         }
1896         if (sorcery) {
1897                 ast_sorcery_unref(sorcery);
1898         }
1899
1900         return res;
1901 }
1902
1903 AST_TEST_DEFINE(delete)
1904 {
1905         int res = AST_TEST_FAIL;
1906         struct ast_sorcery *sorcery = NULL;
1907         struct sorcery_memory_cache *cache = NULL;
1908         RAII_VAR(void *, object, NULL, ao2_cleanup);
1909         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
1910
1911         switch (cmd) {
1912         case TEST_INIT:
1913                 info->name = "delete";
1914                 info->category = "/res/res_sorcery_memory_cache/";
1915                 info->summary = "Attempt to create and then delete an object in the cache";
1916                 info->description = "This test performs the following:\n"
1917                         "\t* Creates a memory cache with default options\n"
1918                         "\t* Creates a sorcery instance with a test object\n"
1919                         "\t* Creates a test object with an id of test\n"
1920                         "\t* Pushes the test object into the memory cache\n"
1921                         "\t* Confirms that the test object is in the cache\n"
1922                         "\t* Deletes the test object from the cache\n"
1923                         "\t* Confirms that the test object is no longer in the cache\n";
1924                 return AST_TEST_NOT_RUN;
1925         case TEST_EXECUTE:
1926                 break;
1927         }
1928
1929         cache = sorcery_memory_cache_open("");
1930         if (!cache) {
1931                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
1932                 goto cleanup;
1933         }
1934
1935         if (ao2_container_count(cache->objects)) {
1936                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
1937                 goto cleanup;
1938         }
1939
1940         sorcery = alloc_and_initialize_sorcery();
1941         if (!sorcery) {
1942                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
1943                 goto cleanup;
1944         }
1945
1946         object = ast_sorcery_alloc(sorcery, "test", "test");
1947         if (!object) {
1948                 ast_test_status_update(test, "Failed to allocate a test object\n");
1949                 goto cleanup;
1950         }
1951
1952         sorcery_memory_cache_create(sorcery, cache, object);
1953
1954         if (!ao2_container_count(cache->objects)) {
1955                 ast_test_status_update(test, "Added test object to memory cache but cache contains no objects\n");
1956                 goto cleanup;
1957         }
1958
1959         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
1960         if (!cached_object) {
1961                 ast_test_status_update(test, "Test object placed into memory cache could not be retrieved\n");
1962                 goto cleanup;
1963         }
1964
1965         ao2_ref(cached_object, -1);
1966         cached_object = NULL;
1967
1968         sorcery_memory_cache_delete(sorcery, cache, object);
1969
1970         cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
1971         if (cached_object) {
1972                 ast_test_status_update(test, "Test object deleted from memory cache can still be retrieved\n");
1973                 goto cleanup;
1974         }
1975
1976         res = AST_TEST_PASS;
1977
1978 cleanup:
1979         if (cache) {
1980                 sorcery_memory_cache_close(cache);
1981         }
1982         if (sorcery) {
1983                 ast_sorcery_unref(sorcery);
1984         }
1985
1986         return res;
1987 }
1988
1989 static int check_cache_content(struct ast_test *test, struct ast_sorcery *sorcery, struct sorcery_memory_cache *cache,
1990                 const char **in_cache, size_t num_in_cache, const char **not_in_cache, size_t num_not_in_cache)
1991 {
1992         int i;
1993         int res = 0;
1994         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
1995
1996         for (i = 0; i < num_in_cache; ++i) {
1997                 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", in_cache[i]);
1998                 if (!cached_object) {
1999                         ast_test_status_update(test, "Failed to retrieve '%s' object from the cache\n",
2000                                         in_cache[i]);
2001                         res = -1;
2002                 }
2003                 ao2_ref(cached_object, -1);
2004         }
2005
2006         for (i = 0; i < num_not_in_cache; ++i) {
2007                 cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", not_in_cache[i]);
2008                 if (cached_object) {
2009                         ast_test_status_update(test, "Retrieved '%s' object from the cache unexpectedly\n",
2010                                         not_in_cache[i]);
2011                         ao2_ref(cached_object, -1);
2012                         res = -1;
2013                 }
2014         }
2015
2016         return res;
2017 }
2018
2019 AST_TEST_DEFINE(maximum_objects)
2020 {
2021         int res = AST_TEST_FAIL;
2022         struct ast_sorcery *sorcery = NULL;
2023         struct sorcery_memory_cache *cache = NULL;
2024         RAII_VAR(void *, alice, NULL, ao2_cleanup);
2025         RAII_VAR(void *, bob, NULL, ao2_cleanup);
2026         RAII_VAR(void *, charlie, NULL, ao2_cleanup);
2027         RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
2028         const char *in_cache[2];
2029         const char *not_in_cache[2];
2030
2031         switch (cmd) {
2032         case TEST_INIT:
2033                 info->name = "maximum_objects";
2034                 info->category = "/res/res_sorcery_memory_cache/";
2035                 info->summary = "Ensure that the 'maximum_objects' option works as expected";
2036                 info->description = "This test performs the following:\n"
2037                         "\t* Creates a memory cache with maximum_objects=2\n"
2038                         "\t* Creates a sorcery instance\n"
2039                         "\t* Creates a three test objects: alice, bob, charlie, and david\n"
2040                         "\t* Pushes alice and bob into the memory cache\n"
2041                         "\t* Confirms that alice and bob are in the memory cache\n"
2042                         "\t* Pushes charlie into the memory cache\n"
2043                         "\t* Confirms that bob and charlie are in the memory cache\n"
2044                         "\t* Deletes charlie from the memory cache\n"
2045                         "\t* Confirms that only bob is in the memory cache\n"
2046                         "\t* Pushes alice into the memory cache\n"
2047                         "\t* Confirms that bob and alice are in the memory cache\n";
2048                 return AST_TEST_NOT_RUN;
2049         case TEST_EXECUTE:
2050                 break;
2051         }
2052
2053         cache = sorcery_memory_cache_open("maximum_objects=2");
2054         if (!cache) {
2055                 ast_test_status_update(test, "Failed to create a sorcery memory cache with maximum_objects=2\n");
2056                 goto cleanup;
2057         }
2058
2059         if (ao2_container_count(cache->objects)) {
2060                 ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
2061                 goto cleanup;
2062         }
2063
2064         sorcery = alloc_and_initialize_sorcery();
2065         if (!sorcery) {
2066                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2067                 goto cleanup;
2068         }
2069
2070         alice = ast_sorcery_alloc(sorcery, "test", "alice");
2071         bob = ast_sorcery_alloc(sorcery, "test", "bob");
2072         charlie = ast_sorcery_alloc(sorcery, "test", "charlie");
2073
2074         if (!alice || !bob || !charlie) {
2075                 ast_test_status_update(test, "Failed to allocate sorcery object(s)\n");
2076                 goto cleanup;
2077         }
2078
2079         sorcery_memory_cache_create(sorcery, cache, alice);
2080         in_cache[0] = "alice";
2081         in_cache[1] = NULL;
2082         not_in_cache[0] = "bob";
2083         not_in_cache[1] = "charlie";
2084         if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
2085                 goto cleanup;
2086         }
2087
2088         /* Delays are added to ensure that we are not adding cache entries within the
2089          * same microsecond
2090          */
2091         usleep(1000);
2092
2093         sorcery_memory_cache_create(sorcery, cache, bob);
2094         in_cache[0] = "alice";
2095         in_cache[1] = "bob";
2096         not_in_cache[0] = "charlie";
2097         not_in_cache[1] = NULL;
2098         if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2099                 goto cleanup;
2100         }
2101
2102         usleep(1000);
2103
2104         sorcery_memory_cache_create(sorcery, cache, charlie);
2105         in_cache[0] = "bob";
2106         in_cache[1] = "charlie";
2107         not_in_cache[0] = "alice";
2108         not_in_cache[1] = NULL;
2109         if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2110                 goto cleanup;
2111         }
2112         usleep(1000);
2113
2114         sorcery_memory_cache_delete(sorcery, cache, charlie);
2115         in_cache[0] = "bob";
2116         in_cache[1] = NULL;
2117         not_in_cache[0] = "alice";
2118         not_in_cache[1] = "charlie";
2119         if (check_cache_content(test, sorcery, cache, in_cache, 1, not_in_cache, 2)) {
2120                 goto cleanup;
2121         }
2122         usleep(1000);
2123
2124         sorcery_memory_cache_create(sorcery, cache, alice);
2125         in_cache[0] = "bob";
2126         in_cache[1] = "alice";
2127         not_in_cache[0] = "charlie";
2128         not_in_cache[1] = NULL;
2129         if (check_cache_content(test, sorcery, cache, in_cache, 2, not_in_cache, 1)) {
2130                 goto cleanup;
2131         }
2132
2133         res = AST_TEST_PASS;
2134
2135 cleanup:
2136         if (cache) {
2137                 sorcery_memory_cache_close(cache);
2138         }
2139         if (sorcery) {
2140                 ast_sorcery_unref(sorcery);
2141         }
2142
2143         return res;
2144 }
2145
2146 AST_TEST_DEFINE(expiration)
2147 {
2148         int res = AST_TEST_FAIL;
2149         struct ast_sorcery *sorcery = NULL;
2150         struct sorcery_memory_cache *cache = NULL;
2151         int i;
2152
2153         switch (cmd) {
2154         case TEST_INIT:
2155                 info->name = "expiration";
2156                 info->category = "/res/res_sorcery_memory_cache/";
2157                 info->summary = "Add objects to a cache configured with maximum lifetime, confirm they are removed";
2158                 info->description = "This test performs the following:\n"
2159                         "\t* Creates a memory cache with a maximum object lifetime of 5 seconds\n"
2160                         "\t* Pushes 10 objects into the memory cache\n"
2161                         "\t* Waits (up to) 10 seconds for expiration to occur\n"
2162                         "\t* Confirms that the objects have been removed from the cache\n";
2163                 return AST_TEST_NOT_RUN;
2164         case TEST_EXECUTE:
2165                 break;
2166         }
2167
2168         cache = sorcery_memory_cache_open("object_lifetime_maximum=5");
2169         if (!cache) {
2170                 ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
2171                 goto cleanup;
2172         }
2173
2174         sorcery = alloc_and_initialize_sorcery();
2175         if (!sorcery) {
2176                 ast_test_status_update(test, "Failed to create a test sorcery instance\n");
2177                 goto cleanup;
2178         }
2179
2180         cache->cache_notify = 1;
2181         ast_mutex_init(&cache->lock);
2182         ast_cond_init(&cache->cond, NULL);
2183
2184         for (i = 0; i < 5; ++i) {
2185                 char uuid[AST_UUID_STR_LEN];
2186                 void *object;
2187
2188                 object = ast_sorcery_alloc(sorcery, "test", ast_uuid_generate_str(uuid, sizeof(uuid)));
2189                 if (!object) {
2190                         ast_test_status_update(test, "Failed to allocate test object for expiration\n");
2191                         goto cleanup;
2192                 }
2193
2194                 sorcery_memory_cache_create(sorcery, cache, object);
2195
2196                 ao2_ref(object, -1);
2197         }
2198
2199         ast_mutex_lock(&cache->lock);
2200         while (!cache->cache_completed) {
2201                 struct timeval start = ast_tvnow();
2202                 struct timespec end = {
2203                         .tv_sec = start.tv_sec + 10,
2204                         .tv_nsec = start.tv_usec * 1000,
2205                 };
2206
2207                 if (ast_cond_timedwait(&cache->cond, &cache->lock, &end) == ETIMEDOUT) {
2208                         break;
2209                 }
2210         }
2211         ast_mutex_unlock(&cache->lock);
2212
2213         if (ao2_container_count(cache->objects)) {
2214                 ast_test_status_update(test, "Objects placed into the memory cache did not expire and get removed\n");
2215                 goto cleanup;
2216         }
2217
2218         res = AST_TEST_PASS;
2219
2220 cleanup:
2221         if (cache) {
2222                 if (cache->cache_notify) {
2223                         ast_cond_destroy(&cache->cond);
2224                         ast_mutex_destroy(&cache->lock);
2225                 }
2226                 sorcery_memory_cache_close(cache);
2227         }
2228         if (sorcery) {
2229                 ast_sorcery_unref(sorcery);
2230         }
2231
2232         return res;
2233 }
2234
2235 /*!
2236  * \brief Backend data that the mock sorcery wizard uses to create objects
2237  */
2238 static struct backend_data {
2239         /*! An arbitrary data field */
2240         int salt;
2241         /*! Another arbitrary data field */
2242         int pepper;
2243         /*! Indicates whether the backend has data */
2244         int exists;
2245 } *real_backend_data;
2246
2247 /*!
2248  * \brief Sorcery object created based on backend data
2249  */
2250 struct test_data {
2251         SORCERY_OBJECT(details);
2252         /*! Mirrors the backend data's salt field */
2253         int salt;
2254         /*! Mirrors the backend data's pepper field */
2255         int pepper;
2256 };
2257
2258 /*!
2259  * \brief Allocation callback for test_data sorcery object
2260  */
2261 static void *test_data_alloc(const char *id) {
2262         return ast_sorcery_generic_alloc(sizeof(struct test_data), NULL);
2263 }
2264
2265 /*!
2266  * \brief Callback for retrieving sorcery object by ID
2267  *
2268  * The mock wizard uses the \ref real_backend_data in order to construct
2269  * objects. If the backend data is "nonexisent" then no object is returned.
2270  * Otherwise, an object is created that has the backend data's salt and
2271  * pepper values copied.
2272  *
2273  * \param sorcery The sorcery instance
2274  * \param data Unused
2275  * \param type The object type. Will always be "test".
2276  * \param id The object id. Will always be "test".
2277  *
2278  * \retval NULL Backend data does not exist
2279  * \retval non-NULL An object representing the backend data
2280  */
2281 static void *mock_retrieve_id(const struct ast_sorcery *sorcery, void *data,
2282                 const char *type, const char *id)
2283 {
2284         struct test_data *b_data;
2285
2286         if (!real_backend_data->exists) {
2287                 return NULL;
2288         }
2289
2290         b_data = ast_sorcery_alloc(sorcery, type, id);
2291         if (!b_data) {
2292                 return NULL;
2293         }
2294
2295         b_data->salt = real_backend_data->salt;
2296         b_data->pepper = real_backend_data->pepper;
2297         return b_data;
2298 }
2299
2300 /*!
2301  * \brief A mock sorcery wizard used for the stale test
2302  */
2303 static struct ast_sorcery_wizard mock_wizard = {
2304         .name = "mock",
2305         .retrieve_id = mock_retrieve_id,
2306 };
2307
2308 /*!
2309  * \brief Wait for the cache to be updated after a stale object is retrieved.
2310  *
2311  * Since the cache does not know what type of objects it is dealing with, and
2312  * since we do not have the internals of the cache, the only way to make this
2313  * determination is to continuously retrieve an object from the cache until
2314  * we retrieve a different object than we had previously retrieved.
2315  *
2316  * \param sorcery The sorcery instance
2317  * \param previous_object The object we had previously retrieved from the cache
2318  * \param[out] new_object The new object we retrieve from the cache
2319  *
2320  * \retval 0 Successfully retrieved a new object from the cache
2321  * \retval non-zero Failed to retrieve a new object from the cache
2322  */
2323 static int wait_for_cache_update(const struct ast_sorcery *sorcery,
2324                 void *previous_object, struct test_data **new_object)
2325 {
2326         struct timeval start = ast_tvnow();
2327
2328         while (ast_remaining_ms(start, 5000) > 0) {
2329                 void *object;
2330
2331                 object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
2332                 if (object != previous_object) {
2333                         *new_object = object;
2334                         return 0;
2335                 }
2336                 ao2_cleanup(object);
2337         }
2338
2339         return -1;
2340 }
2341
2342 AST_TEST_DEFINE(stale)
2343 {
2344         int res = AST_TEST_FAIL;
2345         struct ast_sorcery *sorcery = NULL;
2346         struct test_data *backend_object;
2347         struct backend_data iterations[] = {
2348                 { .salt = 1,      .pepper = 2,       .exists = 1 },
2349                 { .salt = 568729, .pepper = -234123, .exists = 1 },
2350                 { .salt = 0,      .pepper = 0,       .exists = 0 },
2351         };
2352         struct backend_data initial = {
2353                 .salt = 0,
2354                 .pepper = 0,
2355                 .exists = 1,
2356         };
2357         int i;
2358
2359         switch (cmd) {
2360         case TEST_INIT:
2361                 info->name = "stale";
2362                 info->category = "/res/res_sorcery_memory_cache/";
2363                 info->summary = "Ensure that stale objects are replaced with updated objects";
2364                 info->description = "This test performs the following:\n"
2365                         "\t* Create a sorcery instance with two wizards"
2366                         "\t\t* The first is a memory cache that marks items stale after 3 seconds\n"
2367                         "\t\t* The second is a mock of a back-end\n"
2368                         "\t* Pre-populates the cache by retrieving some initial data from the backend.\n"
2369                         "\t* Performs iterations of the following:\n"
2370                         "\t\t* Update backend data with new values\n"
2371                         "\t\t* Retrieve item from the cache\n"
2372                         "\t\t* Ensure the retrieved item does not have the new backend values\n"
2373                         "\t\t* Wait for cached object to become stale\n"
2374                         "\t\t* Retrieve the stale cached object\n"
2375                         "\t\t* Ensure that the stale object retrieved is the same as the fresh one from earlier\n"
2376                         "\t\t* Wait for the cache to update with new data\n"
2377                         "\t\t* Ensure that new data in the cache matches backend data\n";
2378                 return AST_TEST_NOT_RUN;
2379         case TEST_EXECUTE:
2380                 break;
2381         }
2382
2383         ast_sorcery_wizard_register(&mock_wizard);
2384
2385         sorcery = ast_sorcery_open();
2386         if (!sorcery) {
2387                 ast_test_status_update(test, "Failed to create sorcery instance\n");
2388                 goto cleanup;
2389         }
2390
2391         ast_sorcery_apply_wizard_mapping(sorcery, "test", "memory_cache",
2392                         "object_lifetime_stale=3", 1);
2393         ast_sorcery_apply_wizard_mapping(sorcery, "test", "mock", NULL, 0);
2394         ast_sorcery_internal_object_register(sorcery, "test", test_data_alloc, NULL, NULL);
2395
2396         /* Prepopulate the cache */
2397         real_backend_data = &initial;
2398
2399         backend_object = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
2400         if (!backend_object) {
2401                 ast_test_status_update(test, "Unable to retrieve backend data and populate the cache\n");
2402                 goto cleanup;
2403         }
2404         ao2_ref(backend_object, -1);
2405
2406         for (i = 0; i < ARRAY_LEN(iterations); ++i) {
2407                 RAII_VAR(struct test_data *, cache_fresh, NULL, ao2_cleanup);
2408                 RAII_VAR(struct test_data *, cache_stale, NULL, ao2_cleanup);
2409                 RAII_VAR(struct test_data *, cache_new, NULL, ao2_cleanup);
2410
2411                 real_backend_data = &iterations[i];
2412
2413                 ast_test_status_update(test, "Begininning iteration %d\n", i);
2414
2415                 cache_fresh = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
2416                 if (!cache_fresh) {
2417                         ast_test_status_update(test, "Unable to retrieve fresh cached object\n");
2418                         goto cleanup;
2419                 }
2420
2421                 if (cache_fresh->salt == iterations[i].salt || cache_fresh->pepper == iterations[i].pepper) {
2422                         ast_test_status_update(test, "Fresh cached object has unexpected values. Did we hit the backend?\n");
2423                         goto cleanup;
2424                 }
2425
2426                 sleep(5);
2427
2428                 cache_stale = ast_sorcery_retrieve_by_id(sorcery, "test", "test");
2429                 if (!cache_stale) {
2430                         ast_test_status_update(test, "Unable to retrieve stale cached object\n");
2431                         goto cleanup;
2432                 }
2433
2434                 if (cache_stale != cache_fresh) {
2435                         ast_test_status_update(test, "Stale cache hit retrieved different object than fresh cache hit\n");
2436                         goto cleanup;
2437                 }
2438
2439                 if (wait_for_cache_update(sorcery, cache_stale, &cache_new)) {
2440                         ast_test_status_update(test, "Cache was not updated\n");
2441                         goto cleanup;
2442                 }
2443
2444                 if (iterations[i].exists) {
2445                         if (!cache_new) {
2446                                 ast_test_status_update(test, "Failed to retrieve item from cache when there should be one present\n");
2447                                 goto cleanup;
2448                         } else if (cache_new->salt != iterations[i].salt ||
2449                                         cache_new->pepper != iterations[i].pepper) {
2450                                 ast_test_status_update(test, "New cached item has unexpected values\n");
2451                                 goto cleanup;
2452                         }
2453                 } else if (cache_new) {
2454                         ast_test_status_update(test, "Retrieved a cached item when there should not have been one present\n");
2455                         goto cleanup;
2456                 }
2457         }
2458
2459         res = AST_TEST_PASS;
2460
2461 cleanup:
2462         if (sorcery) {
2463                 ast_sorcery_unref(sorcery);
2464         }
2465         ast_sorcery_wizard_unregister(&mock_wizard);
2466         return res;
2467 }
2468
2469 #endif
2470
2471 static int unload_module(void)
2472 {
2473         if (sched) {
2474                 ast_sched_context_destroy(sched);
2475                 sched = NULL;
2476         }
2477
2478         ao2_cleanup(caches);
2479
2480         ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
2481
2482         ast_cli_unregister_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
2483
2484         ast_manager_unregister("SorceryMemoryCacheExpireObject");
2485         ast_manager_unregister("SorceryMemoryCacheExpire");
2486         ast_manager_unregister("SorceryMemoryCacheStaleObject");
2487         ast_manager_unregister("SorceryMemoryCacheStale");
2488
2489         AST_TEST_UNREGISTER(open_with_valid_options);
2490         AST_TEST_UNREGISTER(open_with_invalid_options);
2491         AST_TEST_UNREGISTER(create_and_retrieve);
2492         AST_TEST_UNREGISTER(update);
2493         AST_TEST_UNREGISTER(delete);
2494         AST_TEST_UNREGISTER(maximum_objects);
2495         AST_TEST_UNREGISTER(expiration);
2496         AST_TEST_UNREGISTER(stale);
2497
2498         return 0;
2499 }
2500
2501 static int load_module(void)
2502 {
2503         int res;
2504
2505         sched = ast_sched_context_create();
2506         if (!sched) {
2507                 ast_log(LOG_ERROR, "Failed to create scheduler for cache management\n");
2508                 unload_module();
2509                 return AST_MODULE_LOAD_DECLINE;
2510         }
2511
2512         if (ast_sched_start_thread(sched)) {
2513                 ast_log(LOG_ERROR, "Failed to create scheduler thread for cache management\n");
2514                 unload_module();
2515                 return AST_MODULE_LOAD_DECLINE;
2516         }
2517
2518         caches = ao2_container_alloc(CACHES_CONTAINER_BUCKET_SIZE, sorcery_memory_cache_hash,
2519                 sorcery_memory_cache_cmp);
2520         if (!caches) {
2521                 ast_log(LOG_ERROR, "Failed to create container for configured caches\n");
2522                 unload_module();
2523                 return AST_MODULE_LOAD_DECLINE;
2524         }
2525
2526         if (ast_sorcery_wizard_register(&memory_cache_object_wizard)) {
2527                 unload_module();
2528                 return AST_MODULE_LOAD_DECLINE;
2529         }
2530
2531         res = ast_cli_register_multiple(cli_memory_cache, ARRAY_LEN(cli_memory_cache));
2532         res |= ast_manager_register_xml("SorceryMemoryCacheExpireObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire_object);
2533         res |= ast_manager_register_xml("SorceryMemoryCacheExpire", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_expire);
2534         res |= ast_manager_register_xml("SorceryMemoryCacheStaleObject", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale_object);
2535         res |= ast_manager_register_xml("SorceryMemoryCacheStale", EVENT_FLAG_SYSTEM, sorcery_memory_cache_ami_stale);
2536
2537         if (res) {
2538                 unload_module();
2539                 return AST_MODULE_LOAD_DECLINE;
2540         }
2541
2542         AST_TEST_REGISTER(open_with_valid_options);
2543         AST_TEST_REGISTER(open_with_invalid_options);
2544         AST_TEST_REGISTER(create_and_retrieve);
2545         AST_TEST_REGISTER(update);
2546         AST_TEST_REGISTER(delete);
2547         AST_TEST_REGISTER(maximum_objects);
2548         AST_TEST_REGISTER(expiration);
2549         AST_TEST_REGISTER(stale);
2550
2551         return AST_MODULE_LOAD_SUCCESS;
2552 }
2553
2554 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Sorcery Memory Cache Object Wizard",
2555         .support_level = AST_MODULE_SUPPORT_CORE,
2556         .load = load_module,
2557         .unload = unload_module,
2558         .load_pri = AST_MODPRI_REALTIME_DRIVER,
2559 );