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;
/* 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,
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;
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) {
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:
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;
}
globals.running = 0;
+ ftdm_sched_destroy(&globals.timingsched);
+
ftdm_cpu_monitor_stop();
globals.span_index = 0;
#include "private/ftdm_core.h"
+typedef struct ftdm_timer ftdm_timer_t;
+
static struct {
ftdm_sched_t *freeruns;
ftdm_mutex_t *mutex;
struct ftdm_sched {
char name[80];
+ ftdm_timer_id_t currid;
ftdm_mutex_t *mutex;
ftdm_timer_t *timers;
int freerun;
struct ftdm_timer {
char name[80];
+ ftdm_timer_id_t id;
#ifdef __linux__
struct timeval time;
#endif
}
ftdm_set_string(newsched->name, name);
+ newsched->currid = 1;
*sched = newsched;
ftdm_log(FTDM_LOG_DEBUG, "Created schedule %s\n", name);
int rc = -1;
void *data;
struct timeval now;
- ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
- ftdm_mutex_lock(sched->mutex);
+ ftdm_assert_return(sched != NULL, FTDM_EINVAL, "sched is null!\n");
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");
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;
}
}
}
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__
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);
if (!newtimer) {
goto done;
}
+ newtimer->id = sched->currid;
+ sched->currid++;
ftdm_set_string(newtimer->name, name);
newtimer->callback = callback;
sched->timers = newtimer;
}
- if (timer) {
- *timer = newtimer;
+ if (timerid) {
+ *timerid = newtimer->id;
}
+
status = FTDM_SUCCESS;
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");
-
- 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;
+ if (!timerid) {
+ return FTDM_SUCCESS;
}
- /* 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) {
+ ftdm_mutex_lock(sched->mutex);
+
+ /* 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);
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 */
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);
float txgain;
int availability_rate;
void *user_private;
+ ftdm_timer_id_t hangup_timer;
#ifdef FTDM_DEBUG_DTMF
ftdm_dtmf_debug_t dtmfdbg;
#endif
#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);
* \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);