freetdm: remove busy-waiting and use ftdm interrupt to wait for state change completion

This commit is contained in:
Moises Silva 2010-12-30 10:23:56 -05:00
parent 55da2afa7f
commit 903d136481
4 changed files with 61 additions and 51 deletions

View File

@ -101,8 +101,10 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const c
ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff); ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
/* FIXME: broadcast condition to wake up anyone waiting on state completion if the channel if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
* is blocking (FTDM_CHANNEL_NONBLOCK is not set) */ ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
ftdm_interrupt_signal(fchan->state_change_notify);
}
return FTDM_SUCCESS; return FTDM_SUCCESS;
} }
@ -169,6 +171,7 @@ static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t s
#define DEFAULT_WAIT_TIME 1000 #define DEFAULT_WAIT_TIME 1000
FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq) FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
{ {
ftdm_status_t status;
int ok = 1; int ok = 1;
int waitms = DEFAULT_WAIT_TIME; int waitms = DEFAULT_WAIT_TIME;
@ -191,6 +194,16 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *f
return FTDM_FAIL; return FTDM_FAIL;
} }
if (!ftdmchan->state_change_notify) {
status = ftdm_interrupt_create(&ftdmchan->state_change_notify, FTDM_INVALID_SOCKET);
if (status != FTDM_SUCCESS) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT,
"Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return status;
}
}
if (ftdmchan->span->state_map) { if (ftdmchan->span->state_map) {
ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map); ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
goto end; goto end;
@ -276,67 +289,54 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *f
end: end:
if (ok) { if (!ok) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
ftdmchan->last_state = ftdmchan->state;
ftdmchan->state = state;
ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
ftdmchan->history[ftdmchan->hindex].file = file;
ftdmchan->history[ftdmchan->hindex].func = func;
ftdmchan->history[ftdmchan->hindex].line = line;
ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
ftdmchan->history[ftdmchan->hindex].end_time = 0;
ftdmchan->hindex++;
if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
ftdmchan->hindex = 0;
}
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_mutex_lock(ftdmchan->span->mutex);
ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
if (ftdmchan->span->pendingchans) {
ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
}
ftdm_mutex_unlock(ftdmchan->span->mutex);
} else {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state)); ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
goto done; goto done;
} }
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
ftdmchan->last_state = ftdmchan->state;
ftdmchan->state = state;
ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
ftdmchan->history[ftdmchan->hindex].file = file;
ftdmchan->history[ftdmchan->hindex].func = func;
ftdmchan->history[ftdmchan->hindex].line = line;
ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
ftdmchan->history[ftdmchan->hindex].end_time = 0;
ftdmchan->hindex++;
if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
ftdmchan->hindex = 0;
}
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_mutex_lock(ftdmchan->span->mutex);
ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
if (ftdmchan->span->pendingchans) {
ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
}
ftdm_mutex_unlock(ftdmchan->span->mutex);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) { if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) {
/* the channel should not block waiting for state processing */ /* the channel should not block waiting for state processing */
goto done; goto done;
} }
/* there is an inherent race here between set and check of the change flag but we do not care because ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
* the flag should never last raised for more than a few ms for any state change */
while (waitrq && waitms > 0) {
/* give a chance to the signaling stack to process it */
ftdm_mutex_unlock(ftdmchan->mutex);
ftdm_sleep(10); ftdm_mutex_unlock(ftdmchan->mutex);
waitms -= 10;
ftdm_mutex_lock(ftdmchan->mutex); status = ftdm_interrupt_wait(ftdmchan->state_change_notify, waitms);
/* if the flag is no longer set, the state change was processed (or is being processed) */
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
break;
}
/* if the state is no longer what we set, the state change was ftdm_mutex_lock(ftdmchan->mutex);
* obviously processed (and the current state change flag is for other state change) */
if (ftdmchan->state != state) {
break;
}
}
if (waitms <= 0) { if (status != FTDM_SUCCESS) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n", ftdm_log_chan_ex(ftdmchan, file, func, line,
FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n",
ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME); ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
ok = 0; ok = 0;
goto done;
} }
done: done:
return ok ? FTDM_SUCCESS : FTDM_FAIL; return ok ? FTDM_SUCCESS : FTDM_FAIL;

View File

@ -56,7 +56,11 @@ struct ftdm_interrupt {
/* for generic interruption */ /* for generic interruption */
HANDLE event; HANDLE event;
#else #else
/* for generic interruption */ /* In theory we could be using thread conditions for generic interruption,
* however, Linux does not have a primitive like Windows WaitForMultipleObjects
* to wait for both thread condition and file descriptors, therefore we decided
* to use a dummy pipe for generic interruption/condition logic
* */
int readfd; int readfd;
int writefd; int writefd;
#endif #endif
@ -243,6 +247,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex)
FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device) FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device)
{ {
ftdm_status_t status = FTDM_SUCCESS;
ftdm_interrupt_t *interrupt = NULL; ftdm_interrupt_t *interrupt = NULL;
#ifndef WIN32 #ifndef WIN32
int fds[2]; int fds[2];
@ -253,7 +258,7 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt,
interrupt = ftdm_calloc(1, sizeof(*interrupt)); interrupt = ftdm_calloc(1, sizeof(*interrupt));
if (!interrupt) { if (!interrupt) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n"); ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n");
return FTDM_FAIL; return FTDM_ENOMEM;
} }
interrupt->device = device; interrupt->device = device;
@ -261,11 +266,13 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt,
interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL); interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!interrupt->event) { if (!interrupt->event) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n"); ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n");
status = FTDM_ENOMEM;
goto failed; goto failed;
} }
#else #else
if (pipe(fds)) { if (pipe(fds)) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno)); ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno));
status = FTDM_FAIL;
goto failed; goto failed;
} }
interrupt->readfd = fds[0]; interrupt->readfd = fds[0];
@ -287,7 +294,7 @@ failed:
#endif #endif
ftdm_safe_free(interrupt); ftdm_safe_free(interrupt);
} }
return FTDM_FAIL; return status;
} }
#define ONE_BILLION 1000000000 #define ONE_BILLION 1000000000

View File

@ -462,6 +462,7 @@ struct ftdm_channel {
ftdm_dtmf_debug_t dtmfdbg; ftdm_dtmf_debug_t dtmfdbg;
ftdm_io_dump_t rxdump; ftdm_io_dump_t rxdump;
ftdm_io_dump_t txdump; ftdm_io_dump_t txdump;
ftdm_interrupt_t *state_change_notify; /*!< Notify when a state change is terminated */
int32_t txdrops; int32_t txdrops;
int32_t rxdrops; int32_t rxdrops;
}; };

View File

@ -250,6 +250,8 @@ typedef enum {
#define FTDM_CHANNEL_NONBLOCK (1ULL << 33) #define FTDM_CHANNEL_NONBLOCK (1ULL << 33)
/*!< There is a pending acknowledge for an indication */ /*!< There is a pending acknowledge for an indication */
#define FTDM_CHANNEL_IND_ACK_PENDING (1ULL << 34) #define FTDM_CHANNEL_IND_ACK_PENDING (1ULL << 34)
/*!< There is someone blocking in the channel waiting for state completion */
#define FTDM_CHANNEL_BLOCKING (1ULL << 35)
#include "ftdm_state.h" #include "ftdm_state.h"