summaryrefslogtreecommitdiff
path: root/pbx/pbx_realtime.c
diff options
context:
space:
mode:
authorTilghman Lesher <tilghman@meg.abyt.es>2010-05-27 19:25:16 +0000
committerTilghman Lesher <tilghman@meg.abyt.es>2010-05-27 19:25:16 +0000
commit05a1ec8abc32d8a5287b86701bd4c964e4fbe193 (patch)
tree457df0fae3d67cc69e88d8eb738c2b0913f9dc90 /pbx/pbx_realtime.c
parentfb80119b87efc5ca0e8ee439ef70149601f4f24a (diff)
Cache query results for one second.
Queries from the PBX core come in 3's. Caching avoids the additional performance penalty from those two additional queries hitting the database. (closes issue #16521) Reported by: tilghman Patches: 20091229__issue16521.diff.txt uploaded by tilghman (license 14) Tested by: Hubguru, tilghman git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@266238 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'pbx/pbx_realtime.c')
-rw-r--r--pbx/pbx_realtime.c126
1 files changed, 125 insertions, 1 deletions
diff --git a/pbx/pbx_realtime.c b/pbx/pbx_realtime.c
index 7db41571c..18ddfe7df 100644
--- a/pbx/pbx_realtime.c
+++ b/pbx/pbx_realtime.c
@@ -27,6 +27,8 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include <signal.h>
+
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
@@ -47,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/crypto.h"
#include "asterisk/astdb.h"
#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
#define MODE_MATCH 0
#define MODE_MATCHMORE 1
@@ -62,6 +65,80 @@ AST_APP_OPTIONS(switch_opts, {
AST_APP_OPTION('p', OPTION_PATTERNS_DISABLED),
});
+struct cache_entry {
+ struct timeval when;
+ struct ast_variable *var;
+ int priority;
+ char *context;
+ char exten[2];
+};
+
+struct ao2_container *cache;
+pthread_t cleanup_thread = 0;
+
+static int cache_hash(const void *obj, const int flags)
+{
+ const struct cache_entry *e = obj;
+ return ast_str_case_hash(e->exten) + e->priority;
+}
+
+static int cache_cmp(void *obj, void *arg, int flags)
+{
+ struct cache_entry *e = obj, *f = arg;
+ return e->priority != f->priority ? 0 :
+ strcmp(e->exten, f->exten) ? 0 :
+ strcmp(e->context, f->context) ? 0 :
+ CMP_MATCH;
+}
+
+static struct ast_variable *dup_vars(struct ast_variable *v)
+{
+ struct ast_variable *new, *list = NULL;
+ for (; v; v = v->next) {
+ if (!(new = ast_variable_new(v->name, v->value, v->file))) {
+ ast_variables_destroy(list);
+ return NULL;
+ }
+ /* Reversed list in cache, but when we duplicate out of the cache,
+ * it's back to correct order. */
+ new->next = list;
+ list = new;
+ }
+ return list;
+}
+
+static void free_entry(void *obj)
+{
+ struct cache_entry *e = obj;
+ ast_variables_destroy(e->var);
+}
+
+static int purge_old_fn(void *obj, void *arg, int flags)
+{
+ struct cache_entry *e = obj;
+ struct timeval *now = arg;
+ return ast_tvdiff_ms(*now, e->when) >= 1000 ? CMP_MATCH : 0;
+}
+
+static void *cleanup(void *unused)
+{
+ struct timespec forever = { 999999999, 0 }, one_second = { 1, 0 };
+ struct timeval now;
+
+ for (;;) {
+ pthread_testcancel();
+ if (ao2_container_count(cache) == 0) {
+ nanosleep(&forever, NULL);
+ }
+ pthread_testcancel();
+ now = ast_tvnow();
+ ao2_callback(cache, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, purge_old_fn, &now);
+ pthread_testcancel();
+ nanosleep(&one_second, NULL);
+ }
+}
+
+
/* Realtime switch looks up extensions in the supplied realtime table.
[context@][realtimetable][/options]
@@ -141,6 +218,11 @@ static struct ast_variable *realtime_common(const char *context, const char *ext
char *table;
struct ast_variable *var=NULL;
struct ast_flags flags = { 0, };
+ struct cache_entry *ce;
+ struct {
+ struct cache_entry ce;
+ char exten[AST_MAX_EXTENSION];
+ } cache_search = { { .priority = priority, .context = (char *) context }, };
char *buf = ast_strdupa(data);
if (buf) {
/* "Realtime" prefix is stripped off in the parent engine. The
@@ -158,7 +240,36 @@ static struct ast_variable *realtime_common(const char *context, const char *ext
if (!ast_strlen_zero(opts)) {
ast_app_parse_options(switch_opts, &flags, NULL, opts);
}
- var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
+ ast_copy_string(cache_search.exten, exten, sizeof(cache_search.exten));
+ if (mode == MODE_MATCH && (ce = ao2_find(cache, &cache_search, OBJ_POINTER))) {
+ var = dup_vars(ce->var);
+ ao2_ref(ce, -1);
+ } else {
+ var = realtime_switch_common(table, ctx, exten, priority, mode, flags);
+ do {
+ struct ast_variable *new;
+ /* Only cache matches */
+ if (mode != MODE_MATCH) {
+ break;
+ }
+ if (!(new = dup_vars(var))) {
+ break;
+ }
+ if (!(ce = ao2_alloc(sizeof(*ce) + strlen(exten) + strlen(context), free_entry))) {
+ ast_variables_destroy(new);
+ break;
+ }
+ ce->context = ce->exten + strlen(exten) + 1;
+ strcpy(ce->exten, exten); /* SAFE */
+ strcpy(ce->context, context); /* SAFE */
+ ce->priority = priority;
+ ce->var = new;
+ ce->when = ast_tvnow();
+ ao2_link(cache, ce);
+ pthread_kill(cleanup_thread, SIGURG);
+ ao2_ref(ce, -1);
+ } while (0);
+ }
}
return var;
}
@@ -283,11 +394,24 @@ static struct ast_switch realtime_switch =
static int unload_module(void)
{
ast_unregister_switch(&realtime_switch);
+ pthread_cancel(cleanup_thread);
+ pthread_kill(cleanup_thread, SIGURG);
+ pthread_join(cleanup_thread, NULL);
+ /* Destroy all remaining entries */
+ ao2_ref(cache, -1);
return 0;
}
static int load_module(void)
{
+ if (!(cache = ao2_container_alloc(573, cache_hash, cache_cmp))) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ if (ast_pthread_create(&cleanup_thread, NULL, cleanup, NULL)) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
if (ast_register_switch(&realtime_switch))
return AST_MODULE_LOAD_FAILURE;
return AST_MODULE_LOAD_SUCCESS;