diff --git a/include/asterisk/timing.h b/include/asterisk/timing.h index 413e22031fd0f64995eec0c58c6fff67d07fb31d..91e1e83eac22ce2787b24181ccabaded72781f4e 100644 --- a/include/asterisk/timing.h +++ b/include/asterisk/timing.h @@ -1,9 +1,10 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2008, Digium, Inc. + * Copyright (C) 2008 - 2009, Digium, Inc. * * Kevin P. Fleming <kpfleming@digium.com> + * Russell Bryant <russell@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -20,6 +21,7 @@ \file timing.h \brief Timing source management \author Kevin P. Fleming <kpfleming@digium.com> + \author Russell Bryant <russell@digium.com> Portions of Asterisk require a timing source, a periodic trigger for media handling activities. The functions in this file allow @@ -55,7 +57,7 @@ extern "C" { #endif -enum ast_timing_event { +enum ast_timer_event { AST_TIMING_EVENT_EXPIRED = 1, AST_TIMING_EVENT_CONTINUOUS = 2, }; @@ -67,37 +69,44 @@ enum ast_timing_event { * So, the behavior of these calls should match the documentation of the * public API calls. */ -struct ast_timing_functions { +struct ast_timing_interface { + const char *name; + /*! This handles the case where multiple timing modules are loaded. + * The highest priority timing interface available will be used. */ + unsigned int priority; int (*timer_open)(void); void (*timer_close)(int handle); int (*timer_set_rate)(int handle, unsigned int rate); void (*timer_ack)(int handle, unsigned int quantity); int (*timer_enable_continuous)(int handle); int (*timer_disable_continuous)(int handle); - enum ast_timing_event (*timer_get_event)(int handle); + enum ast_timer_event (*timer_get_event)(int handle); unsigned int (*timer_get_max_rate)(int handle); }; /*! - * \brief Install a set of timing functions. + * \brief Register a set of timing functions. * - * \param funcs An instance of the \c ast_timing_functions structure with pointers + * \param funcs An instance of the \c ast_timing_interfaces structure with pointers * to the functions provided by the timing implementation. * * \retval NULL failure - * \retval non-Null handle to be passed to ast_uninstall_timing_functions() on success + * \retval non-Null handle to be passed to ast_unregister_timing_interface() on success */ -void *ast_install_timing_functions(struct ast_timing_functions *funcs); +#define ast_register_timing_interface(i) _ast_register_timing_interface(i, ast_module_info->self) +void *_ast_register_timing_interface(struct ast_timing_interface *funcs, + struct ast_module *mod); /*! - * \brief Uninstall a previously-installed set of timing functions. + * \brief Unregister a previously registered timing interface. * * \param handle The handle returned from a prior successful call to - * ast_install_timing_functions(). + * ast_register_timing_interface(). * - * \return nothing + * \retval 0 success + * \retval non-zero failure */ -void ast_uninstall_timing_functions(void *handle); +int ast_unregister_timing_interface(void *handle); /*! * \brief Open a timing fd @@ -177,7 +186,7 @@ int ast_timer_disable_continuous(int handle); * * \return which event triggered the timing fd */ -enum ast_timing_event ast_timer_get_event(int handle); +enum ast_timer_event ast_timer_get_event(int handle); /*! * \brief Get maximum rate supported for a timing handle diff --git a/main/channel.c b/main/channel.c index 344b3a9f184ccd6c24ca7f5b536f09767abaa755..3c36c51b72c2bf7e35a2aca4555c0dd71f205fd4 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2499,7 +2499,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) } if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD) { - enum ast_timing_event res; + enum ast_timer_event res; ast_clear_flag(chan, AST_FLAG_EXCEPTION); diff --git a/main/timing.c b/main/timing.c index 982186706713f455e85018596fa465a08a034d06..fbc2f21d8b01497e4c674390bb39a6229cdbd53d 100644 --- a/main/timing.c +++ b/main/timing.c @@ -1,9 +1,10 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2008, Digium, Inc. + * Copyright (C) 2008 - 2009, Digium, Inc. * * Kevin P. Fleming <kpfleming@digium.com> + * Russell Bryant <russell@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -21,6 +22,7 @@ * \brief Timing source management * * \author Kevin P. Fleming <kpfleming@digium.com> + * \author Russell Bryant <russell@digium.com> */ #include "asterisk.h" @@ -34,13 +36,37 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/utils.h" #include "asterisk/time.h" +#include "asterisk/heap.h" +#include "asterisk/module.h" + +struct timing_holder { + /*! Do _not_ move this from the beginning of the struct. */ + ssize_t __heap_index; + struct ast_module *mod; + struct ast_timing_interface *iface; +}; -AST_RWLOCK_DEFINE_STATIC(lock); +static struct ast_heap *timing_interfaces; -static struct ast_timing_functions timer_funcs; +static int timing_holder_cmp(void *_h1, void *_h2) +{ + struct timing_holder *h1 = _h1; + struct timing_holder *h2 = _h2; + + if (h1->iface->priority > h2->iface->priority) { + return 1; + } else if (h1->iface->priority == h2->iface->priority) { + return 0; + } else { + return -1; + } +} -void *ast_install_timing_functions(struct ast_timing_functions *funcs) +void *_ast_register_timing_interface(struct ast_timing_interface *funcs, + struct ast_module *mod) { + struct timing_holder *h; + if (!funcs->timer_open || !funcs->timer_close || !funcs->timer_set_rate || @@ -52,162 +78,158 @@ void *ast_install_timing_functions(struct ast_timing_functions *funcs) return NULL; } - ast_rwlock_wrlock(&lock); - - if (timer_funcs.timer_open) { - ast_rwlock_unlock(&lock); - ast_log(LOG_NOTICE, "Multiple timing modules are loaded. You should only load one.\n"); + if (!(h = ast_calloc(1, sizeof(*h)))) { return NULL; } - - timer_funcs = *funcs; - ast_rwlock_unlock(&lock); + h->iface = funcs; + h->mod = mod; - return &timer_funcs; + ast_heap_wrlock(timing_interfaces); + ast_heap_push(timing_interfaces, h); + ast_heap_unlock(timing_interfaces); + + return h; } -void ast_uninstall_timing_functions(void *handle) +int ast_unregister_timing_interface(void *handle) { - ast_rwlock_wrlock(&lock); + struct timing_holder *h = handle; + int res = -1; - if (handle != &timer_funcs) { - ast_rwlock_unlock(&lock); - return; - } + ast_heap_wrlock(timing_interfaces); + h = ast_heap_remove(timing_interfaces, h); + ast_heap_unlock(timing_interfaces); - memset(&timer_funcs, 0, sizeof(timer_funcs)); + if (h) { + ast_free(h); + h = NULL; + res = 0; + } - ast_rwlock_unlock(&lock); + return res; } int ast_timer_open(void) { - int timer; + int fd = -1; + struct timing_holder *h; - ast_rwlock_rdlock(&lock); + ast_heap_rdlock(timing_interfaces); - if (!timer_funcs.timer_open) { - ast_rwlock_unlock(&lock); - return -1; + if ((h = ast_heap_peek(timing_interfaces, 1))) { + fd = h->iface->timer_open(); + ast_module_ref(h->mod); } - timer = timer_funcs.timer_open(); - - ast_rwlock_unlock(&lock); + ast_heap_unlock(timing_interfaces); - return timer; + return fd; } void ast_timer_close(int timer) { - ast_rwlock_rdlock(&lock); + struct timing_holder *h; - if (!timer_funcs.timer_close) { - ast_rwlock_unlock(&lock); - return; - } + ast_heap_rdlock(timing_interfaces); - timer_funcs.timer_close(timer); + if ((h = ast_heap_peek(timing_interfaces, 1))) { + h->iface->timer_close(timer); + ast_module_unref(h->mod); + } - ast_rwlock_unlock(&lock); + ast_heap_unlock(timing_interfaces); } int ast_timer_set_rate(int handle, unsigned int rate) { - int res; + struct timing_holder *h; + int res = -1; - ast_rwlock_rdlock(&lock); + ast_heap_rdlock(timing_interfaces); - if (!timer_funcs.timer_set_rate) { - ast_rwlock_unlock(&lock); - return -1; + if ((h = ast_heap_peek(timing_interfaces, 1))) { + res = h->iface->timer_set_rate(handle, rate); } - res = timer_funcs.timer_set_rate(handle, rate); - - ast_rwlock_unlock(&lock); + ast_heap_unlock(timing_interfaces); return res; } void ast_timer_ack(int handle, unsigned int quantity) { - ast_rwlock_rdlock(&lock); + struct timing_holder *h; - if (!timer_funcs.timer_ack) { - ast_rwlock_unlock(&lock); - return; - } + ast_heap_rdlock(timing_interfaces); - timer_funcs.timer_ack(handle, quantity); + if ((h = ast_heap_peek(timing_interfaces, 1))) { + h->iface->timer_ack(handle, quantity); + } - ast_rwlock_unlock(&lock); + ast_heap_unlock(timing_interfaces); } int ast_timer_enable_continuous(int handle) { - int result; + struct timing_holder *h; + int res = -1; - ast_rwlock_rdlock(&lock); + ast_heap_rdlock(timing_interfaces); - if (!timer_funcs.timer_enable_continuous) { - ast_rwlock_unlock(&lock); - return -1; + if ((h = ast_heap_peek(timing_interfaces, 1))) { + res = h->iface->timer_enable_continuous(handle); } - result = timer_funcs.timer_enable_continuous(handle); + ast_heap_unlock(timing_interfaces); - ast_rwlock_unlock(&lock); - - return result; + return res; } int ast_timer_disable_continuous(int handle) { - int result; + struct timing_holder *h; + int res = -1; - ast_rwlock_rdlock(&lock); + ast_heap_rdlock(timing_interfaces); - if (!timer_funcs.timer_disable_continuous) { - ast_rwlock_unlock(&lock); - return -1; + if ((h = ast_heap_peek(timing_interfaces, 1))) { + res = h->iface->timer_disable_continuous(handle); } - result = timer_funcs.timer_disable_continuous(handle); - - ast_rwlock_unlock(&lock); + ast_heap_unlock(timing_interfaces); - return result; + return res; } -enum ast_timing_event ast_timer_get_event(int handle) +enum ast_timer_event ast_timer_get_event(int handle) { - enum ast_timing_event result; + struct timing_holder *h; + enum ast_timer_event res = -1; - ast_rwlock_rdlock(&lock); + ast_heap_rdlock(timing_interfaces); - if (!timer_funcs.timer_get_event) { - ast_rwlock_unlock(&lock); - return -1; + if ((h = ast_heap_peek(timing_interfaces, 1))) { + res = h->iface->timer_get_event(handle); } - result = timer_funcs.timer_get_event(handle); + ast_heap_unlock(timing_interfaces); - ast_rwlock_unlock(&lock); - - return result; + return res; } unsigned int ast_timer_get_max_rate(int handle) { - unsigned int res; + struct timing_holder *h; + unsigned int res = 0; - ast_rwlock_rdlock(&lock); + ast_heap_rdlock(timing_interfaces); - res = timer_funcs.timer_get_max_rate(handle); + if ((h = ast_heap_peek(timing_interfaces, 1))) { + res = h->iface->timer_get_max_rate(handle); + } - ast_rwlock_unlock(&lock); + ast_heap_unlock(timing_interfaces); return res; } @@ -217,6 +239,7 @@ static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args * int fd, count = 0; struct timeval start, end; unsigned int test_rate = 50; + struct timing_holder *h; switch (cmd) { case CLI_INIT: @@ -242,13 +265,20 @@ static char *timing_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args * } } - ast_cli(a->fd, "Attempting to test a timer with %u ticks per second ...\n", test_rate); + ast_cli(a->fd, "Attempting to test a timer with %u ticks per second.\n", test_rate); if ((fd = ast_timer_open()) == -1) { ast_cli(a->fd, "Failed to open timing fd\n"); return CLI_FAILURE; } + ast_heap_rdlock(timing_interfaces); + if ((h = ast_heap_peek(timing_interfaces, 1))) { + ast_cli(a->fd, "Using the '%s' timing module for this test.\n", h->iface->name); + h = NULL; + } + ast_heap_unlock(timing_interfaces); + start = ast_tvnow(); ast_timer_set_rate(fd, test_rate); @@ -286,5 +316,9 @@ static struct ast_cli_entry cli_timing[] = { int ast_timing_init(void) { + if (!(timing_interfaces = ast_heap_create(2, timing_holder_cmp, 0))) { + return -1; + } + return ast_cli_register_multiple(cli_timing, ARRAY_LEN(cli_timing)); } diff --git a/res/res_timing_dahdi.c b/res/res_timing_dahdi.c index 1e1150bfec561d635f5289fa4a23784d8219f263..bb0b726794a19d8b0ca692e01134c71f2bfaead6 100644 --- a/res/res_timing_dahdi.c +++ b/res/res_timing_dahdi.c @@ -25,8 +25,6 @@ /*** MODULEINFO <depend>dahdi</depend> - <conflict>res_timing_timerfd</conflict> - <conflict>res_timing_pthread</conflict> ***/ #include "asterisk.h" @@ -52,10 +50,12 @@ static int dahdi_timer_set_rate(int handle, unsigned int rate); static void dahdi_timer_ack(int handle, unsigned int quantity); static int dahdi_timer_enable_continuous(int handle); static int dahdi_timer_disable_continuous(int handle); -static enum ast_timing_event dahdi_timer_get_event(int handle); +static enum ast_timer_event dahdi_timer_get_event(int handle); static unsigned int dahdi_timer_get_max_rate(int handle); -static struct ast_timing_functions dahdi_timing_functions = { +static struct ast_timing_interface dahdi_timing = { + .name = "DAHDI", + .priority = 100, .timer_open = dahdi_timer_open, .timer_close = dahdi_timer_close, .timer_set_rate = dahdi_timer_set_rate, @@ -112,7 +112,7 @@ static int dahdi_timer_disable_continuous(int handle) return ioctl(handle, DAHDI_TIMERPONG, &flags) ? -1 : 0; } -static enum ast_timing_event dahdi_timer_get_event(int handle) +static enum ast_timer_event dahdi_timer_get_event(int handle) { int res; int event; @@ -184,17 +184,13 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } - return (timing_funcs_handle = ast_install_timing_functions(&dahdi_timing_functions)) ? + return (timing_funcs_handle = ast_register_timing_interface(&dahdi_timing)) ? AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE; } static int unload_module(void) { - /* ast_uninstall_timing_functions(timing_funcs_handle); */ - - /* This module can not currently be unloaded. No use count handling is being done. */ - - return -1; + return ast_unregister_timing_interface(timing_funcs_handle); } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DAHDI Timing Interface"); diff --git a/res/res_timing_pthread.c b/res/res_timing_pthread.c index 0afe9c9bfd80b1153316cb69d5b741d819b7b212..a45287588f16f79f1f7a6c5cdd6e1aa04c473361 100644 --- a/res/res_timing_pthread.c +++ b/res/res_timing_pthread.c @@ -23,11 +23,6 @@ * \brief pthread timing interface */ -/*** MODULEINFO - <conflict>res_timing_timerfd</conflict> - <conflict>res_timing_dahdi</conflict> - ***/ - #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); @@ -50,10 +45,12 @@ static int pthread_timer_set_rate(int handle, unsigned int rate); static void pthread_timer_ack(int handle, unsigned int quantity); static int pthread_timer_enable_continuous(int handle); static int pthread_timer_disable_continuous(int handle); -static enum ast_timing_event pthread_timer_get_event(int handle); +static enum ast_timer_event pthread_timer_get_event(int handle); static unsigned int pthread_timer_get_max_rate(int handle); -static struct ast_timing_functions pthread_timing_functions = { +static struct ast_timing_interface pthread_timing = { + .name = "pthread", + .priority = 0, /* use this as a last resort */ .timer_open = pthread_timer_open, .timer_close = pthread_timer_close, .timer_set_rate = pthread_timer_set_rate, @@ -255,10 +252,10 @@ static int pthread_timer_disable_continuous(int handle) return 0; } -static enum ast_timing_event pthread_timer_get_event(int handle) +static enum ast_timer_event pthread_timer_get_event(int handle) { struct pthread_timer *timer; - enum ast_timing_event res = AST_TIMING_EVENT_EXPIRED; + enum ast_timer_event res = AST_TIMING_EVENT_EXPIRED; if (!(timer = find_timer(handle, 0))) { return res; @@ -491,22 +488,26 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } - return (timing_funcs_handle = ast_install_timing_functions(&pthread_timing_functions)) ? + return (timing_funcs_handle = ast_register_timing_interface(&pthread_timing)) ? AST_MODULE_LOAD_SUCCESS : AST_MODULE_LOAD_DECLINE; } static int unload_module(void) { -#if 0 - /* XXX code to stop the timing thread ... */ + int res; - ast_uninstall_timing_functions(timing_funcs_handle); - ao2_ref(pthread_timers, -1); -#endif + ast_mutex_lock(&timing_thread.lock); + timing_thread.stop = 1; + ast_cond_signal(&timing_thread.cond); + ast_mutex_unlock(&timing_thread.lock); + pthread_join(timing_thread.thread, NULL); - /* This module can not currently be unloaded. No use count handling is being done. */ + if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) { + ao2_ref(pthread_timers, -1); + pthread_timers = NULL; + } - return -1; + return res; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "pthread Timing Interface"); diff --git a/res/res_timing_timerfd.c b/res/res_timing_timerfd.c index 60dc2d5efb310426dc347f2372c1f085366abcc7..b1cbb04bdba73ef4852c8eb071653e1ab0797116 100644 --- a/res/res_timing_timerfd.c +++ b/res/res_timing_timerfd.c @@ -25,8 +25,6 @@ /*** MODULEINFO <depend>timerfd</depend> - <conflict>res_timing_pthread</conflict> - <conflict>res_timing_dahdi</conflict> ***/ #include "asterisk.h" @@ -48,10 +46,12 @@ static int timerfd_timer_set_rate(int handle, unsigned int rate); static void timerfd_timer_ack(int handle, unsigned int quantity); static int timerfd_timer_enable_continuous(int handle); static int timerfd_timer_disable_continuous(int handle); -static enum ast_timing_event timerfd_timer_get_event(int handle); +static enum ast_timer_event timerfd_timer_get_event(int handle); static unsigned int timerfd_timer_get_max_rate(int handle); -static struct ast_timing_functions timerfd_timing_functions = { +static struct ast_timing_interface timerfd_timing = { + .name = "timerfd", + .priority = 200, .timer_open = timerfd_timer_open, .timer_close = timerfd_timer_close, .timer_set_rate = timerfd_timer_set_rate, @@ -226,9 +226,9 @@ static int timerfd_timer_disable_continuous(int handle) return res; } -static enum ast_timing_event timerfd_timer_get_event(int handle) +static enum ast_timer_event timerfd_timer_get_event(int handle) { - enum ast_timing_event res; + enum ast_timer_event res; struct timerfd_timer *our_timer, find_helper = { .handle = handle, }; @@ -259,7 +259,7 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } - if (!(timing_funcs_handle = ast_install_timing_functions(&timerfd_timing_functions))) { + if (!(timing_funcs_handle = ast_register_timing_interface(&timerfd_timing))) { ao2_ref(timerfd_timers, -1); return AST_MODULE_LOAD_DECLINE; } @@ -269,11 +269,14 @@ static int load_module(void) static int unload_module(void) { - /* ast_uninstall_timing_functions(timing_funcs_handle); */ + int res; - /* This module can not currently be unloaded. No use count handling is being done. */ + if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) { + ao2_ref(timerfd_timers, -1); + timerfd_timers = NULL; + } - return -1; + return res; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Timerfd Timing Interface");