Fix potential deadlock calling ast_call() with channel locks held.
authorRichard Mudgett <rmudgett@digium.com>
Thu, 10 Nov 2011 23:02:46 +0000 (23:02 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Thu, 10 Nov 2011 23:02:46 +0000 (23:02 +0000)
Fixed app_queue.c:ring_entry() calling ast_call() with the channel locks
held.  Chan_local attempts to do deadlock avoidance in its ast_call()
callback and could deadlock if a channel lock is already held.
........

Merged revisions 344539 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 344540 from http://svn.asterisk.org/svn/asterisk/branches/10

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@344541 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_queue.c

index 2b35f28..d4644a3 100644 (file)
@@ -3159,10 +3159,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
                return 0;
        }
 
-       ast_channel_lock(tmp->chan);
-       while (ast_channel_trylock(qe->chan)) {
-               CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan);
-       }
+       ast_channel_lock_both(tmp->chan, qe->chan);
 
        if (qe->cancel_answered_elsewhere) {
                ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
@@ -3225,13 +3222,14 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
                strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
        }
 
+       ast_channel_unlock(tmp->chan);
+       ast_channel_unlock(qe->chan);
+
        /* Place the call, but don't wait on the answer */
        if ((res = ast_call(tmp->chan, location, 0))) {
                /* Again, keep going even if there's an error */
                ast_debug(1, "ast call on peer returned %d\n", res);
                ast_verb(3, "Couldn't call %s\n", tmp->interface);
-               ast_channel_unlock(tmp->chan);
-               ast_channel_unlock(qe->chan);
                do_hang(tmp);
                (*busies)++;
                update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
@@ -3239,6 +3237,8 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
        } else if (qe->parent->eventwhencalled) {
                char vars[2048];
 
+               ast_channel_lock_both(tmp->chan, qe->chan);
+
                manager_event(EVENT_FLAG_AGENT, "AgentCalled",
                        "Queue: %s\r\n"
                        "AgentCalled: %s\r\n"
@@ -3261,10 +3261,12 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
                        S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
                        qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
                        qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
+
+               ast_channel_unlock(tmp->chan);
+               ast_channel_unlock(qe->chan);
+
                ast_verb(3, "Called %s\n", tmp->interface);
        }
-       ast_channel_unlock(tmp->chan);
-       ast_channel_unlock(qe->chan);
 
        update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
        return 1;
@@ -3698,10 +3700,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
                                        } else {
                                                struct ast_party_redirecting redirecting;
 
-                                               ast_channel_lock(o->chan);
-                                               while (ast_channel_trylock(in)) {
-                                                       CHANNEL_DEADLOCK_AVOIDANCE(o->chan);
-                                               }
+                                               ast_channel_lock_both(o->chan, in);
                                                ast_channel_inherit_variables(in, o->chan);
                                                ast_channel_datastore_inherit(in, o->chan);