From d43af04e9347aaf77450ef806a4cbbf520d6ca9f Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 19 Aug 2011 16:25:26 -0500 Subject: [PATCH] fix races in bypass media regarding channel signalling that may cause answer to be skipped --- src/include/switch_core.h | 2 +- src/include/switch_ivr.h | 2 ++ src/mod/endpoints/mod_sofia/sofia.c | 4 +--- src/switch_channel.c | 1 + src/switch_core_session.c | 12 ++++++++-- src/switch_core_state_machine.c | 14 ++---------- src/switch_ivr.c | 34 ++++++++++++++++++----------- src/switch_ivr_originate.c | 15 +++++++++++++ 8 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 61d89bfcd0..8d4a0cd122 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -687,7 +687,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_thread_launch(_In_ switch_co /*! \brief Signal a session's state machine thread that a state change has occured */ -SWITCH_DECLARE(void) switch_core_session_wake_session_thread(_In_ switch_core_session_t *session); +SWITCH_DECLARE(switch_status_t) switch_core_session_wake_session_thread(_In_ switch_core_session_t *session); SWITCH_DECLARE(void) switch_core_session_signal_state_change(_In_ switch_core_session_t *session); /*! diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 76ce542429..5ce8c882d2 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -111,6 +111,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_events(switch_core_session_ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_next_event(switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_messages(switch_core_session_t *session); SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_signal_data(switch_core_session_t *session); +SWITCH_DECLARE(switch_status_t) switch_ivr_process_indications(switch_core_session_t *session, switch_core_session_message_t *message); + /*! \brief Wait for time to pass for a specified number of milliseconds \param session the session to wait for. diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 6b119b62d1..2475a5cc0b 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1257,9 +1257,7 @@ void sofia_event_callback(nua_event_t event, if (!zstr(sofia_private->uuid)) { if ((session = switch_core_session_locate(sofia_private->uuid))) { - switch_channel_t *channel = switch_core_session_get_channel(session); - - if (switch_core_session_running(session) && !switch_channel_test_flag(channel, CF_PROXY_MODE)) { + if (switch_core_session_running(session)) { switch_core_session_queue_signal_data(session, de); } else { switch_core_session_message_t msg = { 0 }; diff --git a/src/switch_channel.c b/src/switch_channel.c index 0a6071105f..755f140513 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -2885,6 +2885,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_perform_mark_pre_answered(switch_ switch_channel_set_flag(channel, CF_PASSTHRU_PTIME_MISMATCH); } + /* if we're the child of another channel and the other channel is in a blocking read they will never realize we have answered so send a SWITCH_SIG_BREAK to interrupt any blocking reads on that channel */ diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 28ef9fd96a..70f7e55f61 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -832,9 +832,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_flush_message(switch_core_se switch_assert(session != NULL); + if (session->message_queue) { while ((status = (switch_status_t) switch_queue_trypop(session->message_queue, &pop)) == SWITCH_STATUS_SUCCESS) { message = (switch_core_session_message_t *) pop; + switch_ivr_process_indications(session, message); switch_core_session_free_message(&message); } } @@ -1124,14 +1126,20 @@ SWITCH_DECLARE(switch_channel_t *) switch_core_session_get_channel(switch_core_s return session->channel; } -SWITCH_DECLARE(void) switch_core_session_wake_session_thread(switch_core_session_t *session) +SWITCH_DECLARE(switch_status_t) switch_core_session_wake_session_thread(switch_core_session_t *session) { + switch_status_t status; + /* If trylock fails the signal is already awake so we needn't bother */ - if (switch_mutex_trylock(session->mutex) == SWITCH_STATUS_SUCCESS) { + status = switch_mutex_trylock(session->mutex); + + if (status == SWITCH_STATUS_SUCCESS) { switch_thread_cond_signal(session->cond); switch_mutex_unlock(session->mutex); } + + return status; } SWITCH_DECLARE(void) switch_core_session_signal_state_change(switch_core_session_t *session) diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index 1ab9af91a9..1bb97aa03d 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -422,13 +422,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session) switch_channel_hangup(session->channel, SWITCH_CAUSE_INVALID_CALL_REFERENCE); } } else { - switch_core_session_message_t *message; - - while (switch_core_session_dequeue_message(session, &message) == SWITCH_STATUS_SUCCESS) { - switch_core_session_receive_message(session, message); - message = NULL; - } - + switch_ivr_parse_all_events(session); switch_ivr_parse_all_events(session); if (switch_channel_get_state(session->channel) == switch_channel_get_running_state(session->channel)) { @@ -440,11 +434,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session) } switch_ivr_parse_all_events(session); - - while (switch_core_session_dequeue_message(session, &message) == SWITCH_STATUS_SUCCESS) { - switch_core_session_receive_message(session, message); - message = NULL; - } + switch_ivr_parse_all_events(session); } } } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 0989cbc4d4..575dc47701 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -668,43 +668,51 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_next_event(switch_core_session_ } -SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_messages(switch_core_session_t *session) +SWITCH_DECLARE(switch_status_t) switch_ivr_process_indications(switch_core_session_t *session, switch_core_session_message_t *message) { - switch_core_session_message_t *message; + switch_status_t status = SWITCH_STATUS_SUCCESS; switch_channel_t *channel = switch_core_session_get_channel(session); - int i = 0; - - switch_ivr_parse_all_signal_data(session); - - while (switch_core_session_dequeue_message(session, &message) == SWITCH_STATUS_SUCCESS) { - i++; switch(message->message_id) { case SWITCH_MESSAGE_INDICATE_ANSWER: if (switch_channel_answer(channel) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } - switch_core_session_free_message(&message); break; case SWITCH_MESSAGE_INDICATE_PROGRESS: if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } - switch_core_session_free_message(&message); break; case SWITCH_MESSAGE_INDICATE_RINGING: if (switch_channel_ring_ready(channel) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); } - switch_core_session_free_message(&message); break; default: - switch_core_session_receive_message(session, message); + status = SWITCH_STATUS_FALSE; break; } - message = NULL; + return status; +} +SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_messages(switch_core_session_t *session) +{ + switch_core_session_message_t *message; + int i = 0; + + switch_ivr_parse_all_signal_data(session); + + while (switch_core_session_dequeue_message(session, &message) == SWITCH_STATUS_SUCCESS) { + i++; + + if (switch_ivr_process_indications(session, message) == SWITCH_STATUS_SUCCESS) { + switch_core_session_free_message(&message); + } else { + switch_core_session_receive_message(session, message); + message = NULL; + } } return i ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index 9ab1c1b1da..91bd1420f0 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -1030,6 +1030,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_wait_for_answer(switch_core_session_t switch_buffer_destroy(&ringback.audio_buffer); } + + switch_ivr_parse_all_events(session); + switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); if (switch_core_codec_ready(&write_codec)) { @@ -3162,9 +3165,17 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess if (caller_channel) { if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) { switch_channel_pass_callee_id(peer_channel, caller_channel); + if (switch_channel_test_flag(caller_channel, CF_PROXY_MODE)) { + status = SWITCH_STATUS_SUCCESS; + } else { status = switch_channel_answer(caller_channel); + } } else if (switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { + if (switch_channel_test_flag(caller_channel, CF_PROXY_MODE)) { + status = SWITCH_STATUS_SUCCESS; + } else { status = switch_channel_pre_answer(caller_channel); + } } else { status = SWITCH_STATUS_SUCCESS; } @@ -3462,6 +3473,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess switch_ivr_sleep(*bleg, 0, SWITCH_TRUE, NULL); } + if (oglobals.session) { + switch_ivr_parse_all_events(oglobals.session); + } + if (oglobals.session && status == SWITCH_STATUS_SUCCESS) { switch_ivr_sleep(oglobals.session, 0, SWITCH_TRUE, NULL); }