From 9096501ee44db8c259af1cd0594cd26cfa76d913 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Mon, 3 May 2010 13:29:56 -0400 Subject: [PATCH] fix race condition when hangup happends after answer indication but before the session thread is started --- src/include/private/switch_core_pvt.h | 5 +++-- src/include/switch_core.h | 1 + src/switch_channel.c | 30 ++++++++++++++++++++++----- src/switch_core_session.c | 22 ++++++++++++++------ src/switch_core_state_machine.c | 6 ++---- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h index ef1fe80fed..12fd3209c6 100644 --- a/src/include/private/switch_core_pvt.h +++ b/src/include/private/switch_core_pvt.h @@ -92,7 +92,9 @@ typedef enum { SSF_NONE = 0, SSF_DESTROYED = (1 << 0), SSF_WARN_TRANSCODE = (1 << 1), - SSF_HANGUP = (1 << 2) + SSF_HANGUP = (1 << 2), + SSF_THREAD_STARTED = (1 << 3), + SSF_THREAD_RUNNING = (1 << 4) } switch_session_flag_t; @@ -103,7 +105,6 @@ struct switch_core_session { switch_endpoint_interface_t *endpoint_interface; switch_size_t id; switch_session_flag_t flags; - int thread_running; switch_channel_t *channel; switch_io_event_hooks_t event_hooks; diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 7fb24783e9..7cfc102dad 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -479,6 +479,7 @@ SWITCH_DECLARE(void) switch_core_session_run(_In_ switch_core_session_t *session \param session the session on which to check */ SWITCH_DECLARE(unsigned int) switch_core_session_running(_In_ switch_core_session_t *session); +SWITCH_DECLARE(unsigned int) switch_core_session_started(_In_ switch_core_session_t *session); SWITCH_DECLARE(void *) switch_core_perform_permanent_alloc(_In_ switch_size_t memory, _In_z_ const char *file, _In_z_ const char *func, _In_ int line); diff --git a/src/switch_channel.c b/src/switch_channel.c index b826da45d5..77838a8ca6 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -106,6 +106,10 @@ static struct switch_cause_table CAUSE_CHART[] = { {NULL, 0} }; +typedef enum { + OCF_HANGUP = (1 << 0) +} opaque_channel_flag_t; + struct switch_channel { char *name; switch_call_direction_t direction; @@ -132,6 +136,7 @@ struct switch_channel { int vi; int event_count; int profile_index; + opaque_channel_flag_t opaque_flags; }; SWITCH_DECLARE(const char *) switch_channel_cause2str(switch_call_cause_t cause) @@ -2090,29 +2095,44 @@ SWITCH_DECLARE(void) switch_channel_set_hangup_time(switch_channel_t *channel) SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_hangup(switch_channel_t *channel, const char *file, const char *func, int line, switch_call_cause_t hangup_cause) { + int ok = 0; + switch_assert(channel != NULL); + /* one per customer */ + switch_mutex_lock(channel->state_mutex); + if (!(channel->opaque_flags & OCF_HANGUP)) { + channel->opaque_flags |= OCF_HANGUP; + ok = 1; + } + switch_mutex_unlock(channel->state_mutex); + + if (!ok) { + return channel->state; + } + switch_channel_clear_flag(channel, CF_BLOCK_STATE); if (channel->state < CS_HANGUP) { switch_channel_state_t last_state; switch_event_t *event; - if (hangup_cause == SWITCH_CAUSE_LOSE_RACE) { - switch_channel_set_variable(channel, "presence_call_info", NULL); - } - switch_mutex_lock(channel->state_mutex); last_state = channel->state; channel->state = CS_HANGUP; switch_mutex_unlock(channel->state_mutex); + + if (hangup_cause == SWITCH_CAUSE_LOSE_RACE) { + switch_channel_set_variable(channel, "presence_call_info", NULL); + } + channel->hangup_cause = hangup_cause; switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Hangup %s [%s] [%s]\n", channel->name, state_names[last_state], switch_channel_cause2str(channel->hangup_cause)); - if (!switch_core_session_running(channel->session)) { + if (!switch_core_session_running(channel->session) && !switch_core_session_started(channel->session)) { switch_core_session_thread_launch(channel->session); } diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 69f2492a93..1de6ea02d6 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -1024,7 +1024,12 @@ SWITCH_DECLARE(void) switch_core_session_signal_state_change(switch_core_session SWITCH_DECLARE(unsigned int) switch_core_session_running(switch_core_session_t *session) { - return session->thread_running; + return switch_test_flag(session, SSF_THREAD_RUNNING) ? 1 : 0; +} + +SWITCH_DECLARE(unsigned int) switch_core_session_started(switch_core_session_t *session) +{ + return switch_test_flag(session, SSF_THREAD_STARTED) ? 1 : 0; } SWITCH_DECLARE(void) switch_core_session_perform_destroy(switch_core_session_t **session, const char *file, const char *func, int line) @@ -1197,17 +1202,22 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_thread_launch(switch_core_se switch_mutex_lock(session->mutex); - if (!session->thread_running) { - session->thread_running = 1; + if (switch_test_flag(session, SSF_THREAD_RUNNING)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot double-launch thread!\n"); + } else if (switch_test_flag(session, SSF_THREAD_STARTED)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot launch thread again after it has already been run!\n"); + } else { + switch_set_flag(session, SSF_THREAD_RUNNING); + switch_set_flag(session, SSF_THREAD_STARTED); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); if (switch_thread_create(&thread, thd_attr, switch_core_session_thread, session, session->pool) == SWITCH_STATUS_SUCCESS) { + switch_set_flag(session, SSF_THREAD_STARTED); status = SWITCH_STATUS_SUCCESS; } else { - session->thread_running = 0; + switch_clear_flag(session, SSF_THREAD_RUNNING); + switch_clear_flag(session, SSF_THREAD_STARTED); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot create thread!\n"); } - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Cannot double-launch thread!\n"); } switch_mutex_unlock(session->mutex); diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index ec9c190e70..43315705ce 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -291,7 +291,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session) */ switch_assert(session != NULL); - session->thread_running = 1; + switch_set_flag(session, SSF_THREAD_RUNNING); endpoint_interface = session->endpoint_interface; switch_assert(endpoint_interface != NULL); @@ -409,7 +409,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session) done: switch_mutex_unlock(session->mutex); - session->thread_running = 0; + switch_clear_flag(session, SSF_THREAD_RUNNING); } SWITCH_DECLARE(void) switch_core_session_destroy_state(switch_core_session_t *session) @@ -429,7 +429,6 @@ SWITCH_DECLARE(void) switch_core_session_destroy_state(switch_core_session_t *se switch_channel_clear_flag(session->channel, CF_TRANSFER); switch_channel_clear_flag(session->channel, CF_REDIRECT); - session->thread_running = 1; endpoint_interface = session->endpoint_interface; switch_assert(endpoint_interface != NULL); @@ -567,7 +566,6 @@ SWITCH_DECLARE(void) switch_core_session_reporting_state(switch_core_session_t * switch_assert(session != NULL); - session->thread_running = 1; endpoint_interface = session->endpoint_interface; switch_assert(endpoint_interface != NULL);