freetdm: add force hangup timer and refactor scheduling code
This commit is contained in:
parent
6b77b4ec07
commit
696becc093
|
@ -102,6 +102,7 @@ static struct {
|
|||
ftdm_mutex_t *mutex;
|
||||
ftdm_mutex_t *span_mutex;
|
||||
ftdm_mutex_t *group_mutex;
|
||||
ftdm_sched_t *timingsched;
|
||||
uint32_t span_index;
|
||||
uint32_t group_index;
|
||||
uint32_t running;
|
||||
|
@ -2002,6 +2003,9 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c
|
|||
/* make user's life easier, and just ignore double hangup requests */
|
||||
return FTDM_SUCCESS;
|
||||
}
|
||||
if (chan->hangup_timer) {
|
||||
ftdm_sched_cancel_timer(globals.timingsched, chan->hangup_timer);
|
||||
}
|
||||
ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
|
||||
} else {
|
||||
/* the signaling stack did not touch the state,
|
||||
|
@ -2283,6 +2287,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
|||
close_dtmf_debug(ftdmchan);
|
||||
#endif
|
||||
ftdm_channel_clear_vars(ftdmchan);
|
||||
if (ftdmchan->hangup_timer) {
|
||||
ftdm_sched_cancel_timer(globals.timingsched, ftdmchan->hangup_timer);
|
||||
}
|
||||
|
||||
ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
|
||||
ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
|
||||
|
@ -4601,6 +4608,21 @@ FT_DECLARE(ftdm_status_t) ftdm_span_trigger_signals(const ftdm_span_t *span)
|
|||
return FTDM_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void execute_safety_hangup(void *data)
|
||||
{
|
||||
ftdm_channel_t *fchan = data;
|
||||
ftdm_channel_lock(fchan);
|
||||
fchan->hangup_timer = 0;
|
||||
if (fchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
|
||||
ftdm_log_chan_msg(fchan, FTDM_LOG_CRIT, "Forcing hangup\n");
|
||||
ftdm_channel_call_hangup(fchan);
|
||||
} else {
|
||||
ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Not performing safety hangup, channel state is %s\n", ftdm_channel_state2str(fchan->state));
|
||||
}
|
||||
ftdm_channel_unlock(fchan);
|
||||
}
|
||||
|
||||
FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t *sigmsg)
|
||||
{
|
||||
if (sigmsg->channel) {
|
||||
|
@ -4634,6 +4656,11 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t
|
|||
ftdm_log_chan_msg(sigmsg->channel, FTDM_LOG_DEBUG, "Ignoring SIGEVENT_STOP since user already requested hangup\n");
|
||||
goto done;
|
||||
}
|
||||
if (sigmsg->channel->state == FTDM_CHANNEL_STATE_TERMINATING) {
|
||||
ftdm_log_chan_msg(sigmsg->channel, FTDM_LOG_DEBUG, "Scheduling safety hangup timer\n");
|
||||
/* if the user does not move us to hangup in 2 seconds, we will do it ourselves */
|
||||
ftdm_sched_timer(globals.timingsched, "safety-hangup", 2000, execute_safety_hangup, sigmsg->channel, &sigmsg->channel->hangup_timer);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -4755,6 +4782,14 @@ FT_DECLARE(ftdm_status_t) ftdm_global_init(void)
|
|||
ftdm_mutex_create(&globals.span_mutex);
|
||||
ftdm_mutex_create(&globals.group_mutex);
|
||||
ftdm_sched_global_init();
|
||||
if (ftdm_sched_create(&globals.timingsched, "freetdm-master") != FTDM_SUCCESS) {
|
||||
ftdm_log(FTDM_LOG_CRIT, "Failed to create master timing schedule context\n");
|
||||
return FTDM_FAIL;
|
||||
}
|
||||
if (ftdm_sched_free_run(globals.timingsched) != FTDM_SUCCESS) {
|
||||
ftdm_log(FTDM_LOG_CRIT, "Failed to run master timing schedule context\n");
|
||||
return FTDM_FAIL;
|
||||
}
|
||||
globals.running = 1;
|
||||
return FTDM_SUCCESS;
|
||||
}
|
||||
|
@ -4807,6 +4842,8 @@ FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void)
|
|||
|
||||
globals.running = 0;
|
||||
|
||||
ftdm_sched_destroy(&globals.timingsched);
|
||||
|
||||
ftdm_cpu_monitor_stop();
|
||||
|
||||
globals.span_index = 0;
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
#include "private/ftdm_core.h"
|
||||
|
||||
typedef struct ftdm_timer ftdm_timer_t;
|
||||
|
||||
static struct {
|
||||
ftdm_sched_t *freeruns;
|
||||
ftdm_mutex_t *mutex;
|
||||
|
@ -42,6 +44,7 @@ static struct {
|
|||
|
||||
struct ftdm_sched {
|
||||
char name[80];
|
||||
ftdm_timer_id_t currid;
|
||||
ftdm_mutex_t *mutex;
|
||||
ftdm_timer_t *timers;
|
||||
int freerun;
|
||||
|
@ -51,6 +54,7 @@ struct ftdm_sched {
|
|||
|
||||
struct ftdm_timer {
|
||||
char name[80];
|
||||
ftdm_timer_id_t id;
|
||||
#ifdef __linux__
|
||||
struct timeval time;
|
||||
#endif
|
||||
|
@ -191,6 +195,7 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched, const char *na
|
|||
}
|
||||
|
||||
ftdm_set_string(newsched->name, name);
|
||||
newsched->currid = 1;
|
||||
|
||||
*sched = newsched;
|
||||
ftdm_log(FTDM_LOG_DEBUG, "Created schedule %s\n", name);
|
||||
|
@ -219,12 +224,13 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_run(ftdm_sched_t *sched)
|
|||
int rc = -1;
|
||||
void *data;
|
||||
struct timeval now;
|
||||
|
||||
ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
|
||||
|
||||
ftdm_mutex_lock(sched->mutex);
|
||||
|
||||
tryagain:
|
||||
|
||||
ftdm_mutex_lock(sched->mutex);
|
||||
|
||||
rc = gettimeofday(&now, NULL);
|
||||
if (rc == -1) {
|
||||
ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve time of day\n");
|
||||
|
@ -257,11 +263,16 @@ tryagain:
|
|||
runtimer->prev->next = runtimer->next;
|
||||
}
|
||||
|
||||
runtimer->id = 0;
|
||||
ftdm_safe_free(runtimer);
|
||||
|
||||
/* avoid deadlocks by releasing the sched lock before triggering callbacks */
|
||||
ftdm_mutex_unlock(sched->mutex);
|
||||
|
||||
callback(data);
|
||||
/* after calling a callback we must start the scanning again since the
|
||||
* callback may have added or cancelled timers to the linked list */
|
||||
* callback or some other thread may have added or cancelled timers to
|
||||
* the linked list */
|
||||
goto tryagain;
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +294,7 @@ done:
|
|||
}
|
||||
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name,
|
||||
int ms, ftdm_sched_callback_t callback, void *data, ftdm_timer_t **timer)
|
||||
int ms, ftdm_sched_callback_t callback, void *data, ftdm_timer_id_t *timerid)
|
||||
{
|
||||
ftdm_status_t status = FTDM_FAIL;
|
||||
#ifdef __linux__
|
||||
|
@ -296,8 +307,8 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name
|
|||
ftdm_assert_return(callback != NULL, FTDM_EINVAL, "sched callback is null!\n");
|
||||
ftdm_assert_return(ms > 0, FTDM_EINVAL, "milliseconds must be bigger than 0!\n");
|
||||
|
||||
if (timer) {
|
||||
*timer = NULL;
|
||||
if (timerid) {
|
||||
*timerid = 0;
|
||||
}
|
||||
|
||||
rc = gettimeofday(&now, NULL);
|
||||
|
@ -312,6 +323,8 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name
|
|||
if (!newtimer) {
|
||||
goto done;
|
||||
}
|
||||
newtimer->id = sched->currid;
|
||||
sched->currid++;
|
||||
|
||||
ftdm_set_string(newtimer->name, name);
|
||||
newtimer->callback = callback;
|
||||
|
@ -332,9 +345,10 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name
|
|||
sched->timers = newtimer;
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
*timer = newtimer;
|
||||
if (timerid) {
|
||||
*timerid = newtimer->id;
|
||||
}
|
||||
|
||||
status = FTDM_SUCCESS;
|
||||
done:
|
||||
|
||||
|
@ -418,53 +432,37 @@ done:
|
|||
return status;
|
||||
}
|
||||
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_timer_t **intimer)
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_timer_id_t timerid)
|
||||
{
|
||||
ftdm_status_t status = FTDM_FAIL;
|
||||
ftdm_timer_t *timer;
|
||||
|
||||
ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
|
||||
ftdm_assert_return(intimer != NULL, FTDM_EINVAL, "timer is null!\n");
|
||||
ftdm_assert_return(*intimer != NULL, FTDM_EINVAL, "timer is null!\n");
|
||||
|
||||
if (!timerid) {
|
||||
return FTDM_SUCCESS;
|
||||
}
|
||||
|
||||
ftdm_mutex_lock(sched->mutex);
|
||||
|
||||
/* special case where the cancelled timer is the head */
|
||||
if (*intimer == sched->timers) {
|
||||
timer = *intimer;
|
||||
/* the timer next is the new head (even if that means the new head will be NULL)*/
|
||||
sched->timers = timer->next;
|
||||
/* if there is a new head, clean its prev member */
|
||||
if (sched->timers) {
|
||||
sched->timers->prev = NULL;
|
||||
}
|
||||
/* free the old head */
|
||||
ftdm_safe_free(timer);
|
||||
status = FTDM_SUCCESS;
|
||||
*intimer = NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* look for the timer and destroy it (we know now that is not head, se we start at the next member after head) */
|
||||
for (timer = sched->timers->next; timer; timer = timer->next) {
|
||||
if (timer == *intimer) {
|
||||
/* look for the timer and destroy it */
|
||||
for (timer = sched->timers; timer; timer = timer->next) {
|
||||
if (timer->id == timerid) {
|
||||
if (timer == sched->timers) {
|
||||
/* it's the head timer, put a new head */
|
||||
sched->timers = timer->next;
|
||||
}
|
||||
if (timer->prev) {
|
||||
timer->prev->next = timer->next;
|
||||
}
|
||||
if (timer->next) {
|
||||
timer->next->prev = timer->prev;
|
||||
}
|
||||
ftdm_log(FTDM_LOG_DEBUG, "cancelled timer %s\n", timer->name);
|
||||
ftdm_safe_free(timer);
|
||||
status = FTDM_SUCCESS;
|
||||
*intimer = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (status == FTDM_FAIL) {
|
||||
ftdm_log(FTDM_LOG_ERROR, "Could not find timer %s to cancel it\n", (*intimer)->name);
|
||||
}
|
||||
|
||||
ftdm_mutex_unlock(sched->mutex);
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ typedef struct sngisdn_chan_data {
|
|||
|
||||
uint8_t globalFlg;
|
||||
sngisdn_glare_data_t glare;
|
||||
ftdm_timer_t *timers[SNGISDN_NUM_TIMERS];
|
||||
ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS];
|
||||
} sngisdn_chan_data_t;
|
||||
|
||||
/* Span specific data */
|
||||
|
|
|
@ -709,7 +709,7 @@ void sngisdn_process_fac_ind (sngisdn_event_data_t *sngisdn_event)
|
|||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to retrieve Caller Name from Facility IE\n");
|
||||
}
|
||||
/* Cancel facility timeout */
|
||||
ftdm_sched_cancel_timer(signal_data->sched, &sngisdn_info->timers[SNGISDN_TIMER_FACILITY]);
|
||||
ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_TIMER_FACILITY]);
|
||||
}
|
||||
|
||||
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
|
||||
|
|
|
@ -425,6 +425,7 @@ struct ftdm_channel {
|
|||
float txgain;
|
||||
int availability_rate;
|
||||
void *user_private;
|
||||
ftdm_timer_id_t hangup_timer;
|
||||
#ifdef FTDM_DEBUG_DTMF
|
||||
ftdm_dtmf_debug_t dtmfdbg;
|
||||
#endif
|
||||
|
|
|
@ -44,8 +44,8 @@ extern "C" {
|
|||
#define FTDM_MICROSECONDS_PER_SECOND 1000000
|
||||
|
||||
typedef struct ftdm_sched ftdm_sched_t;
|
||||
typedef struct ftdm_timer ftdm_timer_t;
|
||||
typedef void (*ftdm_sched_callback_t)(void *data);
|
||||
typedef uint64_t ftdm_timer_id_t;
|
||||
|
||||
/*! \brief Create a new scheduling context */
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched, const char *name);
|
||||
|
@ -62,18 +62,22 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_free_run(ftdm_sched_t *sched);
|
|||
* \param name Timer name, typically unique but is not required to be unique, any null terminated string is fine (required)
|
||||
* \param callback The callback to call upon timer expiration (required)
|
||||
* \param data Optional data to pass to the callback
|
||||
* \param timer The timer that was created, it can be NULL if you dont care,
|
||||
* but you need this if you want to be able to cancel the timer with ftdm_sched_cancel_timer
|
||||
* \param timer Timer id pointer to store the id of the newly created timer. It can be null
|
||||
* if you do not need to know the id, but you need this if you want to be able
|
||||
* to cancel the timer with ftdm_sched_cancel_timer
|
||||
*/
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name,
|
||||
int ms, ftdm_sched_callback_t callback, void *data, ftdm_timer_t **timer);
|
||||
int ms, ftdm_sched_callback_t callback, void *data, ftdm_timer_id_t *timer);
|
||||
|
||||
/*!
|
||||
* \brief Cancel the timer
|
||||
* Note that there is a race between cancelling and triggering a timer.
|
||||
* By the time you call this function the timer may be about to be triggered.
|
||||
* This is specially true with timers in free run schedule.
|
||||
* \param sched The scheduling context (required)
|
||||
* \param timer The timer to cancel (required)
|
||||
*/
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_timer_t **timer);
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_timer_id_t timer);
|
||||
|
||||
/*! \brief Destroy the context and all of the scheduled timers in it */
|
||||
FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **sched);
|
||||
|
|
Loading…
Reference in New Issue