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