freetdm: run sched in the background if requested
This commit is contained in:
parent
48c7bb320e
commit
22e8a44278
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue