freetdm: remove busy-waiting and use ftdm interrupt to wait for state change completion
This commit is contained in:
parent
55da2afa7f
commit
903d136481
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue