[asterisk-commits] russell: trunk r119157 - in /trunk: ./ main/autoservice.c
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu May 29 17:28:51 CDT 2008
Author: russell
Date: Thu May 29 17:28:50 2008
New Revision: 119157
URL: http://svn.digium.com/view/asterisk?view=rev&rev=119157
Log:
Merged revisions 119156 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r119156 | russell | 2008-05-29 17:24:29 -0500 (Thu, 29 May 2008) | 10 lines
Fix a race condition in channel autoservice. There was still a small window of opportunity
for a DTMF frame, or some other deferred frame type, to come in and get dropped.
(closes issue #12656)
(closes issue #12656)
Reported by: dimas
Patches:
v3-12656.patch uploaded by dimas (license 88)
-- with some modifications by me
........
Modified:
trunk/ (props changed)
trunk/main/autoservice.c
Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.
Modified: trunk/main/autoservice.c
URL: http://svn.digium.com/view/asterisk/trunk/main/autoservice.c?view=diff&rev=119157&r1=119156&r2=119157
==============================================================================
--- trunk/main/autoservice.c (original)
+++ trunk/main/autoservice.c Thu May 29 17:28:50 2008
@@ -56,7 +56,7 @@
* it gets stopped for the last time. */
unsigned int use_count;
unsigned int orig_end_dtmf_flag:1;
- AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
AST_LIST_ENTRY(asent) list;
};
@@ -66,28 +66,17 @@
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;
- 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], *chan;
+ 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;
+ struct ast_frame *f = NULL;
+ struct ast_frame *defer_frame = NULL;
AST_LIST_LOCK(&aslist);
@@ -95,42 +84,48 @@
* to get used again. */
as_chan_list_state++;
- if (AST_LIST_EMPTY(&aslist))
+ if (AST_LIST_EMPTY(&aslist)) {
ast_cond_wait(&as_cond, &aslist.lock);
+ }
AST_LIST_TRAVERSE(&aslist, as, list) {
if (!ast_check_hangup(as->chan)) {
- 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");
+ }
}
}
AST_LIST_UNLOCK(&aslist);
- if ((chan = ast_waitfor_n(mons, x, &ms))) {
- struct ast_frame *f = ast_read(chan);
+ chan = ast_waitfor_n(mons, x, &ms);
+ if (!chan) {
+ continue;
+ }
+
+ f = ast_read(chan);
- if (!f) {
- struct ast_frame hangup_frame = { 0, };
- /* No frame means the channel has been hung up.
- * A hangup frame needs to be queued here as ast_waitfor() may
- * never return again for the condition to be detected outside
- * of autoservice. So, we'll leave a HANGUP queued up so the
- * thread in charge of this channel will know. */
-
- hangup_frame.frametype = AST_FRAME_CONTROL;
- hangup_frame.subclass = AST_CONTROL_HANGUP;
-
- defer_frame(chan, &hangup_frame);
-
- continue;
- }
-
+ if (!f) {
+ struct ast_frame hangup_frame = { 0, };
+ /* No frame means the channel has been hung up.
+ * A hangup frame needs to be queued here as ast_waitfor() may
+ * never return again for the condition to be detected outside
+ * of autoservice. So, we'll leave a HANGUP queued up so the
+ * thread in charge of this channel will know. */
+
+ hangup_frame.frametype = AST_FRAME_CONTROL;
+ hangup_frame.subclass = AST_CONTROL_HANGUP;
+
+ defer_frame = &hangup_frame;
+ } 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:
@@ -138,7 +133,7 @@
case AST_FRAME_TEXT:
case AST_FRAME_IMAGE:
case AST_FRAME_HTML:
- defer_frame(chan, f);
+ defer_frame = f;
break;
/* Throw these frames away */
@@ -151,9 +146,31 @@
case AST_FRAME_MODEM:
break;
}
-
- if (f)
+ }
+
+ if (!defer_frame) {
+ if (f) {
ast_frfree(f);
+ }
+ continue;
+ }
+
+ for (i = 0; i < x; i++) {
+ struct ast_frame *dup_f;
+
+ if (mons[i] != chan) {
+ continue;
+ }
+
+ if ((dup_f = ast_frdup(f))) {
+ AST_LIST_INSERT_TAIL(&ents[i]->deferred_frames, dup_f, frame_list);
+ }
+
+ break;
+ }
+
+ if (f) {
+ ast_frfree(f);
}
}
@@ -224,14 +241,9 @@
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 = NULL;
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);
@@ -241,41 +253,52 @@
* it after its gone! */
chan_list_state = as_chan_list_state;
+ /* Find the entry, but do not free it because it still can be in the
+ autoservice thread array */
AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
if (as->chan == chan) {
- AST_LIST_REMOVE_CURRENT(list);
as->use_count--;
- if (as->use_count)
- break;
- AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list);
- orig_end_dtmf_flag = as->orig_end_dtmf_flag;
- ast_free(as);
- removed = 1;
- if (!ast_check_hangup(chan))
- res = 0;
+ if (as->use_count < 1) {
+ AST_LIST_REMOVE_CURRENT(list);
+ removed = as;
+ }
break;
}
}
AST_LIST_TRAVERSE_SAFE_END;
- if (removed && asthread != AST_PTHREADT_NULL)
+ if (removed && asthread != AST_PTHREADT_NULL) {
pthread_kill(asthread, SIGURG);
-
+ }
+
AST_LIST_UNLOCK(&aslist);
- if (!removed)
+ 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->deferred_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;
}
More information about the asterisk-commits
mailing list