fix race condition in state thread

This commit is contained in:
Anthony Minessale 2013-02-22 17:46:54 -06:00
parent 31865f0dc4
commit ee7dd033fd
5 changed files with 41 additions and 9 deletions

View File

@ -649,6 +649,7 @@ SWITCH_DECLARE(const char *) switch_channel_get_partner_uuid(switch_channel_t *c
SWITCH_DECLARE(switch_hold_record_t *) switch_channel_get_hold_record(switch_channel_t *channel);
SWITCH_DECLARE(void) switch_channel_state_thread_lock(switch_channel_t *channel);
SWITCH_DECLARE(void) switch_channel_state_thread_unlock(switch_channel_t *channel);
SWITCH_DECLARE(switch_status_t) switch_channel_state_thread_trylock(switch_channel_t *channel);
SWITCH_END_EXTERN_C
#endif

View File

@ -2288,6 +2288,13 @@ SWITCH_DECLARE(void) switch_channel_state_thread_lock(switch_channel_t *channel)
switch_mutex_lock(channel->thread_mutex);
}
SWITCH_DECLARE(switch_status_t) switch_channel_state_thread_trylock(switch_channel_t *channel)
{
return switch_mutex_trylock(channel->thread_mutex);
}
SWITCH_DECLARE(void) switch_channel_state_thread_unlock(switch_channel_t *channel)
{
switch_mutex_unlock(channel->thread_mutex);

View File

@ -927,7 +927,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_message(switch_core_
switch_assert(session != NULL);
if (session->message_queue && switch_queue_size(session->message_queue)) {
if (session->message_queue) {
if ((status = (switch_status_t) switch_queue_trypop(session->message_queue, &pop)) == SWITCH_STATUS_SUCCESS) {
*message = (switch_core_session_message_t *) pop;
if ((*message)->delivery_time && (*message)->delivery_time > switch_epoch_time_now(NULL)) {
@ -968,7 +968,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_queue_signal_data(switch_cor
switch_assert(session != NULL);
if (session->signal_data_queue) {
if (switch_queue_trypush(session->signal_data_queue, signal_data) == SWITCH_STATUS_SUCCESS) {
if (switch_queue_push(session->signal_data_queue, signal_data) == SWITCH_STATUS_SUCCESS) {
status = SWITCH_STATUS_SUCCESS;
}
@ -988,7 +988,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_dequeue_signal_data(switch_c
switch_assert(session != NULL);
if (session->signal_data_queue && switch_queue_size(session->signal_data_queue)) {
if (session->signal_data_queue) {
if ((status = (switch_status_t) switch_queue_trypop(session->signal_data_queue, &pop)) == SWITCH_STATUS_SUCCESS) {
*signal_data = pop;
}
@ -1257,14 +1257,33 @@ SWITCH_DECLARE(switch_mutex_t *) switch_core_session_get_mutex(switch_core_sessi
SWITCH_DECLARE(switch_status_t) switch_core_session_wake_session_thread(switch_core_session_t *session)
{
switch_status_t status;
int tries = 0;
/* If trylock fails the signal is already awake so we needn't bother */
/* If trylock fails the signal is already awake so we needn't bother ..... or do we????*/
top:
status = switch_mutex_trylock(session->mutex);
if (status == SWITCH_STATUS_SUCCESS) {
switch_thread_cond_signal(session->cond);
switch_mutex_unlock(session->mutex);
} else {
if (switch_channel_state_thread_trylock(session->channel) == SWITCH_STATUS_SUCCESS) {
/* We've beat them for sure, as soon as we release this lock, they will be checking their queue on the next line. */
switch_channel_state_thread_unlock(session->channel);
} else {
/* What luck! The channel has already started going to sleep *after* we checked if we need to wake it up.
It will miss any messages in its queue because they were inserted after *it* checked its queue. (catch-22)
So, it's not asleep yet, but it's too late for us to be sure they know we want them to stay awake and check its queue again.
Now *we* need to sleep instead but just for 1ms so we can circle back and try again.
This is so rare (yet possible) to happen that we can be fairly certian it will not happen 2x in a row but we'll try 10x just in case.
*/
if (++tries < 10) {
switch_cond_next();
goto top;
}
}
}
return status;

View File

@ -524,7 +524,16 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session_t *session)
switch_channel_state_thread_lock(session->channel);
switch_channel_set_flag(session->channel, CF_THREAD_SLEEPING);
if (switch_channel_get_state(session->channel) == switch_channel_get_running_state(session->channel)) {
switch_ivr_parse_all_events(session);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s session thread sleep state: %s!\n",
switch_channel_get_name(session->channel),
switch_channel_state_name(switch_channel_get_running_state(session->channel)));
switch_thread_cond_wait(session->cond, session->mutex);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1, "%s session thread wake state: %s!\n",
switch_channel_get_name(session->channel),
switch_channel_state_name(switch_channel_get_running_state(session->channel)));
}
switch_channel_clear_flag(session->channel, CF_THREAD_SLEEPING);
switch_channel_state_thread_unlock(session->channel);

View File

@ -851,10 +851,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_parse_all_events(switch_core_session_
x++;
}
if (x) {
switch_ivr_sleep(session, 0, SWITCH_TRUE, NULL);
}
return SWITCH_STATUS_SUCCESS;
}