aboutsummaryrefslogtreecommitdiffstats
path: root/channels/chan_local.c
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2008-05-13 21:18:55 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2008-05-13 21:18:55 +0000
commit4004787b0236caa8620270393c3d1cf5a32c58b3 (patch)
tree4cd617e4b500ebaec094d887ef209ac75376d189 /channels/chan_local.c
parent8da7a7e7fdb5d03f8109fc0e7075e2db2ff42e4e (diff)
Merged revisions 116038 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r116038 | russell | 2008-05-13 16:17:23 -0500 (Tue, 13 May 2008) | 24 lines Fix a deadlock involving channel autoservice and chan_local that was debugged and fixed by mmichelson and me. We observed a system that had a bunch of threads stuck in ast_autoservice_stop(). The reason these threads were waiting around is because this function waits to ensure that the channel list in the autoservice thread gets rebuilt before the stop() function returns. However, the autoservice thread was also locked, so the autoservice channel list was never getting rebuilt. The autoservice thread was stuck waiting for the channel lock on a local channel. However, the local channel was locked by a thread that was stuck in the autoservice stop function. It turned out that the issue came down to the local_queue_frame() function in chan_local. This function assumed that one of the channels passed in as an argument was locked when called. However, that was not always the case. There were multiple cases in which this channel was not locked when the function was called. We fixed up chan_local to indicate to this function whether this channel was locked or not. The previous assumption had caused local_queue_frame() to improperly return with the channel locked, where it would then never get unlocked. (closes issue #12584) (related to issue #12603) ........ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@116039 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_local.c')
-rw-r--r--channels/chan_local.c25
1 files changed, 14 insertions, 11 deletions
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 176bc7356..5ab27041d 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -195,7 +195,8 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
return bridged;
}
-static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us)
+static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f,
+ struct ast_channel *us, int us_locked)
{
struct ast_channel *other = NULL;
@@ -219,11 +220,13 @@ static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_fra
/* Ensure that we have both channels locked */
while (other && ast_channel_trylock(other)) {
ast_mutex_unlock(&p->lock);
- if (us)
+ if (us && us_locked) {
ast_channel_unlock(us);
+ }
usleep(1);
- if (us)
+ if (us && us_locked) {
ast_channel_lock(us);
+ }
ast_mutex_lock(&p->lock);
other = isoutbound ? p->owner : p->chan;
}
@@ -252,7 +255,7 @@ static int local_answer(struct ast_channel *ast)
if (isoutbound) {
/* Pass along answer since somebody answered us */
struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
- res = local_queue_frame(p, isoutbound, &answer, ast);
+ res = local_queue_frame(p, isoutbound, &answer, ast, 1);
} else
ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
if (!res)
@@ -341,7 +344,7 @@ static int local_write(struct ast_channel *ast, struct ast_frame *f)
if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
check_bridge(p, isoutbound);
if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
- res = local_queue_frame(p, isoutbound, f, ast);
+ res = local_queue_frame(p, isoutbound, f, ast, 1);
else {
ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
res = 0;
@@ -395,7 +398,7 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
f.subclass = condition;
f.data = (void*)data;
f.datalen = datalen;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1)))
ast_mutex_unlock(&p->lock);
}
@@ -415,7 +418,7 @@ static int local_digit_begin(struct ast_channel *ast, char digit)
ast_mutex_lock(&p->lock);
isoutbound = IS_OUTBOUND(ast, p);
f.subclass = digit;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
ast_mutex_unlock(&p->lock);
return res;
@@ -435,7 +438,7 @@ static int local_digit_end(struct ast_channel *ast, char digit, unsigned int dur
isoutbound = IS_OUTBOUND(ast, p);
f.subclass = digit;
f.len = duration;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
ast_mutex_unlock(&p->lock);
return res;
@@ -455,7 +458,7 @@ static int local_sendtext(struct ast_channel *ast, const char *text)
isoutbound = IS_OUTBOUND(ast, p);
f.data = (char *) text;
f.datalen = strlen(text) + 1;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
ast_mutex_unlock(&p->lock);
return res;
}
@@ -475,7 +478,7 @@ static int local_sendhtml(struct ast_channel *ast, int subclass, const char *dat
f.subclass = subclass;
f.data = (char *)data;
f.datalen = datalen;
- if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
+ if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
ast_mutex_unlock(&p->lock);
return res;
}
@@ -600,7 +603,7 @@ static int local_hangup(struct ast_channel *ast)
/* Need to actually hangup since there is no PBX */
ochan = p->chan;
else
- res = local_queue_frame(p, isoutbound, &f, NULL);
+ res = local_queue_frame(p, isoutbound, &f, NULL, 1);
if (!res)
ast_mutex_unlock(&p->lock);
if (ochan)