diff options
author | Kevin P. Fleming <kpfleming@digium.com> | 2009-01-08 16:43:26 +0000 |
---|---|---|
committer | Kevin P. Fleming <kpfleming@digium.com> | 2009-01-08 16:43:26 +0000 |
commit | d5f97b4052d4d7cf1d8371d2f879ce46e44cb010 (patch) | |
tree | 7cb2b118511c1ea149ca2ff362c17fdef95ff037 /channels/chan_sip.c | |
parent | d92e53a6f5d36af8da46029a51482483ba0f7ef0 (diff) |
Merged revisions 167620 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r167620 | kpfleming | 2009-01-07 17:32:21 -0600 (Wed, 07 Jan 2009) | 5 lines
When a SIP request or response arrives for a dialog with an associated Asterisk channel, and the lock on that channel cannot be obtained because it is held by another thread, instead of dropping the request/response, queue it for later processing when the channel lock becomes available.
http://reviewboard.digium.com/r/123/
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@167700 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels/chan_sip.c')
-rw-r--r-- | channels/chan_sip.c | 142 |
1 files changed, 131 insertions, 11 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index f588543a5..96a2fd2f0 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1177,6 +1177,7 @@ struct sip_request { struct ast_str *data; /* XXX Do we need to unref socket.ser when the request goes away? */ struct sip_socket socket; /*!< The socket used for this request */ + AST_LIST_ENTRY(sip_request) next; }; /*! \brief structure used in transfers */ @@ -1656,6 +1657,8 @@ struct sip_pvt { struct sip_history_head *history; /*!< History of this SIP dialog */ size_t history_entries; /*!< Number of entires in the history */ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ + AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */ + int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */ struct sip_invite_param *options; /*!< Options for INVITE */ int autoframing; /*!< The number of Asters we group in a Pyroflax before strolling to the Grokyzpå @@ -2879,6 +2882,10 @@ static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdi if (dialog->autokillid > -1) AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr")); + if (dialog->request_queue_sched_id > -1) { + AST_SCHED_DEL_UNREF(sched, dialog->request_queue_sched_id, dialog_unref(dialog, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr")); + } + dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time"); return NULL; } @@ -4986,6 +4993,7 @@ static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi) /*! \brief Execute destruction of SIP dialog structure, release memory */ static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) { + struct sip_request *req; if (sip_debug_test_pvt(p)) ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text); @@ -5082,6 +5090,10 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist) p->history = NULL; } + while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) { + ast_free(req); + } + if (p->chanvars) { ast_variables_destroy(p->chanvars); p->chanvars = NULL; @@ -6519,6 +6531,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si p->initid = -1; p->waitid = -1; p->autokillid = -1; + p->request_queue_sched_id = -1; p->subscribed = NONE; p->stateid = -1; p->sessionversion_remote = -1; @@ -6625,6 +6638,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si ast_string_field_set(p, context, default_context); ast_string_field_set(p, parkinglot, default_parkinglot); + AST_LIST_HEAD_INIT_NOLOCK(&p->request_queue); /* Add to active dialog list */ @@ -20203,6 +20217,95 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so return res; } +static void process_request_queue(struct sip_pvt *p, int *recount, int *nounlock) +{ + struct sip_request *req; + + while ((req = AST_LIST_REMOVE_HEAD(&p->request_queue, next))) { + if (handle_incoming(p, req, &p->recv, recount, nounlock) == -1) { + /* Request failed */ + if (option_debug) { + ast_log(LOG_DEBUG, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>"); + } + } + ast_free(req); + } +} + +static int scheduler_process_request_queue(const void *data) +{ + struct sip_pvt *p = (struct sip_pvt *) data; + int recount = 0; + int nounlock = 0; + int lockretry; + + for (lockretry = 10; lockretry > 0; lockretry--) { + sip_pvt_lock(p); + + /* lock the owner if it has one -- we may need it */ + /* because this is deadlock-prone, we need to try and unlock if failed */ + if (!p->owner || !ast_channel_trylock(p->owner)) { + break; /* locking succeeded */ + } + + if (lockretry != 1) { + sip_pvt_unlock(p); + /* Sleep for a very short amount of time */ + usleep(1); + } + } + + if (!lockretry) { + int retry = !AST_LIST_EMPTY(&p->request_queue); + + /* we couldn't get the owner lock, which is needed to process + the queued requests, so return a non-zero value, which will + cause the scheduler to run this request again later if there + still requests to be processed + */ + sip_pvt_unlock(p); + if (!retry) { + dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it."); + } + return retry; + }; + + process_request_queue(p, &recount, &nounlock); + p->request_queue_sched_id = -1; + + if (p->owner && !nounlock) { + ast_channel_unlock(p->owner); + } + sip_pvt_unlock(p); + + if (recount) { + ast_update_use_count(); + } + + dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it."); + + return 0; +} + +static int queue_request(struct sip_pvt *p, const struct sip_request *req, const struct sockaddr_in *sin) +{ + struct sip_request *newreq; + + if (!(newreq = ast_calloc(1, sizeof(*newreq)))) { + return -1; + } + + copy_request(newreq, req); + AST_LIST_INSERT_TAIL(&p->request_queue, newreq, next); + if (p->request_queue_sched_id == -1) { + if ((p->request_queue_sched_id = ast_sched_add(sched, 10, scheduler_process_request_queue, dialog_ref(p, "Increment refcount to pass dialog pointer to sched callback"))) == -1) { + dialog_unref(p, "Decrement refcount due to sched_add failure"); + } + } + + return 0; +} + /*! \brief Read data from SIP UDP socket \note sipsock_read locks the owner channel while we are processing the SIP message \return 1 on error, 0 on success @@ -20275,11 +20378,10 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin) ntohs(sin->sin_port), req->data->str); } - if(parse_request(req) == -1) { /* Bad packet, can't parse */ + if (parse_request(req) == -1) { /* Bad packet, can't parse */ ast_str_reset(req->data); /* nulling this out is NOT a good idea here. */ return 1; } - req->method = find_sip_method(req->rlPart1); if (req->debug) @@ -20291,7 +20393,7 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin) } /* Process request, with netlock held, and with usual deadlock avoidance */ - for (lockretry = 100; lockretry > 0; lockretry--) { + for (lockretry = 10; lockretry > 0; lockretry--) { ast_mutex_lock(&netlock); /* Find the active SIP dialog or create a new one */ @@ -20308,13 +20410,14 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin) /* becaues this is deadlock-prone, we need to try and unlock if failed */ if (!p->owner || !ast_channel_trylock(p->owner)) break; /* locking succeeded */ - ast_debug(1, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid); - sip_pvt_unlock(p); - if (lockretry != 1) + + if (lockretry != 1) { + sip_pvt_unlock(p); ao2_t_ref(p, -1, "release p (from find_call) inside lockretry loop"); /* we'll look for it again, but p is dead now */ - ast_mutex_unlock(&netlock); - /* Sleep for a very short amount of time */ - usleep(1); + ast_mutex_unlock(&netlock); + /* Sleep for a very short amount of time */ + usleep(1); + } } p->recv = *sin; @@ -20322,18 +20425,35 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin) append_history(p, "Rx", "%s / %s / %s", req->data->str, get_header(req, "CSeq"), req->rlPart2); if (!lockretry) { + if (!queue_request(p, req, sin)) { + /* the request has been queued for later handling */ + sip_pvt_unlock(p); + ao2_t_ref(p, -1, "release p (from find_call) after queueing request"); + ast_mutex_unlock(&netlock); + return 1; + } + if (p->owner) - ast_log(LOG_ERROR, "We could NOT get the channel lock for %s! \n", S_OR(p->owner->name, "- no channel name ??? - ")); + ast_log(LOG_ERROR, "Channel lock for %s could not be obtained, and request was unable to be queued.\n", S_OR(p->owner->name, "- no channel name ??? - ")); ast_log(LOG_ERROR, "SIP transaction failed: %s \n", p->callid); if (req->method != SIP_ACK) transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */ /* XXX We could add retry-after to make sure they come back */ append_history(p, "LockFail", "Owner lock failed, transaction failed."); + sip_pvt_unlock(p); ao2_t_ref(p, -1, "release p (from find_call) at end of lockretry"); /* p is gone after the return */ + ast_mutex_unlock(&netlock); return 1; } - nounlock = 0; + /* if there are queued requests on this sip_pvt, process them first, so that everything is + handled in order + */ + if (!AST_LIST_EMPTY(&p->request_queue)) { + AST_SCHED_DEL_UNREF(sched, p->request_queue_sched_id, dialog_unref(p, "when you delete the request_queue_sched_id sched, you should dec the refcount for the stored dialog ptr")); + process_request_queue(p, &recount, &nounlock); + } + if (handle_incoming(p, req, sin, &recount, &nounlock) == -1) { /* Request failed */ ast_debug(1, "SIP message could not be handled, bad request: %-70.70s\n", p->callid[0] ? p->callid : "<no callid>"); |