From 22e8a44278e6581f2eda05189d92cc7760935f72 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Thu, 27 May 2010 15:16:03 -0400 Subject: [PATCH] freetdm: run sched in the background if requested --- libs/freetdm/sample/sched/ftdmsched | Bin 0 -> 10096 bytes libs/freetdm/sample/sched/ftdmsched.c | 112 +++++++++++++ libs/freetdm/src/ftdm_io.c | 11 ++ libs/freetdm/src/ftdm_sched.c | 156 ++++++++++++++++-- libs/freetdm/src/include/ftdm_threadmutex.h | 1 - libs/freetdm/src/include/private/ftdm_sched.h | 11 +- 6 files changed, 279 insertions(+), 12 deletions(-) create mode 100755 libs/freetdm/sample/sched/ftdmsched create mode 100644 libs/freetdm/sample/sched/ftdmsched.c diff --git a/libs/freetdm/sample/sched/ftdmsched b/libs/freetdm/sample/sched/ftdmsched new file mode 100755 index 0000000000000000000000000000000000000000..21ac8e52d56163c9015705cbe3a387d90f255fae GIT binary patch literal 10096 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 literal 0 HcmV?d00001 diff --git a/libs/freetdm/sample/sched/ftdmsched.c b/libs/freetdm/sample/sched/ftdmsched.c new file mode 100644 index 0000000000..5cd7d6340a --- /dev/null +++ b/libs/freetdm/sample/sched/ftdmsched.c @@ -0,0 +1,112 @@ +#define _GNU_SOURCE +#include +#include +#include +#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; +} + diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index e461d14f96..3dcca80ab3 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -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); diff --git a/libs/freetdm/src/ftdm_sched.c b/libs/freetdm/src/ftdm_sched.c index 17d5406a3d..10332a0787 100644 --- a/libs/freetdm/src/ftdm_sched.c +++ b/libs/freetdm/src/ftdm_sched.c @@ -34,9 +34,19 @@ #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); diff --git a/libs/freetdm/src/include/ftdm_threadmutex.h b/libs/freetdm/src/include/ftdm_threadmutex.h index 4d0030e260..c5afb46eb5 100644 --- a/libs/freetdm/src/include/ftdm_threadmutex.h +++ b/libs/freetdm/src/include/ftdm_threadmutex.h @@ -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); diff --git a/libs/freetdm/src/include/private/ftdm_sched.h b/libs/freetdm/src/include/private/ftdm_sched.h index 214fbefe1c..0951d050a7 100644 --- a/libs/freetdm/src/include/private/ftdm_sched.h +++ b/libs/freetdm/src/include/private/ftdm_sched.h @@ -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