diff --git a/main/autoservice.c b/main/autoservice.c index fcc42911f7366c1239ee46bf166c73453fd6a3ba..7fb86dad7be294521900de6016571a3511581dd7 100644 --- a/main/autoservice.c +++ b/main/autoservice.c @@ -60,6 +60,8 @@ static AST_RWLIST_HEAD_STATIC(aslist, asent); static pthread_t asthread = AST_PTHREADT_NULL; +static int as_chan_list_state; + static void defer_frame(struct ast_channel *chan, struct ast_frame *f) { struct ast_frame *dup_f; @@ -83,6 +85,11 @@ static void *autoservice_run(void *ign) int x = 0, ms = 500; AST_RWLIST_RDLOCK(&aslist); + + /* At this point, we know that no channels that have been removed are going + * to get used again. */ + as_chan_list_state++; + AST_RWLIST_TRAVERSE(&aslist, as, list) { if (!ast_check_hangup(as->chan)) { if (x < MAX_AUTOMONS) @@ -207,10 +214,18 @@ int ast_autoservice_stop(struct ast_channel *chan) struct ast_frame *f; int removed = 0; int orig_end_dtmf_flag = 0; + int chan_list_state; AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames); AST_RWLIST_WRLOCK(&aslist); + + /* Save the autoservice channel list state. We _must_ verify that the channel + * list has been rebuilt before we return. Because, after we return, the channel + * could get destroyed and we don't want our poor autoservice thread to step on + * it after its gone! */ + chan_list_state = as_chan_list_state; + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) { if (as->chan == chan) { AST_RWLIST_REMOVE_CURRENT(list); @@ -248,5 +263,8 @@ int ast_autoservice_stop(struct ast_channel *chan) ast_frfree(f); } + while (chan_list_state == as_chan_list_state) + usleep(1000); + return res; }