Merge branch 'moy.forcehangup'

Conflicts:
	libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
This commit is contained in:
Moises Silva 2010-09-21 14:24:38 -04:00
commit 132795af79
6 changed files with 93 additions and 44 deletions

View File

@ -50,6 +50,7 @@
#endif #endif
#include "ftdm_cpu_monitor.h" #include "ftdm_cpu_monitor.h"
#define FORCE_HANGUP_TIMER 3000
#define SPAN_PENDING_CHANS_QUEUE_SIZE 1000 #define SPAN_PENDING_CHANS_QUEUE_SIZE 1000
#define SPAN_PENDING_SIGNALS_QUEUE_SIZE 1000 #define SPAN_PENDING_SIGNALS_QUEUE_SIZE 1000
#define FTDM_READ_TRACE_INDEX 0 #define FTDM_READ_TRACE_INDEX 0
@ -102,6 +103,7 @@ static struct {
ftdm_mutex_t *mutex; ftdm_mutex_t *mutex;
ftdm_mutex_t *span_mutex; ftdm_mutex_t *span_mutex;
ftdm_mutex_t *group_mutex; ftdm_mutex_t *group_mutex;
ftdm_sched_t *timingsched;
uint32_t span_index; uint32_t span_index;
uint32_t group_index; uint32_t group_index;
uint32_t running; uint32_t running;
@ -2002,6 +2004,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 */ /* make user's life easier, and just ignore double hangup requests */
return FTDM_SUCCESS; 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); ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
} else { } else {
/* the signaling stack did not touch the state, /* the signaling stack did not touch the state,
@ -2283,6 +2288,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
close_dtmf_debug(ftdmchan); close_dtmf_debug(ftdmchan);
#endif #endif
ftdm_channel_clear_vars(ftdmchan); 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->init_state = FTDM_CHANNEL_STATE_DOWN;
ftdmchan->state = FTDM_CHANNEL_STATE_DOWN; ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
@ -4601,6 +4609,21 @@ FT_DECLARE(ftdm_status_t) ftdm_span_trigger_signals(const ftdm_span_t *span)
return FTDM_SUCCESS; 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(fchan, FTDM_LOG_CRIT, "Forcing hangup since the user did not confirmed our hangup after %dms\n", FORCE_HANGUP_TIMER);
call_hangup(fchan, __FILE__, __FUNCTION__, __LINE__);
} 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) FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t *sigmsg)
{ {
if (sigmsg->channel) { if (sigmsg->channel) {
@ -4634,6 +4657,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"); ftdm_log_chan_msg(sigmsg->channel, FTDM_LOG_DEBUG, "Ignoring SIGEVENT_STOP since user already requested hangup\n");
goto done; 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", FORCE_HANGUP_TIMER, execute_safety_hangup, sigmsg->channel, &sigmsg->channel->hangup_timer);
}
break; break;
default: default:
@ -4755,6 +4783,14 @@ FT_DECLARE(ftdm_status_t) ftdm_global_init(void)
ftdm_mutex_create(&globals.span_mutex); ftdm_mutex_create(&globals.span_mutex);
ftdm_mutex_create(&globals.group_mutex); ftdm_mutex_create(&globals.group_mutex);
ftdm_sched_global_init(); 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; globals.running = 1;
return FTDM_SUCCESS; return FTDM_SUCCESS;
} }
@ -4807,6 +4843,8 @@ FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void)
globals.running = 0; globals.running = 0;
ftdm_sched_destroy(&globals.timingsched);
ftdm_cpu_monitor_stop(); ftdm_cpu_monitor_stop();
globals.span_index = 0; globals.span_index = 0;

View File

@ -34,6 +34,8 @@
#include "private/ftdm_core.h" #include "private/ftdm_core.h"
typedef struct ftdm_timer ftdm_timer_t;
static struct { static struct {
ftdm_sched_t *freeruns; ftdm_sched_t *freeruns;
ftdm_mutex_t *mutex; ftdm_mutex_t *mutex;
@ -42,6 +44,7 @@ static struct {
struct ftdm_sched { struct ftdm_sched {
char name[80]; char name[80];
ftdm_timer_id_t currid;
ftdm_mutex_t *mutex; ftdm_mutex_t *mutex;
ftdm_timer_t *timers; ftdm_timer_t *timers;
int freerun; int freerun;
@ -51,6 +54,7 @@ struct ftdm_sched {
struct ftdm_timer { struct ftdm_timer {
char name[80]; char name[80];
ftdm_timer_id_t id;
#ifdef __linux__ #ifdef __linux__
struct timeval time; struct timeval time;
#endif #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); ftdm_set_string(newsched->name, name);
newsched->currid = 1;
*sched = newsched; *sched = newsched;
ftdm_log(FTDM_LOG_DEBUG, "Created schedule %s\n", name); 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; int rc = -1;
void *data; void *data;
struct timeval now; struct timeval now;
ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n"); ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
ftdm_mutex_lock(sched->mutex);
tryagain: tryagain:
ftdm_mutex_lock(sched->mutex);
rc = gettimeofday(&now, NULL); rc = gettimeofday(&now, NULL);
if (rc == -1) { if (rc == -1) {
ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve time of day\n"); ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve time of day\n");
@ -257,11 +263,16 @@ tryagain:
runtimer->prev->next = runtimer->next; runtimer->prev->next = runtimer->next;
} }
runtimer->id = 0;
ftdm_safe_free(runtimer); ftdm_safe_free(runtimer);
/* avoid deadlocks by releasing the sched lock before triggering callbacks */
ftdm_mutex_unlock(sched->mutex);
callback(data); callback(data);
/* after calling a callback we must start the scanning again since the /* 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; goto tryagain;
} }
} }
@ -283,7 +294,7 @@ done:
} }
FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name, 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; ftdm_status_t status = FTDM_FAIL;
#ifdef __linux__ #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(callback != NULL, FTDM_EINVAL, "sched callback is null!\n");
ftdm_assert_return(ms > 0, FTDM_EINVAL, "milliseconds must be bigger than 0!\n"); ftdm_assert_return(ms > 0, FTDM_EINVAL, "milliseconds must be bigger than 0!\n");
if (timer) { if (timerid) {
*timer = NULL; *timerid = 0;
} }
rc = gettimeofday(&now, NULL); rc = gettimeofday(&now, NULL);
@ -312,6 +323,15 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name
if (!newtimer) { if (!newtimer) {
goto done; goto done;
} }
newtimer->id = sched->currid;
sched->currid++;
if (!sched->currid) {
ftdm_log(FTDM_LOG_NOTICE, "Timer id wrap around for sched %s\n", sched->name);
/* we do not want currid to be zero since is an invalid id
* TODO: check that currid does not exists already in the context, it'd be insane
* though, having a timer to live all that time */
sched->currid++;
}
ftdm_set_string(newtimer->name, name); ftdm_set_string(newtimer->name, name);
newtimer->callback = callback; newtimer->callback = callback;
@ -332,9 +352,10 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_timer(ftdm_sched_t *sched, const char *name
sched->timers = newtimer; sched->timers = newtimer;
} }
if (timer) { if (timerid) {
*timer = newtimer; *timerid = newtimer->id;
} }
status = FTDM_SUCCESS; status = FTDM_SUCCESS;
done: done:
@ -418,53 +439,37 @@ done:
return status; 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_status_t status = FTDM_FAIL;
ftdm_timer_t *timer; ftdm_timer_t *timer;
ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n"); 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); ftdm_mutex_lock(sched->mutex);
/* special case where the cancelled timer is the head */ /* look for the timer and destroy it */
if (*intimer == sched->timers) { for (timer = sched->timers; timer; timer = timer->next) {
timer = *intimer; if (timer->id == timerid) {
/* the timer next is the new head (even if that means the new head will be NULL)*/ if (timer == sched->timers) {
sched->timers = timer->next; /* it's the head timer, put a new head */
/* if there is a new head, clean its prev member */ sched->timers = timer->next;
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) {
if (timer->prev) { if (timer->prev) {
timer->prev->next = timer->next; timer->prev->next = timer->next;
} }
if (timer->next) { if (timer->next) {
timer->next->prev = timer->prev; timer->next->prev = timer->prev;
} }
ftdm_log(FTDM_LOG_DEBUG, "cancelled timer %s\n", timer->name);
ftdm_safe_free(timer); ftdm_safe_free(timer);
status = FTDM_SUCCESS; status = FTDM_SUCCESS;
*intimer = NULL;
break; 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); ftdm_mutex_unlock(sched->mutex);

View File

@ -156,7 +156,7 @@ typedef struct sngisdn_chan_data {
uint8_t globalFlg; uint8_t globalFlg;
sngisdn_glare_data_t glare; sngisdn_glare_data_t glare;
ftdm_timer_t *timers[SNGISDN_NUM_TIMERS]; ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS];
} sngisdn_chan_data_t; } sngisdn_chan_data_t;
/* Span specific data */ /* Span specific data */

View File

@ -149,7 +149,8 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event)
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_GET_CALLERID); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_GET_CALLERID);
/* Launch timer in case we never get a FACILITY msg */ /* Launch timer in case we never get a FACILITY msg */
if (signal_data->facility_timeout) { if (signal_data->facility_timeout) {
ftdm_sched_timer(signal_data->sched, "facility_timeout", signal_data->facility_timeout, sngisdn_facility_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_TIMER_FACILITY]); ftdm_sched_timer(signal_data->sched, "facility_timeout", signal_data->facility_timeout,
sngisdn_facility_timeout, (void*) sngisdn_info, sngisdn_info->timers[SNGISDN_TIMER_FACILITY]);
} }
break; break;
} else if (ret_val == 0) { } else if (ret_val == 0) {
@ -715,7 +716,7 @@ void sngisdn_process_fac_ind (sngisdn_event_data_t *sngisdn_event)
} }
if (signal_data->facility_timeout) { if (signal_data->facility_timeout) {
/* Cancel facility timeout */ /* 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]);
} }
} }

View File

@ -425,6 +425,7 @@ struct ftdm_channel {
float txgain; float txgain;
int availability_rate; int availability_rate;
void *user_private; void *user_private;
ftdm_timer_id_t hangup_timer;
#ifdef FTDM_DEBUG_DTMF #ifdef FTDM_DEBUG_DTMF
ftdm_dtmf_debug_t dtmfdbg; ftdm_dtmf_debug_t dtmfdbg;
#endif #endif

View File

@ -44,8 +44,8 @@ extern "C" {
#define FTDM_MICROSECONDS_PER_SECOND 1000000 #define FTDM_MICROSECONDS_PER_SECOND 1000000
typedef struct ftdm_sched ftdm_sched_t; typedef struct ftdm_sched ftdm_sched_t;
typedef struct ftdm_timer ftdm_timer_t;
typedef void (*ftdm_sched_callback_t)(void *data); typedef void (*ftdm_sched_callback_t)(void *data);
typedef uint64_t ftdm_timer_id_t;
/*! \brief Create a new scheduling context */ /*! \brief Create a new scheduling context */
FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched, const char *name); 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 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 callback The callback to call upon timer expiration (required)
* \param data Optional data to pass to the callback * \param data Optional data to pass to the callback
* \param timer The timer that was created, it can be NULL if you dont care, * \param timer Timer id pointer to store the id of the newly created timer. It can be null
* but you need this if you want to be able to cancel the timer with ftdm_sched_cancel_timer * 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, 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 * \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 sched The scheduling context (required)
* \param timer The timer to cancel (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 */ /*! \brief Destroy the context and all of the scheduled timers in it */
FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **sched); FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **sched);