diff options
author | Richard Mudgett <rmudgett@digium.com> | 2015-06-05 15:37:33 -0500 |
---|---|---|
committer | Richard Mudgett <rmudgett@digium.com> | 2015-07-06 12:52:36 -0500 |
commit | ada7346792452f021911063668997f79fdabc1f1 (patch) | |
tree | 1a7dd441d2f9f9bc33c176894470790a469179dd /res/res_pjsip | |
parent | cba550df7aea4fdac8a182215ddacb555824e088 (diff) |
res_pjsip: Need to use the same serializer for a pjproject SIP transaction.
All send/receive processing for a SIP transaction needs to be done under
the same threadpool serializer to prevent reentrancy problems inside
pjproject and res_pjsip.
* Add threadpool API call to get the current serializer associated with
the worker thread.
* Pick a serializer from a pool of default serializers if the caller of
res_pjsip.c:ast_sip_push_task() does not provide one.
This is a simple way to ensure that all outgoing SIP request messages are
processed under a serializer. Otherwise, any place where a pushed task is
done that would result in an outgoing out-of-dialog request would need to
be modified to supply a serializer. Serializers from the default
serializer pool are picked in a round robin sequence for simplicity.
A side effect is that the default serializer pool will limit the growth of
the thread pool from random tasks. This is not necessarily a bad thing.
* Made pjsip_distributor.c save the thread's serializer name on the
outgoing request tdata struct so the response can be processed under the
same serializer.
This is a cherry-pick from master.
**** ASTERISK-25115 Change-Id: Iea71c16ce1132017b5791635e198b8c27973f40a
NOTE: session_inv_on_state_changed() is disassociating the dialog from the
session when the invite dialog becomes PJSIP_INV_STATE_DISCONNECTED.
Unfortunately this is a tad too soon because our BYE request transaction
has not completed yet.
ASTERISK-25183 #close
Reported by: Matt Jordan
Change-Id: I8bad0ae1daf18d75b8c9e55874244b7962df2d0a
Diffstat (limited to 'res/res_pjsip')
-rw-r--r-- | res/res_pjsip/pjsip_distributor.c | 105 |
1 files changed, 98 insertions, 7 deletions
diff --git a/res/res_pjsip/pjsip_distributor.c b/res/res_pjsip/pjsip_distributor.c index e32f02833..9b052603a 100644 --- a/res/res_pjsip/pjsip_distributor.c +++ b/res/res_pjsip/pjsip_distributor.c @@ -22,22 +22,106 @@ #include "asterisk/res_pjsip.h" #include "include/res_pjsip_private.h" +#include "asterisk/taskprocessor.h" +#include "asterisk/threadpool.h" static int distribute(void *data); static pj_bool_t distributor(pjsip_rx_data *rdata); +static pj_status_t record_serializer(pjsip_tx_data *tdata); static pjsip_module distributor_mod = { .name = {"Request Distributor", 19}, .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 6, + .on_tx_request = record_serializer, .on_rx_request = distributor, .on_rx_response = distributor, }; +/*! + * \internal + * \brief Record the task's serializer name on the tdata structure. + * \since 14.0.0 + * + * \param tdata The outgoing message. + * + * \retval PJ_SUCCESS. + */ +static pj_status_t record_serializer(pjsip_tx_data *tdata) +{ + struct ast_taskprocessor *serializer; + + serializer = ast_threadpool_serializer_get_current(); + if (serializer) { + const char *name; + + name = ast_taskprocessor_name(serializer); + if (!ast_strlen_zero(name) + && (!tdata->mod_data[distributor_mod.id] + || strcmp(tdata->mod_data[distributor_mod.id], name))) { + char *tdata_name; + + /* The serializer in use changed. */ + tdata_name = pj_pool_alloc(tdata->pool, strlen(name) + 1); + strcpy(tdata_name, name);/* Safe */ + + tdata->mod_data[distributor_mod.id] = tdata_name; + } + } + + return PJ_SUCCESS; +} + +/*! + * \internal + * \brief Find the request tdata to get the serializer it used. + * \since 14.0.0 + * + * \param rdata The incoming message. + * + * \retval serializer on success. + * \retval NULL on error or could not find the serializer. + */ +static struct ast_taskprocessor *find_request_serializer(pjsip_rx_data *rdata) +{ + struct ast_taskprocessor *serializer = NULL; + pj_str_t tsx_key; + pjsip_transaction *tsx; + + pjsip_tsx_create_key(rdata->tp_info.pool, &tsx_key, PJSIP_ROLE_UAC, + &rdata->msg_info.cseq->method, rdata); + + tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); + if (!tsx) { + ast_debug(1, "Could not find %.*s transaction for %d response.\n", + (int) pj_strlen(&rdata->msg_info.cseq->method.name), + pj_strbuf(&rdata->msg_info.cseq->method.name), + rdata->msg_info.msg->line.status.code); + return NULL; + } + + if (tsx->last_tx) { + const char *serializer_name; + + serializer_name = tsx->last_tx->mod_data[distributor_mod.id]; + if (!ast_strlen_zero(serializer_name)) { + serializer = ast_taskprocessor_get(serializer_name, TPS_REF_IF_EXISTS); + } + } + +#ifdef HAVE_PJ_TRANSACTION_GRP_LOCK + pj_grp_lock_release(tsx->grp_lock); +#else + pj_mutex_unlock(tsx->mutex); +#endif + + return serializer; +} + /*! Dialog-specific information the distributor uses */ struct distributor_dialog_data { - /* Serializer to distribute tasks to for this dialog */ + /*! Serializer to distribute tasks to for this dialog */ struct ast_taskprocessor *serializer; - /* Endpoint associated with this dialog */ + /*! Endpoint associated with this dialog */ struct ast_sip_endpoint *endpoint; }; @@ -167,6 +251,7 @@ static pj_bool_t distributor(pjsip_rx_data *rdata) pjsip_dialog *dlg = find_dialog(rdata); struct distributor_dialog_data *dist = NULL; struct ast_taskprocessor *serializer = NULL; + struct ast_taskprocessor *req_serializer = NULL; pjsip_rx_data *clone; if (dlg) { @@ -176,11 +261,16 @@ static pj_bool_t distributor(pjsip_rx_data *rdata) } } - if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG && ( - !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) || - !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) && - !serializer) { - pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 481, NULL, NULL, NULL); + if (serializer) { + /* We have a serializer so we know where to send the message. */ + } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG) { + req_serializer = find_request_serializer(rdata); + serializer = req_serializer; + } else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method) + || !pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method)) { + /* We have a BYE or CANCEL request without a serializer. */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, NULL, NULL, NULL); goto end; } @@ -196,6 +286,7 @@ end: if (dlg) { pjsip_dlg_dec_lock(dlg); } + ast_taskprocessor_unreference(req_serializer); return PJ_TRUE; } |