--- /dev/null
+#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;
+}
+
#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 {
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;
goto failed;
}
+ ftdm_set_string(newsched->name, name);
+
*sched = newsched;
+ ftdm_log(FTDM_LOG_DEBUG, "Created schedule %s\n", name);
return FTDM_SUCCESS;
failed:
#endif
ftdm_timer_t *current = NULL;
ftdm_timer_t *winner = NULL;
-
+
+ /* forever by default */
*timeto = -1;
#ifndef __linux__
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;
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;
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);