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