From 3985dd57937870f3d47a4ddd68b477a42ffddec1 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 5 Feb 2016 04:29:17 +0000 Subject: Fixed #1902: - Crash when endpoint has multiple worker threads and SIP TCP transport is disconnected during incoming call handling. - Deprecated pjsip_dlg_create_uas(), replaced by pjsip_dlg_create_uas_and_inc_lock(). - Serialized transaction state notifications (of 'terminated' and 'destroyed') in case of transport error. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5241 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip-apps/src/samples/footprint.c | 2 +- pjsip-apps/src/samples/pjsip-perf.c | 8 ++- pjsip-apps/src/samples/simpleua.c | 25 ++++++-- pjsip-apps/src/samples/sipecho.c | 11 +++- pjsip-apps/src/samples/siprtp.c | 7 ++- pjsip/include/pjsip-ua/sip_replaces.h | 2 +- pjsip/include/pjsip/sip_dialog.h | 59 ++++++++++++++++- pjsip/src/pjsip/sip_dialog.c | 49 +++++++++++++-- pjsip/src/pjsip/sip_transaction.c | 112 ++++++++++++++++++--------------- pjsip/src/pjsip/sip_ua_layer.c | 2 +- pjsip/src/pjsua-lib/pjsua_call.c | 26 +++++++- pjsip/src/pjsua-lib/pjsua_core.c | 4 +- pjsip/src/pjsua-lib/pjsua_pres.c | 9 ++- pjsip/src/test/inv_offer_answer_test.c | 5 +- 14 files changed, 240 insertions(+), 81 deletions(-) diff --git a/pjsip-apps/src/samples/footprint.c b/pjsip-apps/src/samples/footprint.c index 9b666874..74e6be43 100644 --- a/pjsip-apps/src/samples/footprint.c +++ b/pjsip-apps/src/samples/footprint.c @@ -341,7 +341,7 @@ int dummy_function() pjsip_ua_init_module(NULL, NULL); pjsip_ua_destroy(); pjsip_dlg_create_uac(NULL, NULL, NULL, NULL, NULL, NULL); - pjsip_dlg_create_uas(NULL, NULL, NULL, NULL); + pjsip_dlg_create_uas_and_inc_lock(NULL, NULL, NULL, NULL); pjsip_dlg_terminate(NULL); pjsip_dlg_set_route_set(NULL, NULL); pjsip_dlg_create_request(NULL, NULL, -1, NULL); diff --git a/pjsip-apps/src/samples/pjsip-perf.c b/pjsip-apps/src/samples/pjsip-perf.c index 10bde943..bc963330 100644 --- a/pjsip-apps/src/samples/pjsip-perf.c +++ b/pjsip-apps/src/samples/pjsip-perf.c @@ -475,8 +475,8 @@ static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata) } /* Create UAS dialog */ - status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, - &app.local_contact, &dlg); + status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), rdata, + &app.local_contact, &dlg); if (status != PJ_SUCCESS) { const pj_str_t reason = pj_str("Unable to create dialog"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, @@ -502,9 +502,13 @@ static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata) if (status != PJ_SUCCESS) { pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata); pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); + pjsip_dlg_dec_lock(dlg); return PJ_TRUE; } + /* Invite session has been created, decrement & release dialog lock. */ + pjsip_dlg_dec_lock(dlg); + /* Send 100/Trying if needed */ if (app.server.send_trying) { status = send_response(call->inv, rdata, 100, &has_initial); diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c index b7f10250..a1bac754 100644 --- a/pjsip-apps/src/samples/simpleua.c +++ b/pjsip-apps/src/samples/simpleua.c @@ -725,10 +725,10 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) /* * Create UAS dialog. */ - status = pjsip_dlg_create_uas( pjsip_ua_instance(), - rdata, - &local_uri, /* contact */ - &dlg); + status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), + rdata, + &local_uri, /* contact */ + &dlg); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(g_endpt, rdata, 500, NULL, NULL, NULL); @@ -741,7 +741,11 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, MAX_MEDIA_CNT, g_sock_info, &local_sdp); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); + pj_assert(status == PJ_SUCCESS); + if (status != PJ_SUCCESS) { + pjsip_dlg_dec_lock(dlg); + return PJ_TRUE; + } /* @@ -749,7 +753,16 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) * capability to the session. */ status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &g_inv); - PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE); + pj_assert(status == PJ_SUCCESS); + if (status != PJ_SUCCESS) { + pjsip_dlg_dec_lock(dlg); + return PJ_TRUE; + } + + /* + * Invite session has been created, decrement & release dialog lock. + */ + pjsip_dlg_dec_lock(dlg); /* diff --git a/pjsip-apps/src/samples/sipecho.c b/pjsip-apps/src/samples/sipecho.c index 6c5244d8..b0fb4c2f 100644 --- a/pjsip-apps/src/samples/sipecho.c +++ b/pjsip-apps/src/samples/sipecho.c @@ -417,7 +417,7 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) pj_sockaddr hostaddr; char temp[80], hostip[PJ_INET6_ADDRSTRLEN]; pj_str_t local_uri; - pjsip_dialog *dlg; + pjsip_dialog *dlg = NULL; pjsip_rdata_sdp_info *sdp_info; pjmedia_sdp_session *answer = NULL; pjsip_tx_data *tdata = NULL; @@ -498,13 +498,18 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) pj_ansi_sprintf(temp, "", hostip, sip_port); local_uri = pj_str(temp); - status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, - &local_uri, &dlg); + status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), rdata, + &local_uri, &dlg); if (status == PJ_SUCCESS) answer = create_answer((int)(call-app.call), dlg->pool, sdp_info->sdp); + if (status == PJ_SUCCESS) status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &call->inv); + + if (dlg) + pjsip_dlg_dec_lock(dlg); + if (status == PJ_SUCCESS) status = pjsip_inv_initial_answer(call->inv, rdata, 100, NULL, NULL, &tdata); diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c index 262e2033..8e74c338 100644 --- a/pjsip-apps/src/samples/siprtp.c +++ b/pjsip-apps/src/samples/siprtp.c @@ -637,8 +637,8 @@ static void process_incoming_call(pjsip_rx_data *rdata) } /* Create UAS dialog */ - status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, - &app.local_contact, &dlg); + status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), rdata, + &app.local_contact, &dlg); if (status != PJ_SUCCESS) { const pj_str_t reason = pj_str("Unable to create dialog"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, @@ -655,9 +655,12 @@ static void process_incoming_call(pjsip_rx_data *rdata) if (status != PJ_SUCCESS) { pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata); pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata); + pjsip_dlg_dec_lock(dlg); return; } + /* Invite session has been created, decrement & release dialog lock */ + pjsip_dlg_dec_lock(dlg); /* Attach call data to invite session */ call->inv->mod_data[mod_siprtp.id] = call; diff --git a/pjsip/include/pjsip-ua/sip_replaces.h b/pjsip/include/pjsip-ua/sip_replaces.h index aff44498..4544b7ac 100644 --- a/pjsip/include/pjsip-ua/sip_replaces.h +++ b/pjsip/include/pjsip-ua/sip_replaces.h @@ -138,7 +138,7 @@ // Create UAS Invite session as usual. // - status = pjsip_dlg_create_uas(.., rdata, .., &dlg); + status = pjsip_dlg_create_uas_and_inc_lock(.., rdata, .., &dlg); .. status = pjsip_inv_create_uas(dlg, .., &inv); diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h index 9f37c32f..7f68476c 100644 --- a/pjsip/include/pjsip/sip_dialog.h +++ b/pjsip/include/pjsip/sip_dialog.h @@ -61,6 +61,15 @@ PJ_BEGIN_DECL +/* Deprecated API pjsip_dlg_create_uas() due to a fatal bug of possible + * premature dialog destroy. Application should not change this setting, + * unless it uses single worker thread. + * See also https://trac.pjsip.org/repos/ticket/1902. + */ +#ifndef DEPRECATED_FOR_TICKET_1902 +# define DEPRECATED_FOR_TICKET_1902 1 +#endif + /** * This structure is used to describe dialog's participants, which in this * case is local party (i.e. us) and remote party. @@ -240,6 +249,7 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua, pjsip_dialog **p_dlg); +#if !DEPRECATED_FOR_TICKET_1902 /** * Initialize UAS dialog from the information found in the incoming request * that creates a dialog (such as INVITE, REFER, or SUBSCRIBE), and set the @@ -279,6 +289,50 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, pjsip_rx_data *rdata, const pj_str_t *contact, pjsip_dialog **p_dlg); +#endif + + +/** + * Initialize UAS dialog from the information found in the incoming request + * that creates a dialog (such as INVITE, REFER, or SUBSCRIBE), and set the + * local Contact to contact. If contact is not specified, the local contact + * is initialized from the URI in the To header in the request. + * + * This function will also create UAS transaction for the incoming request, + * and associate the transaction to the rdata. Application can query the + * transaction used to handle this request by calling #pjsip_rdata_get_tsx() + * after this function returns. + * + * Note that initially, the session count in the dialog will be initialized + * to 1 (one), and the dialog is locked. Application needs to explicitly call + * #pjsip_dlg_dec_lock() to release the lock and decrease the session count. + * + * + * @param ua The user agent module instance. + * @param rdata The incoming request that creates the dialog, + * such as INVITE, SUBSCRIBE, or REFER. + * @param contact Optional dialog local Contact to be put as Contact + * header value, hence the format must follow + * RFC 3261 Section 20.10: + * When the header field value contains a display + * name, the URI including all URI parameters is + * enclosed in "<" and ">". If no "<" and ">" are + * present, all parameters after the URI are header + * parameters, not URI parameters. The display name + * can be tokens, or a quoted string, if a larger + * character set is desired. + * If this argument is NULL, the local contact will be + * initialized from the value of To header in the + * request. + * @param p_dlg Pointer to receive the dialog. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) +pjsip_dlg_create_uas_and_inc_lock( pjsip_user_agent *ua, + pjsip_rx_data *rdata, + const pj_str_t *contact, + pjsip_dialog **p_dlg); /** @@ -361,8 +415,9 @@ PJ_DECL(pj_status_t) pjsip_dlg_terminate( pjsip_dialog *dlg ); * for UAC dialog, before any request is sent. After dialog has been * established, the route set can not be changed. * - * For UAS dialog,the route set will be initialized in pjsip_dlg_create_uas() - * from the Record-Route headers in the incoming request. + * For UAS dialog, the route set will be initialized in + * pjsip_dlg_create_uas_and_inc_lock() from the Record-Route headers in + * the incoming request. * * The route_set argument is standard list of Route headers (i.e. with * sentinel). diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index d158bc3d..1964caa9 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -311,10 +311,11 @@ on_error: /* * Create UAS dialog. */ -PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, - pjsip_rx_data *rdata, - const pj_str_t *contact, - pjsip_dialog **p_dlg) +pj_status_t create_uas_dialog( pjsip_user_agent *ua, + pjsip_rx_data *rdata, + const pj_str_t *contact, + pj_bool_t inc_lock, + pjsip_dialog **p_dlg) { pj_status_t status; pjsip_hdr *pos = NULL; @@ -510,6 +511,12 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, if (status != PJ_SUCCESS) goto on_error; + /* Increment the dialog's lock since tsx may cause the dialog to be + * destroyed prematurely (such as in case of transport error). + */ + if (inc_lock) + pjsip_dlg_inc_lock(dlg); + /* Create UAS transaction for this request. */ status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); if (status != PJ_SUCCESS) @@ -552,11 +559,43 @@ on_error: --dlg->tsx_count; } - destroy_dialog(dlg, PJ_FALSE); + if (inc_lock) { + pjsip_dlg_dec_lock(dlg); + } else { + destroy_dialog(dlg, PJ_FALSE); + } + return status; } +#if !DEPRECATED_FOR_TICKET_1902 +/* + * Create UAS dialog. + */ +PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, + pjsip_rx_data *rdata, + const pj_str_t *contact, + pjsip_dialog **p_dlg) +{ + return create_uas_dialog(ua, rdata, contact, PJ_FALSE, p_dlg); +} +#endif + + +/* + * Create UAS dialog and increase its session count. + */ +PJ_DEF(pj_status_t) +pjsip_dlg_create_uas_and_inc_lock( pjsip_user_agent *ua, + pjsip_rx_data *rdata, + const pj_str_t *contact, + pjsip_dialog **p_dlg) +{ + return create_uas_dialog(ua, rdata, contact, PJ_TRUE, p_dlg); +} + + /* * Bind dialog to a specific transport/listener. */ diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index ccd3af61..46bd971c 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -138,6 +138,12 @@ static pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, #define TIMEOUT_TIMER 2 #define TRANSPORT_ERR_TIMER 3 +/* Flags for tsx_set_state() */ +enum +{ + NO_NOTIFY = 1, + NO_SCHEDULE_HANDLER = 2, +}; /* Prototypes. */ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, @@ -169,11 +175,8 @@ static void tsx_tp_state_callback( static void tsx_set_state( pjsip_transaction *tsx, pjsip_tsx_state_e state, pjsip_event_id_e event_src_type, - void *event_src ); -static void tsx_set_state_no_notify( pjsip_transaction *tsx, - pjsip_tsx_state_e state, - pjsip_event_id_e event_src_type, - void *event_src ); + void *event_src, + int flag); static void tsx_set_status_code(pjsip_transaction *tsx, int code, const pj_str_t *reason); static pj_status_t tsx_create( pjsip_module *tsx_user, @@ -1103,6 +1106,7 @@ static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) entry->id = 0; if (tsx->state < PJSIP_TSX_STATE_TERMINATED) { pjsip_tsx_state_e prev_state; + pj_time_val timeout = { 0, 0 }; pj_grp_lock_acquire(tsx->grp_lock); prev_state = tsx->state; @@ -1122,8 +1126,15 @@ static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) * otherwise we'll get a deadlock. See: * https://trac.pjsip.org/repos/ticket/1646 */ - tsx_set_state_no_notify( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, NULL); + /* Also don't schedule tsx handler, otherwise we'll get race + * condition of TU notifications due to delayed TERMINATED + * state TU notification. It happened in multiple worker threads + * environment between TERMINATED & DESTROYED! See: + * https://trac.pjsip.org/repos/ticket/1902 + */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, + PJSIP_EVENT_TRANSPORT_ERROR, NULL, + NO_NOTIFY | NO_SCHEDULE_HANDLER); pj_grp_lock_release(tsx->grp_lock); /* Now notify TU about state change, WITHOUT holding the @@ -1138,6 +1149,10 @@ static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) prev_state); (*tsx->tsx_user->on_tsx_state)(tsx, &e); } + + /* Now let's schedule the tsx handler */ + tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout, + TIMEOUT_TIMER); } } else { pjsip_event event; @@ -1167,7 +1182,8 @@ static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) static void tsx_set_state( pjsip_transaction *tsx, pjsip_tsx_state_e state, pjsip_event_id_e event_src_type, - void *event_src ) + void *event_src, + int flag) { pjsip_tsx_state_e prev_state = tsx->state; @@ -1192,7 +1208,9 @@ static void tsx_set_state( pjsip_transaction *tsx, /* Before informing TU about state changed, inform TU about * rx event. */ - if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user) { + if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user && + (flag & NO_NOTIFY)==0) + { pjsip_rx_data *rdata = (pjsip_rx_data*) event_src; pj_assert(rdata != NULL); @@ -1206,7 +1224,9 @@ static void tsx_set_state( pjsip_transaction *tsx, } /* Inform TU about state changed. */ - if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) { + if (tsx->tsx_user && tsx->tsx_user->on_tsx_state && + (flag & NO_NOTIFY) == 0) + { pjsip_event e; PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src, prev_state); @@ -1218,7 +1238,7 @@ static void tsx_set_state( pjsip_transaction *tsx, * saved last transmitted message. */ if (state == PJSIP_TSX_STATE_TERMINATED) { - pj_time_val timeout = {0, 0}; + pj_time_val timeout = { 0, 0 }; /* If we're still waiting for a message to be sent.. */ if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) { @@ -1236,7 +1256,10 @@ static void tsx_set_state( pjsip_transaction *tsx, lock_timer(tsx); tsx_cancel_timer(tsx, &tsx->timeout_timer); - tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout, TIMEOUT_TIMER); + if ((flag & NO_SCHEDULE_HANDLER) == 0) { + tsx_schedule_timer(tsx, &tsx->timeout_timer, &timeout, + TIMEOUT_TIMER); + } unlock_timer(tsx); } else if (state == PJSIP_TSX_STATE_DESTROYED) { @@ -1251,18 +1274,6 @@ static void tsx_set_state( pjsip_transaction *tsx, pj_log_pop_indent(); } -/* Set transaction state without notifying tsx_user */ -static void tsx_set_state_no_notify( pjsip_transaction *tsx, - pjsip_tsx_state_e state, - pjsip_event_id_e event_src_type, - void *event_src ) -{ - pjsip_module *tsx_user = tsx->tsx_user; - tsx->tsx_user = NULL; - tsx_set_state(tsx, state, event_src_type, event_src); - tsx->tsx_user = tsx_user; -} - /* * Create, initialize, and register UAC transaction. */ @@ -1626,7 +1637,8 @@ PJ_DEF(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ) if (tsx->state < PJSIP_TSX_STATE_TERMINATED) { tsx_set_status_code(tsx, code, NULL); - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL); + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, + NULL, 0); } pj_grp_lock_release(tsx->grp_lock); @@ -1837,7 +1849,7 @@ static void send_msg_callback( pjsip_send_state *send_state, /* Pending destroy? */ if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) { tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, - PJSIP_EVENT_UNKNOWN, NULL ); + PJSIP_EVENT_UNKNOWN, NULL, 0 ); pj_grp_lock_release(tsx->grp_lock); return; } @@ -1912,7 +1924,8 @@ static void send_msg_callback( pjsip_send_state *send_state, tsx->state != PJSIP_TSX_STATE_DESTROYED) { tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata); + PJSIP_EVENT_TRANSPORT_ERROR, + send_state->tdata, 0); } /* Don't forget to destroy if we have pending destroy flag * (http://trac.pjsip.org/repos/ticket/906) @@ -1920,7 +1933,8 @@ static void send_msg_callback( pjsip_send_state *send_state, else if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) { tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED, - PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata); + PJSIP_EVENT_TRANSPORT_ERROR, + send_state->tdata, 0); } } else { @@ -2122,7 +2136,7 @@ static pj_status_t tsx_send_msg( pjsip_transaction *tsx, tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, NULL ); + PJSIP_EVENT_TRANSPORT_ERROR, NULL, 0 ); return status; } @@ -2354,7 +2368,7 @@ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, event->body.rx_msg.rdata->msg_info.msg->type == PJSIP_REQUEST_MSG); tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG, - event->body.rx_msg.rdata); + event->body.rx_msg.rdata, 0); } else { pjsip_tx_data *tdata; @@ -2409,7 +2423,7 @@ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx, /* Move state. */ tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, - PJSIP_EVENT_TX_MSG, tdata); + PJSIP_EVENT_TX_MSG, tdata, 0); } return PJ_SUCCESS; @@ -2450,7 +2464,7 @@ static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx, /* Inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer); + PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0); /* Transaction is destroyed */ //return PJSIP_ETSXDESTROYED; @@ -2569,7 +2583,7 @@ static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx, if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) { tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, - PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata); + PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata, 0); } @@ -2650,7 +2664,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, } tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, - PJSIP_EVENT_TX_MSG, tdata ); + PJSIP_EVENT_TX_MSG, tdata, 0 ); /* Retransmit provisional response every 1 minute if this is * an INVITE provisional response greater than 100. @@ -2684,7 +2698,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, * is handled by TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TX_MSG, tdata ); + PJSIP_EVENT_TX_MSG, tdata, 0 ); /* Transaction is destroyed. */ //return PJSIP_ETSXDESTROYED; @@ -2742,7 +2756,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, /* Set state to "Completed" */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_TX_MSG, tdata ); + PJSIP_EVENT_TX_MSG, tdata, 0 ); } } else if (tsx->status_code >= 300) { @@ -2801,7 +2815,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, /* Inform TU */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_TX_MSG, tdata ); + PJSIP_EVENT_TX_MSG, tdata, 0 ); } else { pj_assert(0); @@ -2835,7 +2849,7 @@ static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx, tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer); + PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0); return PJ_EBUG; @@ -2900,7 +2914,7 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, /* Inform the message to TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { @@ -2914,7 +2928,7 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, */ if (tsx->method.id == PJSIP_INVITE_METHOD) { tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); //return PJSIP_ETSXDESTROYED; } else { @@ -2941,7 +2955,7 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, /* Move state to Completed, inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); } } else if (event->type == PJSIP_EVENT_TIMER && @@ -2949,7 +2963,7 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, /* Inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer); + PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0); } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { @@ -3040,7 +3054,7 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, /* Inform TU. */ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata); + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0); /* Generate and send ACK for INVITE. */ if (tsx->method.id == PJSIP_INVITE_METHOD) { @@ -3151,7 +3165,7 @@ static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx, /* Move state to "Confirmed" */ tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata, 0 ); } } else if (event->type == PJSIP_EVENT_TIMER) { @@ -3175,14 +3189,14 @@ static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx, tsx_set_status_code(tsx, PJSIP_SC_TSX_TIMEOUT, NULL); tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer ); + PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 ); //return PJSIP_ETSXDESTROYED; } else { /* Transaction terminated, it can now be deleted. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer ); + PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 ); //return PJSIP_ETSXDESTROYED; } } @@ -3215,7 +3229,7 @@ static pj_status_t tsx_on_state_completed_uac( pjsip_transaction *tsx, /* Move to Terminated state. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, event->body.timer.entry ); + PJSIP_EVENT_TIMER, event->body.timer.entry, 0 ); /* Transaction has been destroyed. */ //return PJSIP_ETSXDESTROYED; @@ -3290,7 +3304,7 @@ static pj_status_t tsx_on_state_confirmed( pjsip_transaction *tsx, /* Move to Terminated state. */ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer ); + PJSIP_EVENT_TIMER, &tsx->timeout_timer, 0 ); /* Transaction has been destroyed. */ //return PJSIP_ETSXDESTROYED; @@ -3321,7 +3335,7 @@ static pj_status_t tsx_on_state_terminated( pjsip_transaction *tsx, /* Destroy this transaction */ tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, - event->type, event->body.user.user1 ); + event->type, event->body.user.user1, 0 ); return PJ_SUCCESS; } diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c index 021005a4..716d25a2 100644 --- a/pjsip/src/pjsip/sip_ua_layer.c +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -277,7 +277,7 @@ static struct dlg_set *alloc_dlgset_node(void) /* * Register new dialog. Called by pjsip_dlg_create_uac() and - * pjsip_dlg_create_uas(); + * pjsip_dlg_create_uas_and_inc_lock(); */ PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, pjsip_dialog *dlg ) diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 86306ffd..bb549654 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -982,10 +982,19 @@ on_incoming_call_med_tp_complete(pjsua_call_id call_id, pjmedia_sdp_session *answer; pjsip_tx_data *response = NULL; unsigned options = 0; + pjsip_dialog *dlg = call->async_call.dlg; int sip_err_code = (info? info->sip_err_code: 0); pj_status_t status = (info? info->status: PJ_SUCCESS); PJSUA_LOCK(); + + /* Increment the dialog's lock to prevent it to be destroyed prematurely, + * such as in case of transport error. + */ + pjsip_dlg_inc_lock(dlg); + + /* Decrement dialog session. */ + pjsip_dlg_dec_session(dlg, &pjsua_var.mod); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error initializing media channel", status); @@ -996,6 +1005,7 @@ on_incoming_call_med_tp_complete(pjsua_call_id call_id, if (call->async_call.med_ch_deinit) { pjsua_media_channel_deinit(call->index); call->med_ch_cb = NULL; + pjsip_dlg_dec_lock(dlg); PJSUA_UNLOCK(); return PJ_SUCCESS; } @@ -1067,7 +1077,9 @@ on_return: process_pending_call_answer(call); } } - + + pjsip_dlg_dec_lock(dlg); + PJSUA_UNLOCK(); return status; } @@ -1351,8 +1363,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) } /* Create dialog: */ - status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, - &contact, &dlg); + status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), rdata, + &contact, &dlg); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); @@ -1460,6 +1472,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) call->async_call.dlg = dlg; pj_list_init(&call->async_call.call_var.inc_call.answers); + pjsip_dlg_inc_session(dlg, &pjsua_var.mod); + /* Init media channel, only when there is offer or call replace request. * For incoming call without SDP offer, media channel init will be done * in pjsua_call_answer(), see ticket #1526. @@ -1501,6 +1515,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) } pjsip_dlg_dec_lock(dlg); + pjsip_dlg_dec_session(dlg, &pjsua_var.mod); + call->inv = NULL; call->async_call.dlg = NULL; goto on_return; @@ -1618,6 +1634,10 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) /* This INVITE request has been handled. */ on_return: + if (dlg) { + pjsip_dlg_dec_lock(dlg); + } + pj_log_pop_indent(); PJSUA_UNLOCK(); return PJ_TRUE; diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index c3e919df..41254c4c 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -1105,7 +1105,9 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread); for (ii=0; ii