diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2004-11-07 21:46:09 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2004-11-07 21:46:09 +0000 |
commit | 1c77ca6ca2492b08f4902b9c0054ba1424b6412d (patch) | |
tree | ed40a8bfdc5a02ee77dad4eabe88576d9f65b08d /channels/chan_mgcp.c | |
parent | 74c68781722f76bfe79775d2617ff75d6d6e176f (diff) |
Major MGCP locking fixes (bug #2696)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@4174 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_mgcp.c')
-rwxr-xr-x | channels/chan_mgcp.c | 95 |
1 files changed, 79 insertions, 16 deletions
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 0b1fafd83..9b9917416 100755 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -573,6 +573,47 @@ static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p) } } +static void mgcp_queue_frame(struct mgcp_subchannel *sub, struct ast_frame *f) +{ + for(;;) { + if (sub->owner) { + if (!ast_mutex_trylock(&sub->owner->lock)) { + ast_queue_frame(sub->owner, f); + ast_mutex_unlock(&sub->owner->lock); + } else { + ast_mutex_unlock(&sub->lock); + usleep(1); + ast_mutex_lock(&sub->lock); + } + } else + break; + } +} + +static void mgcp_queue_hangup(struct mgcp_subchannel *sub) +{ + for(;;) { + if (sub->owner) { + if (!ast_mutex_trylock(&sub->owner->lock)) { + ast_queue_hangup(sub->owner); + ast_mutex_unlock(&sub->owner->lock); + } else { + ast_mutex_unlock(&sub->lock); + usleep(1); + ast_mutex_lock(&sub->lock); + } + } else + break; + } +} + +static void mgcp_queue_control(struct mgcp_subchannel *sub, int control) +{ + struct ast_frame f = { AST_FRAME_CONTROL, }; + f.subclass = control; + return mgcp_queue_frame(sub, &f); +} + static int retrans_pkt(void *data) { struct mgcp_gateway *gw = (struct mgcp_gateway *)data; @@ -811,7 +852,7 @@ static int mgcp_call(struct ast_channel *ast, char *dest, int timeout) } sub = ast->pvt->pvt; p = sub->parent; - + ast_mutex_lock(&sub->lock); switch (p->hookstate) { case MGCP_OFFHOOK: tone = "L/wt"; @@ -824,6 +865,7 @@ static int mgcp_call(struct ast_channel *ast, char *dest, int timeout) if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "mgcp_call called on %s, neither down nor reserved\n", ast->name); + ast_mutex_unlock(&sub->lock); return -1; } @@ -845,7 +887,7 @@ static int mgcp_call(struct ast_channel *ast, char *dest, int timeout) transmit_notify_request_with_callerid(sub, tone, ast->cid.cid_num, ast->cid.cid_name); ast_setstate(ast, AST_STATE_RINGING); - ast_queue_control(ast, AST_CONTROL_RINGING); + mgcp_queue_control(sub, AST_CONTROL_RINGING); if (sub->next->owner && strlen(sub->next->cxident) && strlen(sub->next->callid)) { /* Put the connection back in sendrecv */ @@ -857,6 +899,7 @@ static int mgcp_call(struct ast_channel *ast, char *dest, int timeout) ast_log(LOG_NOTICE, "Don't know how to dial on trunks yet\n"); res = -1; } + ast_mutex_unlock(&sub->lock); return res; } @@ -1057,6 +1100,7 @@ static int mgcp_answer(struct ast_channel *ast) int res = 0; struct mgcp_subchannel *sub = ast->pvt->pvt; struct mgcp_endpoint *p = sub->parent; + ast_mutex_lock(&sub->lock); sub->cxmode = MGCP_CX_SENDRECV; if (!sub->rtp) { start_rtp(sub); @@ -1074,6 +1118,7 @@ static int mgcp_answer(struct ast_channel *ast) transmit_notify_request(sub, ""); transmit_modify_request(sub); } + ast_mutex_unlock(&sub->lock); return res; } @@ -1151,12 +1196,15 @@ static int mgcp_write(struct ast_channel *ast, struct ast_frame *frame) static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct mgcp_subchannel *sub = newchan->pvt->pvt; + ast_mutex_lock(&sub->lock); ast_log(LOG_NOTICE, "mgcp_fixup(%s, %s)\n", oldchan->name, newchan->name); if (sub->owner != oldchan) { + ast_mutex_unlock(&sub->lock); ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner); return -1; } sub->owner = newchan; + ast_mutex_unlock(&sub->lock); return 0; } @@ -1168,7 +1216,9 @@ static int mgcp_senddigit(struct ast_channel *ast, char digit) tmp[1] = '/'; tmp[2] = digit; tmp[3] = '\0'; + ast_mutex_lock(&sub->lock); transmit_notify_request(sub, tmp); + ast_mutex_unlock(&sub->lock); return -1; } @@ -1208,9 +1258,11 @@ static char *control2str(int ind) { static int mgcp_indicate(struct ast_channel *ast, int ind) { struct mgcp_subchannel *sub = ast->pvt->pvt; + int res = 0; if (mgcpdebug) { ast_verbose(VERBOSE_PREFIX_3 "MGCP asked to indicate %d '%s' condition on channel %s\n", ind, control2str(ind), ast->name); } + ast_mutex_lock(&sub->lock); switch(ind) { case AST_CONTROL_RINGING: #ifdef DLINK_BUGGY_FIRMWARE @@ -1230,9 +1282,10 @@ static int mgcp_indicate(struct ast_channel *ast, int ind) break; default: ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind); - return -1; + res = -1; } - return 0; + ast_mutex_unlock(&sub->lock); + return res; } static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state) @@ -1400,7 +1453,7 @@ static char *get_csv(char *c, int *len, char **next) return s; } -static struct mgcp_subchannel *find_subchannel(char *name, int msgid, struct sockaddr_in *sin) +static struct mgcp_subchannel *find_subchannel_and_lock(char *name, int msgid, struct sockaddr_in *sin) { struct mgcp_endpoint *p = NULL; struct mgcp_subchannel *sub = NULL; @@ -1498,6 +1551,7 @@ static struct mgcp_subchannel *find_subchannel(char *name, int msgid, struct soc p = p->next; } if (sub && found) { + ast_mutex_lock(&sub->lock); break; } } @@ -2745,7 +2799,7 @@ static int attempt_transfer(struct mgcp_endpoint *p) p->sub->next->owner->_softhangup |= AST_SOFTHANGUP_DEV; if (p->sub->next->owner) { p->sub->next->alreadygone = 1; - ast_queue_hangup(p->sub->next->owner); + mgcp_queue_hangup(p->sub->next); } } return 0; @@ -2775,7 +2829,7 @@ static void handle_hd_hf(struct mgcp_subchannel *sub, char *ev) } /*transmit_notify_request(sub, "aw");*/ transmit_notify_request(sub, ""); - ast_queue_control(sub->owner, AST_CONTROL_ANSWER); + mgcp_queue_control(sub, AST_CONTROL_ANSWER); } } else { /* Start switch */ @@ -3030,21 +3084,25 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, if (p->transfer && (sub->owner && sub->next->owner) && ((!sub->outgoing) || (!sub->next->outgoing))) { /* We're allowed to transfer, we have two avtive calls and */ /* we made at least one of the calls. Let's try and transfer */ - if ((res = attempt_transfer(p)) < 0) { + ast_mutex_lock(&p->sub->next->lock); + res = attempt_transfer(p); + if (res < 0) { if (p->sub->next->owner) { sub->next->alreadygone = 1; - ast_queue_hangup(sub->next->owner); + mgcp_queue_hangup(sub->next); } } else if (res) { ast_log(LOG_WARNING, "Transfer attempt failed\n"); + ast_mutex_unlock(&p->sub->next->lock); return -1; } + ast_mutex_unlock(&p->sub->next->lock); } else { /* Hangup the current call */ /* If there is another active call, mgcp_hangup will ring the phone with the other call */ if (sub->owner) { sub->alreadygone = 1; - ast_queue_hangup(sub->owner); + mgcp_queue_hangup(sub); } else { /* SC: verbose level check */ if (option_verbose > 2) { @@ -3075,10 +3133,12 @@ static int handle_request(struct mgcp_subchannel *sub, struct mgcp_request *req, f.src = "mgcp"; if (sub->owner) { /* XXX MUST queue this frame to all subs in threeway call if threeway call is active */ - ast_queue_frame(sub->owner, &f); + mgcp_queue_frame(sub, &f); + ast_mutex_lock(&sub->next->lock); if (sub->next->owner) { - ast_queue_frame(sub->next->owner, &f); + mgcp_queue_frame(sub->next, &f); } + ast_mutex_unlock(&sub->next->lock); } if (strstr(p->curtone, "wt") && (ev[0] == 'A')) { memset(p->curtone, 0, sizeof(p->curtone)); @@ -3167,11 +3227,12 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore) if (sscanf(req.verb, "%d", &result) && sscanf(req.identifier, "%d", &ident)) { /* Try to find who this message is for, if it's important */ - sub = find_subchannel(NULL, ident, &sin); + sub = find_subchannel_and_lock(NULL, ident, &sin); if (sub) { struct mgcp_gateway *gw = sub->parent->parent; struct mgcp_message *cur, *prev; + ast_mutex_unlock(&sub->lock); ast_mutex_lock(&gw->msgs_lock); for (prev = NULL, cur = gw->msgs; cur; prev = cur, cur = cur->next) { if (cur->seqno == ident) { @@ -3191,7 +3252,6 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore) } ast_mutex_unlock(&gw->msgs_lock); - if (cur) { handle_response(cur->owner_ep, cur->owner_sub, result, ident, &req); free(cur); @@ -3209,12 +3269,13 @@ static int mgcpsock_read(int *id, int fd, short events, void *ignore) return 1; } /* Process request, with iflock held */ - sub = find_subchannel(req.endpoint, 0, &sin); + sub = find_subchannel_and_lock(req.endpoint, 0, &sin); if (sub) { /* look first to find a matching response in the queue */ if (!find_and_retrans(sub, &req)) /* pass the request off to the currently mastering subchannel */ handle_request(sub, &req, &sin); + ast_mutex_unlock(&sub->lock); } } return 1; @@ -3362,7 +3423,7 @@ static struct ast_channel *mgcp_request(const char *type, int format, void *data ast_log(LOG_NOTICE, "MGCP Channels require an endpoint\n"); return NULL; } - sub = find_subchannel(tmp, 0, NULL); + sub = find_subchannel_and_lock(tmp, 0, NULL); if (!sub) { ast_log(LOG_WARNING, "Unable to find MGCP endpoint '%s'\n", tmp); *cause = AST_CAUSE_UNREGISTERED; @@ -3386,9 +3447,11 @@ static struct ast_channel *mgcp_request(const char *type, int format, void *data } } *cause = AST_CAUSE_BUSY; + ast_mutex_unlock(&sub->lock); return NULL; } tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN); + ast_mutex_unlock(&sub->lock); if (!tmpc) ast_log(LOG_WARNING, "Unable to make channel for '%s'\n", tmp); restart_monitor(); |