From 22e8a44278e6581f2eda05189d92cc7760935f72 Mon Sep 17 00:00:00 2001
From: Moises Silva <moy@sangoma.com>
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 <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;
+}
+
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