]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
freetdm: run sched in the background if requested
authorMoises Silva <moy@sangoma.com>
Thu, 27 May 2010 19:16:03 +0000 (15:16 -0400)
committerMoises Silva <moy@sangoma.com>
Thu, 27 May 2010 19:16:03 +0000 (15:16 -0400)
libs/freetdm/sample/sched/ftdmsched [new file with mode: 0755]
libs/freetdm/sample/sched/ftdmsched.c [new file with mode: 0644]
libs/freetdm/src/ftdm_io.c
libs/freetdm/src/ftdm_sched.c
libs/freetdm/src/include/ftdm_threadmutex.h
libs/freetdm/src/include/private/ftdm_sched.h

diff --git a/libs/freetdm/sample/sched/ftdmsched b/libs/freetdm/sample/sched/ftdmsched
new file mode 100755 (executable)
index 0000000..21ac8e5
Binary files /dev/null and b/libs/freetdm/sample/sched/ftdmsched differ
diff --git a/libs/freetdm/sample/sched/ftdmsched.c b/libs/freetdm/sample/sched/ftdmsched.c
new file mode 100644 (file)
index 0000000..5cd7d63
--- /dev/null
@@ -0,0 +1,112 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "../../src/include/private/ftdm_core.h"
+
+static int running = 1;
+
+typedef struct custom_data {
+       ftdm_timer_t *heartbeat_timer;
+       int beat;
+       int counter;
+       ftdm_sched_callback_t callback;
+       ftdm_sched_t *sched;
+} custom_data_t;
+
+void trap(int signal)
+{
+       running = 0;
+}
+
+void handle_heartbeat(void *usrdata)
+{
+       ftdm_status_t status;
+       custom_data_t *data = usrdata;
+
+       printf("beep (elapsed %dms count= %d)\n", data->beat, data->counter);
+       if (data->beat > 1000) {
+               data->beat -= 1000;
+       } else if (data->beat <= 1000 && data->beat > 200) {
+               data->beat -= 100;
+       } else if (data->beat <= 200 && data->beat > 100) {
+               if (!data->counter--) {
+                       data->counter = 5;
+                       data->beat -= 100;
+               }
+       } else if (data->beat <= 100 && data->beat > 10) {
+               if (!data->counter--) {
+                       data->counter = 10;
+                       data->beat -= 10;
+                       if (data->beat == 10) {
+                               data->counter = 200;
+                       }
+               }
+       } else {
+               if (!data->counter--) {
+                       data->counter = 5;
+                       data->beat--;
+               }
+       }
+
+       if (!data->beat) {
+               printf("beeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep you're dead!\n");
+               return;
+       }
+
+       data->heartbeat_timer = NULL;
+       status = ftdm_sched_timer(data->sched, "heartbeat", data->beat, data->callback, data, &data->heartbeat_timer);
+       if (status != FTDM_SUCCESS) {
+               fprintf(stderr, "Error creating heartbeat timer\n");
+               running = 0;
+               return;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       ftdm_status_t status;
+       custom_data_t data;
+
+       ftdm_sched_t *sched;
+       signal(SIGINT, trap);
+       
+       ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
+
+       ftdm_cpu_monitor_disable();
+
+       if (ftdm_global_init() != FTDM_SUCCESS) {
+               fprintf(stderr, "Error loading FreeTDM\n");
+               exit(-1);
+       }
+
+       status = ftdm_sched_create(&sched, "testsched");
+       if (status != FTDM_SUCCESS) {
+               fprintf(stderr, "Error creating sched\n");
+               exit(-1);
+       }
+
+       data.sched = sched;
+       data.counter = 10;
+       data.beat = 5000;
+       data.callback = handle_heartbeat;
+       status = ftdm_sched_timer(sched, "heartbeat", data.beat, data.callback, &data, &data.heartbeat_timer);
+       if (status != FTDM_SUCCESS) {
+               fprintf(stderr, "Error creating heartbeat timer\n");
+               exit(-1);
+       }
+
+       ftdm_sched_free_run(sched);
+
+       while (running) {
+               ftdm_sleep(10);
+       }
+
+       ftdm_global_destroy();
+
+       printf("Done, press any key to die!\n");
+
+       getchar();
+       return 0;
+}
+
index e461d14f96c50f638786c6e84c22298b2fe2085f..3dcca80ab39d76dc6a24921e73daba7a009347be 100644 (file)
@@ -4241,6 +4241,7 @@ FT_DECLARE(ftdm_status_t) ftdm_global_init(void)
        ftdm_mutex_create(&globals.mutex);
        ftdm_mutex_create(&globals.span_mutex);
        ftdm_mutex_create(&globals.group_mutex);
+       ftdm_sched_global_init();
        globals.running = 1;
        return FTDM_SUCCESS;
 }
@@ -4287,6 +4288,7 @@ FT_DECLARE(uint32_t) ftdm_running(void)
 FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void)
 {
        ftdm_span_t *sp;
+       uint32_t sanity = 100;
 
        time_end();
 
@@ -4320,6 +4322,15 @@ FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void)
 
        ftdm_unload_modules();
 
+       while (ftdm_free_sched_running() && --sanity) {
+               ftdm_log(FTDM_LOG_DEBUG, "Waiting for schedule thread to finish\n");
+               ftdm_sleep(100);
+       }
+
+       if (!sanity) {
+               ftdm_log(FTDM_LOG_CRIT, "schedule thread did not stop running, we may crash on shutdown\n");
+       }
+
        ftdm_mutex_lock(globals.mutex);
        hashtable_destroy(globals.interface_hash);
        hashtable_destroy(globals.module_hash);
index 17d5406a3dff7ef06a83df0083dff2f094c1574a..10332a078734bb679930bc187de041ac513282d6 100644 (file)
 
 #include "private/ftdm_core.h"
 
+static struct {
+       ftdm_sched_t *freeruns;
+       ftdm_mutex_t *mutex;
+       ftdm_bool_t running;
+} sched_globals;
+
 struct ftdm_sched {
+       char name[80];
        ftdm_mutex_t *mutex;
        ftdm_timer_t *timers;
+       int freerun;
+       ftdm_sched_t *next;
+       ftdm_sched_t *prev;
 };
 
 struct ftdm_timer {
@@ -50,11 +60,109 @@ struct ftdm_timer {
        ftdm_timer_t *prev;
 };
 
-FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched)
+/* FIXME: use ftdm_interrupt_t to wait for new schedules to monitor */
+#define SCHED_MAX_SLEEP 100
+static void *run_main_schedule(ftdm_thread_t *thread, void *data)
+{
+       int32_t timeto;
+       int32_t sleepms;
+       ftdm_status_t status;
+       ftdm_sched_t *current = NULL;
+       while (ftdm_running()) {
+               
+               sleepms = SCHED_MAX_SLEEP;
+
+               ftdm_mutex_lock(sched_globals.mutex);
+
+               if (!sched_globals.freeruns) {
+               
+                       /* there are no free runs, wait a bit and check again (FIXME: use ftdm_interrupt_t for this) */
+                       ftdm_mutex_unlock(sched_globals.mutex);
+
+                       ftdm_sleep(sleepms);
+               }
+
+               for (current = sched_globals.freeruns; current; current = current->next) {
+
+                       /* first run the schedule */
+                       ftdm_sched_run(current);
+
+                       /* now find out how much time to sleep until running them again */
+                       status = ftdm_sched_get_time_to_next_timer(current, &timeto);
+                       if (status != FTDM_SUCCESS) {
+                               ftdm_log(FTDM_LOG_WARNING, "Failed to get time to next timer for schedule %s, skipping\n", current->name);      
+                               continue;
+                       }
+
+                       /* if timeto == -1 we don't want to sleep forever, so keep the last sleepms */
+                       if (timeto != -1 && sleepms > timeto) {
+                               sleepms = timeto;
+                       }
+               }
+
+               ftdm_mutex_unlock(sched_globals.mutex);
+
+               ftdm_sleep(sleepms);
+       }
+       ftdm_log(FTDM_LOG_NOTICE, "Main scheduling thread going out ...\n");
+       sched_globals.running = 0;
+       return NULL;
+}
+
+FT_DECLARE(ftdm_status_t) ftdm_sched_global_init()
+{
+       ftdm_log(FTDM_LOG_DEBUG, "Initializing scheduling API\n");
+       memset(&sched_globals, 0, sizeof(sched_globals));
+       if (ftdm_mutex_create(&sched_globals.mutex) == FTDM_SUCCESS) {
+               return FTDM_SUCCESS;
+       }
+       return FTDM_FAIL;
+}
+
+FT_DECLARE(ftdm_status_t) ftdm_sched_free_run(ftdm_sched_t *sched)
+{
+       ftdm_status_t status;
+       ftdm_assert_return(sched != NULL, FTDM_EINVAL, "invalid pointer\n");
+
+       ftdm_mutex_lock(sched_globals.mutex);
+
+       if (sched_globals.running == FTDM_FALSE) {
+               ftdm_log(FTDM_LOG_NOTICE, "Launching main schedule thread\n");
+               status = ftdm_thread_create_detached(run_main_schedule, NULL);
+               if (status != FTDM_SUCCESS) {
+                       ftdm_log(FTDM_LOG_CRIT, "Failed to launch main schedule thread\n");
+                       goto done;
+               } 
+               sched_globals.running = FTDM_TRUE;
+       }
+
+       ftdm_log(FTDM_LOG_DEBUG, "Running schedule %s in the main schedule thread\n", sched->name);
+       
+       /* Add the schedule to the global list of free runs */
+       if (!sched_globals.freeruns) {
+               sched_globals.freeruns = sched;
+       }  else {
+               sched->next = sched_globals.freeruns;
+               sched_globals.freeruns->prev = sched;
+               sched_globals.freeruns = sched;
+       }
+
+done:
+       ftdm_mutex_unlock(sched_globals.mutex);
+       return status;
+}
+
+FT_DECLARE(ftdm_bool_t) ftdm_free_sched_running(void)
+{
+       return sched_globals.running;
+}
+
+FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched, const char *name)
 {
        ftdm_sched_t *newsched = NULL;
 
-       ftdm_assert_return(sched != NULL, FTDM_EINVAL, "invalid pointer");
+       ftdm_assert_return(sched != NULL, FTDM_EINVAL, "invalid pointer\n");
+       ftdm_assert_return(name != NULL, FTDM_EINVAL, "invalid sched name\n");
 
        *sched = NULL;
 
@@ -67,7 +175,10 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched)
                goto failed;
        }
 
+       ftdm_set_string(newsched->name, name);
+
        *sched = newsched;
+       ftdm_log(FTDM_LOG_DEBUG, "Created schedule %s\n", name);
        return FTDM_SUCCESS;
 
 failed:
@@ -235,7 +346,8 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_get_time_to_next_timer(const ftdm_sched_t *
 #endif
        ftdm_timer_t *current = NULL;
        ftdm_timer_t *winner = NULL;
-
+       
+       /* forever by default */
        *timeto = -1;
 
 #ifndef __linux__
@@ -299,21 +411,24 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_cancel_timer(ftdm_sched_t *sched, ftdm_time
 
        ftdm_mutex_lock(sched->mutex);
 
+       /* special case where the cancelled timer is the head */
        if (*intimer == sched->timers) {
-               timer = sched->timers;
-               if (timer->next) {
-                       sched->timers = timer->next;
+               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;
-               } else {
-                       sched->timers = NULL;
                }
+               /* free the old head */
                ftdm_safe_free(timer);
                status = FTDM_SUCCESS;
                *intimer = NULL;
                goto done;
        }
 
-       for (timer = sched->timers; timer; timer = timer->next) {
+       /* 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) {
                                timer->prev->next = timer->next;
@@ -347,7 +462,26 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **insched)
        ftdm_assert_return(*insched != NULL, FTDM_EINVAL, "sched is null!\n");
 
        sched = *insched;
-       
+
+       /* since destroying a sched may affect the global list, we gotta check */       
+       ftdm_mutex_lock(sched_globals.mutex);
+
+       /* if we're head, replace head with our next (whatever our next is, even null will do) */
+       if (sched == sched_globals.freeruns) {
+               sched_globals.freeruns = sched->next;
+       }
+       /* if we have a previous member (then we were not head) set our previous next to our next */
+       if (sched->prev) {
+               sched->prev->next = sched->next;
+       }
+       /* if we have a next then set their prev to our prev (if we were head prev will be null and sched->next is already the new head) */
+       if (sched->next) {
+               sched->next->prev = sched->prev;
+       }
+
+       ftdm_mutex_unlock(sched_globals.mutex);
+
+       /* now grab the sched mutex */
        ftdm_mutex_lock(sched->mutex);
 
        timer = sched->timers;
@@ -357,6 +491,8 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **insched)
                ftdm_safe_free(deltimer);
        }
 
+       ftdm_log(FTDM_LOG_DEBUG, "Destroying schedule %s\n", sched->name);
+
        ftdm_mutex_unlock(sched->mutex);
 
        ftdm_mutex_destroy(&sched->mutex);
index 4d0030e26026f8150c580199b9f3dad0eb204219..c5afb46eb55ccde6227704f16018cd63f362799f 100644 (file)
@@ -40,7 +40,6 @@ FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached(ftdm_thread_function_t fun
 FT_DECLARE(ftdm_status_t) ftdm_thread_create_detached_ex(ftdm_thread_function_t func, void *data, ftdm_size_t stack_size);
 FT_DECLARE(void) ftdm_thread_override_default_stacksize(ftdm_size_t size);
 
-
 FT_DECLARE(ftdm_status_t) ftdm_mutex_create(ftdm_mutex_t **mutex);
 FT_DECLARE(ftdm_status_t) ftdm_mutex_destroy(ftdm_mutex_t **mutex);
 
index 214fbefe1c203ed35bddb8ef6adff235966c3af7..0951d050a70cba2c47a4a3482f88b41074559518 100644 (file)
@@ -48,11 +48,14 @@ typedef struct ftdm_timer ftdm_timer_t;
 typedef void (*ftdm_sched_callback_t)(void *data);
 
 /*! \brief Create a new scheduling context */
-FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched);
+FT_DECLARE(ftdm_status_t) ftdm_sched_create(ftdm_sched_t **sched, const char *name);
 
 /*! \brief Run the schedule to find timers that are expired and run its callbacks */
 FT_DECLARE(ftdm_status_t) ftdm_sched_run(ftdm_sched_t *sched);
 
+/*! \brief Run the schedule in its own thread. Callbacks will be called in a core thread. You *must* not block there! */
+FT_DECLARE(ftdm_status_t) ftdm_sched_free_run(ftdm_sched_t *sched);
+
 /*! 
  * \brief Schedule a new timer 
  * \param sched The scheduling context (required)
@@ -82,6 +85,12 @@ FT_DECLARE(ftdm_status_t) ftdm_sched_destroy(ftdm_sched_t **sched);
  */
 FT_DECLARE(ftdm_status_t) ftdm_sched_get_time_to_next_timer(const ftdm_sched_t *sched, int32_t *timeto);
 
+/*! \brief Global initialization, called just once, this is called by FreeTDM core, other users MUST not call it */
+FT_DECLARE(ftdm_status_t) ftdm_sched_global_init(void);
+
+/*! \brief Checks if the main scheduling thread is running */
+FT_DECLARE(ftdm_bool_t) ftdm_free_sched_running(void);
+
 #ifdef __cplusplus
 } 
 #endif