]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
freetdm: add force hangup timer and refactor scheduling code
authorMoises Silva <moy@sangoma.com>
Tue, 21 Sep 2010 11:19:56 +0000 (07:19 -0400)
committerMoises Silva <moy@sangoma.com>
Tue, 21 Sep 2010 11:19:56 +0000 (07:19 -0400)
libs/freetdm/src/ftdm_io.c
libs/freetdm/src/ftdm_sched.c
libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h
libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c
libs/freetdm/src/include/private/ftdm_core.h
libs/freetdm/src/include/private/ftdm_sched.h

index 6991e9ab9b87a4cbf0fdd66eaeb323a5bef0acfd..84ce1ac521fa45b6f3e568f754a1cb11df80cd85 100644 (file)
@@ -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;
index 5aacc202bf83f460475e4a550f04949b391c85b1..340f442c64167b6cb8d73ef298c736f29bad9ecc 100644 (file)
@@ -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);
+       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");
@@ -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");
-
-       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);
 
index cd96a18fce58fa500b334891092bb02f10f0736c..ae6c0d92f7bc70511527267fa357a42c42928482 100644 (file)
@@ -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 */
index e2e67eed1929cdf46ead4aea47140b49490e99c8..d63ee4cdfebd7eea0db82c44b474fce5ca0aec17 100644 (file)
@@ -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);
index d8947f6de4d521d6ad1a334b524d68c29790f49f..a701c1383d740e96981cf05b2ef271f46674b9d6 100644 (file)
@@ -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
index 0951d050a70cba2c47a4a3482f88b41074559518..9a222896f5ea1e00615f8485c61fac8eeec81191 100644 (file)
@@ -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);