fix race condition when hangup happends after answer indication but before the session thread is started

This commit is contained in:
Anthony Minessale 2010-05-03 13:29:56 -04:00 committed by Sangoma Dev Box
parent 652d2fdfb3
commit 9096501ee4
5 changed files with 47 additions and 17 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);