Index: pbx/pbx_realtime.c =================================================================== --- pbx/pbx_realtime.c (revision 236182) +++ pbx/pbx_realtime.c (working copy) @@ -33,6 +33,8 @@ #include #include +#include + #include "asterisk/file.h" #include "asterisk/logger.h" #include "asterisk/channel.h" @@ -54,6 +56,7 @@ #include "asterisk/utils.h" #include "asterisk/crypto.h" #include "asterisk/astdb.h" +#include "asterisk/astobj2.h" #define MODE_MATCH 0 #define MODE_MATCHMORE 1 @@ -62,6 +65,80 @@ #define EXT_DATA_SIZE 256 +struct cache_entry { + struct timeval when; + struct ast_variable *var; + int priority; + char *context; + char exten[0]; +}; + +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))) { + 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 (;;) { + 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); + pthread_testcancel(); + } +} + + /* Realtime switch looks up extensions in the supplied realtime table. [context@][realtimetable][/options] @@ -134,6 +211,11 @@ const char *ctx = NULL; char *table; struct ast_variable *var=NULL; + 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) { char *opts = strchr(buf, '/'); @@ -146,7 +228,36 @@ } ctx = S_OR(ctx, context); table = S_OR(table, "extensions"); - var = realtime_switch_common(table, ctx, exten, priority, mode); + 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); + 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) + 1 + strlen(context) + 1, free_entry))) { + ast_free(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; } @@ -246,11 +357,25 @@ static int unload_module(void) { ast_unregister_switch(&realtime_switch); + sched_yield(); + pthread_cancel(cleanup_thread); + pthread_kill(cleanup_thread, SIGURG); + pthread_join(cleanup_thread, NULL); + ao2_ref(cache, -1); + cache = NULL; 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; + } + ast_register_switch(&realtime_switch); return 0; } Index: main/cdr.c =================================================================== --- main/cdr.c (revision 236182) +++ main/cdr.c (working copy) @@ -63,10 +63,10 @@ char name[20]; char desc[80]; ast_cdrbe be; - AST_LIST_ENTRY(ast_cdr_beitem) list; + AST_RWLIST_ENTRY(ast_cdr_beitem) list; }; -static AST_LIST_HEAD_STATIC(be_list, ast_cdr_beitem); +static AST_RWLIST_HEAD_STATIC(be_list, ast_cdr_beitem); struct ast_cdr_batch_item { struct ast_cdr *cdr; @@ -117,12 +117,12 @@ return -1; } - AST_LIST_LOCK(&be_list); - AST_LIST_TRAVERSE(&be_list, i, list) { + AST_RWLIST_RDLOCK(&be_list); + AST_RWLIST_TRAVERSE(&be_list, i, list) { if (!strcasecmp(name, i->name)) break; } - AST_LIST_UNLOCK(&be_list); + AST_RWLIST_UNLOCK(&be_list); if (i) { ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name); @@ -136,9 +136,9 @@ ast_copy_string(i->name, name, sizeof(i->name)); ast_copy_string(i->desc, desc, sizeof(i->desc)); - AST_LIST_LOCK(&be_list); - AST_LIST_INSERT_HEAD(&be_list, i, list); - AST_LIST_UNLOCK(&be_list); + AST_RWLIST_WRLOCK(&be_list); + AST_RWLIST_INSERT_HEAD(&be_list, i, list); + AST_RWLIST_UNLOCK(&be_list); return 0; } @@ -148,18 +148,18 @@ { struct ast_cdr_beitem *i = NULL; - AST_LIST_LOCK(&be_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) { + AST_RWLIST_WRLOCK(&be_list); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) { if (!strcasecmp(name, i->name)) { - AST_LIST_REMOVE_CURRENT(&be_list, list); + AST_RWLIST_REMOVE_CURRENT(&be_list, list); if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name); free(i); break; } } - AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&be_list); + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&be_list); } int ast_cdr_isset_unanswered(void) @@ -1057,11 +1057,11 @@ ast_set_flag(cdr, AST_CDR_FLAG_POSTED); if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) continue; - AST_LIST_LOCK(&be_list); - AST_LIST_TRAVERSE(&be_list, i, list) { + AST_RWLIST_RDLOCK(&be_list); + AST_RWLIST_TRAVERSE(&be_list, i, list) { i->be(cdr); } - AST_LIST_UNLOCK(&be_list); + AST_RWLIST_UNLOCK(&be_list); } } @@ -1345,11 +1345,11 @@ ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : ""); ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : ""); } - AST_LIST_LOCK(&be_list); - AST_LIST_TRAVERSE(&be_list, beitem, list) { + AST_RWLIST_RDLOCK(&be_list); + AST_RWLIST_TRAVERSE(&be_list, beitem, list) { ast_cli(fd, "CDR registered backend: %s\n", beitem->name); } - AST_LIST_UNLOCK(&be_list); + AST_RWLIST_UNLOCK(&be_list); } return 0; Index: main/channel.c =================================================================== --- main/channel.c (revision 236182) +++ main/channel.c (working copy) @@ -86,6 +86,10 @@ AST_THREADSTORAGE(state2str_threadbuf, state2str_threadbuf_init); #define STATE2STR_BUFSIZE 32 +#ifdef DEBUG_THREADS +AST_THREADSTORAGE(last_exceptionally, last_exceptionally_init); +#endif + /*! Default amount of time to use when emulating a digit as a begin and end * 100ms */ #define AST_DEFAULT_EMULATE_DTMF_DURATION 100 @@ -951,7 +955,25 @@ } if ((queued_voice_frames + new_voice_frames) > 96) { +#ifdef DEBUG_THREADS + time_t *last_time = ast_threadstorage_get(&last_exceptionally, sizeof(time_t)); +#endif ast_log(LOG_WARNING, "Exceptionally long voice queue length queuing to %s\n", chan->name); +#ifdef DEBUG_THREADS + if (last_time && *last_time != time(NULL)) { + FILE *f = tmpfile(); + if (f) { + char buf[1024]; + ast_cli_command(fileno(f), "core show locks"); + lseek(fileno(f), 0, SEEK_SET); + while (fgets(buf, sizeof(buf), f)) { + ast_log(LOG_WARNING, "%s", buf); + } + *last_time = time(NULL); + fclose(f); + } + } +#endif while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list))) { ast_frfree(f); } Index: main/pbx.c =================================================================== --- main/pbx.c (revision 236182) +++ main/pbx.c (working copy) @@ -5487,13 +5487,21 @@ { char *args; struct ast_flags flags = { 0 }; + int autoservice = 0; if (!ast_strlen_zero(data)) { args = ast_strdupa(data); ast_app_parse_options(resetcdr_opts, &flags, NULL, args); } + if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED) && !ast_check_hangup(chan)) { + ast_autoservice_start(chan); + autoservice = 1; + } ast_cdr_reset(chan->cdr, &flags); + if (autoservice) { + ast_autoservice_stop(chan); + } return 0; }