Index: main/autoservice.c =================================================================== --- main/autoservice.c (revision 116589) +++ main/autoservice.c (working copy) @@ -72,28 +72,14 @@ static int as_chan_list_state; -static void defer_frame(struct ast_channel *chan, struct ast_frame *f) -{ - struct ast_frame *dup_f; - struct asent *as; - - AST_LIST_LOCK(&aslist); - AST_LIST_TRAVERSE(&aslist, as, list) { - if (as->chan != chan) - continue; - if ((dup_f = ast_frdup(f))) - AST_LIST_INSERT_TAIL(&as->dtmf_frames, dup_f, frame_list); - } - AST_LIST_UNLOCK(&aslist); -} - static void *autoservice_run(void *ign) { for (;;) { struct ast_channel *mons[MAX_AUTOMONS]; + struct asent *ents[MAX_AUTOMONS]; struct ast_channel *chan; struct asent *as; - int x = 0, ms = 50; + int i, x = 0, ms = 50; AST_LIST_LOCK(&aslist); @@ -106,9 +92,10 @@ AST_LIST_TRAVERSE(&aslist, as, list) { if (!as->chan->_softhangup) { - if (x < MAX_AUTOMONS) + if (x < MAX_AUTOMONS) { + ents[x] = as; mons[x++] = as->chan; - else + } else ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n"); } } @@ -118,6 +105,7 @@ chan = ast_waitfor_n(mons, x, &ms); if (chan) { struct ast_frame *f = ast_read(chan); + struct ast_frame *defer_frame = NULL; if (!f) { struct ast_frame hangup_frame = { 0, }; @@ -130,35 +118,49 @@ hangup_frame.frametype = AST_FRAME_CONTROL; hangup_frame.subclass = AST_CONTROL_HANGUP; - defer_frame(chan, &hangup_frame); + defer_frame = &hangup_frame; continue; - } + } else { - /* Do not add a default entry in this switch statement. Each new - * frame type should be addressed directly as to whether it should - * be queued up or not. */ - switch (f->frametype) { - /* Save these frames */ - case AST_FRAME_DTMF_END: - case AST_FRAME_CONTROL: - case AST_FRAME_TEXT: - case AST_FRAME_IMAGE: - case AST_FRAME_HTML: - defer_frame(chan, f); - break; + /* Do not add a default entry in this switch statement. Each new + * frame type should be addressed directly as to whether it should + * be queued up or not. */ + switch (f->frametype) { + /* Save these frames */ + case AST_FRAME_DTMF_END: + case AST_FRAME_CONTROL: + case AST_FRAME_TEXT: + case AST_FRAME_IMAGE: + case AST_FRAME_HTML: + defer_frame = f; + break; - /* Throw these frames away */ - case AST_FRAME_DTMF_BEGIN: - case AST_FRAME_VOICE: - case AST_FRAME_VIDEO: - case AST_FRAME_NULL: - case AST_FRAME_IAX: - case AST_FRAME_CNG: - case AST_FRAME_MODEM: - break; + /* Throw these frames away */ + case AST_FRAME_DTMF_BEGIN: + case AST_FRAME_VOICE: + case AST_FRAME_VIDEO: + case AST_FRAME_NULL: + case AST_FRAME_IAX: + case AST_FRAME_CNG: + case AST_FRAME_MODEM: + break; + } } + if (defer_frame) { + for (i = 0; i < x; i++) { + if (mons[i] == chan) { + struct ast_frame *dup_f; + if ((dup_f = ast_frdup(f))) { + AST_LIST_INSERT_TAIL(&ents[i]->dtmf_frames, dup_f, frame_list); + } + break; + } + } + + } + if (f) ast_frfree(f); } @@ -230,15 +232,10 @@ int ast_autoservice_stop(struct ast_channel *chan) { int res = -1; - struct asent *as; - AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames; + struct asent *as, *removed; 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_LIST_LOCK(&aslist); /* Save the autoservice channel list state. We _must_ verify that the channel @@ -247,18 +244,16 @@ * it after its gone! */ chan_list_state = as_chan_list_state; + removed = NULL; + /* Just find the enty but do not actually free it because it still can be in the + autoservice thread array */ AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) { if (as->chan == chan) { as->use_count--; - if (as->use_count) - break; - AST_LIST_REMOVE_CURRENT(&aslist, list); - AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list); - orig_end_dtmf_flag = as->orig_end_dtmf_flag; - free(as); - removed = 1; - if (!chan->_softhangup) - res = 0; + if (as->use_count < 1) { + AST_LIST_REMOVE_CURRENT(&aslist, list); + removed = as; + } break; } } @@ -272,16 +267,25 @@ if (!removed) return 0; - if (!orig_end_dtmf_flag) + /* Wait while autoservice thread rebuilds its list. */ + while (chan_list_state == as_chan_list_state) + usleep(1000); + + /* Now autoservice thread should have no references to our entry + and we can safely destroy it */ + + if (!chan->_softhangup) + res = 0; + + if (!as->orig_end_dtmf_flag) ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); - while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) { + while ((f = AST_LIST_REMOVE_HEAD(&as->dtmf_frames, frame_list))) { ast_queue_frame(chan, f); ast_frfree(f); } - while (chan_list_state == as_chan_list_state) - usleep(1000); + free(as); return res; }