aboutsummaryrefslogtreecommitdiffstats
path: root/res/res_timing_timerfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'res/res_timing_timerfd.c')
-rw-r--r--res/res_timing_timerfd.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/res/res_timing_timerfd.c b/res/res_timing_timerfd.c
new file mode 100644
index 000000000..85af3cc8f
--- /dev/null
+++ b/res/res_timing_timerfd.c
@@ -0,0 +1,271 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2008, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \author Mark Michelson <mmichelson@digium.com>
+ *
+ * \brief timerfd timing interface
+ */
+
+/*** MODULEINFO
+ <depend>timerfd</depend>
+ <conflict>res_timing_pthread</conflict>
+ <conflict>res_timing_dahdi</conflict>
+ ***/
+
+#include "asterisk.h"
+
+#include <sys/timerfd.h>
+
+#include "asterisk/module.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/timing.h"
+#include "asterisk/logger.h"
+#include "asterisk/utils.h"
+#include "asterisk/time.h"
+
+static void *timing_funcs_handle;
+
+static int timerfd_timer_open(void);
+static void timerfd_timer_close(int handle);
+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 unsigned int timerfd_timer_get_max_rate(int handle);
+
+static struct ast_timing_functions timerfd_timing_functions = {
+ .timer_open = timerfd_timer_open,
+ .timer_close = timerfd_timer_close,
+ .timer_set_rate = timerfd_timer_set_rate,
+ .timer_ack = timerfd_timer_ack,
+ .timer_enable_continuous = timerfd_timer_enable_continuous,
+ .timer_disable_continuous = timerfd_timer_disable_continuous,
+ .timer_get_event = timerfd_timer_get_event,
+ .timer_get_max_rate = timerfd_timer_get_max_rate,
+};
+
+static struct ao2_container *timerfd_timers;
+
+#define TIMERFD_TIMER_BUCKETS 563
+#define TIMERFD_MAX_RATE 1000
+
+struct timerfd_timer {
+ int handle;
+ struct itimerspec saved_timer;
+ unsigned int is_continuous:1;
+};
+
+static int timerfd_timer_hash(const void *obj, const int flags)
+{
+ const struct timerfd_timer *timer = obj;
+
+ return timer->handle;
+}
+
+static int timerfd_timer_cmp(void *obj, void *args, void *data, int flags)
+{
+ struct timerfd_timer *timer1 = obj, *timer2 = args;
+ return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void timer_destroy(void *obj)
+{
+ struct timerfd_timer *timer = obj;
+ close(timer->handle);
+}
+
+static int timerfd_timer_open(void)
+{
+ struct timerfd_timer *timer;
+ int handle;
+
+ if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
+ ast_log(LOG_ERROR, "Could not allocate memory for timerfd_timer structure\n");
+ return -1;
+ }
+ if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
+ ast_log(LOG_ERROR, "Failed to create timerfd timer: %s\n", strerror(errno));
+ ao2_ref(timer, -1);
+ return -1;
+ }
+
+ timer->handle = handle;
+ ao2_link(timerfd_timers, timer);
+ /* Get rid of the reference from the allocation */
+ ao2_ref(timer, -1);
+ return handle;
+}
+
+static void timerfd_timer_close(int handle)
+{
+ struct timerfd_timer *our_timer, find_helper = {
+ .handle = handle,
+ };
+
+ if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
+ ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
+ return;
+ }
+
+ ao2_unlink(timerfd_timers, our_timer);
+ ao2_ref(our_timer, -1);
+}
+
+static int timerfd_timer_set_rate(int handle, unsigned int rate)
+{
+ struct itimerspec itspec;
+ itspec.it_value.tv_sec = 0;
+ itspec.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
+ itspec.it_interval.tv_sec = itspec.it_value.tv_sec;
+ itspec.it_interval.tv_nsec = itspec.it_value.tv_nsec;
+
+ return timerfd_settime(handle, 0, &itspec, NULL);
+}
+
+static void timerfd_timer_ack(int handle, unsigned int quantity)
+{
+ uint64_t expirations;
+ int read_result = 0;
+
+ do {
+ read_result = read(handle, &expirations, sizeof(expirations));
+ if (read_result == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ ast_log(LOG_ERROR, "Read error: %s\n", strerror(errno));
+ break;
+ }
+ }
+ } while (read_result != sizeof(expirations));
+
+ if (expirations != quantity) {
+ ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, expirations);
+ }
+}
+
+static int timerfd_timer_enable_continuous(int handle)
+{
+ int res;
+ struct itimerspec continuous_timer = {
+ .it_value.tv_nsec = 1L;
+ };
+ struct timerfd_timer *our_timer, find_helper = {
+ .handle = handle,
+ };
+
+ if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
+ ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
+ return -1;
+ }
+
+ if (our_timer->is_continuous) {
+ /*It's already in continous mode, no need to do
+ * anything further
+ */
+ ao2_ref(our_timer, -1);
+ return 0;
+ }
+
+ res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
+ our_timer->is_continuous = 1;
+ ao2_ref(our_timer, -1);
+ return res;
+}
+
+static int timerfd_timer_disable_continuous(int handle)
+{
+ int res;
+ struct timerfd_timer *our_timer, find_helper = {
+ .handle = handle,
+ };
+
+ if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
+ ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
+ return -1;
+ }
+
+ if(!our_timer->is_continuous) {
+ /* No reason to do anything if we're not
+ * in continuous mode
+ */
+ ao2_ref(our_timer, -1);
+ return 0;
+ }
+
+ res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
+ our_timer->is_continuous = 0;
+ memset(&our_timer->saved_timer, 0, sizeof(our_timer->saved_timer));
+ ao2_ref(our_timer, -1);
+ return res;
+}
+
+static enum ast_timing_event timerfd_timer_get_event(int handle)
+{
+ enum ast_timing_event res;
+ struct timerfd_timer *our_timer, find_helper = {
+ .handle = handle,
+ };
+
+ if (!(our_timer = ao2_find(timerfd_timers, &find_helper, NULL, OBJ_POINTER))) {
+ ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
+ return -1;
+ }
+
+ if (our_timer->is_continuous) {
+ res = AST_TIMING_EVENT_CONTINUOUS;
+ } else {
+ res = AST_TIMING_EVENT_EXPIRED;
+ }
+
+ ao2_ref(our_timer, -1);
+ return res;
+}
+
+static unsigned int timerfd_timer_get_max_rate(int handle)
+{
+ return TIMERFD_MAX_RATE;
+}
+
+static int load_module(void)
+{
+ if (!(timerfd_timers = ao2_container_alloc(TIMERFD_TIMER_BUCKETS, timerfd_timer_hash, timerfd_timer_cmp))) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!(timing_funcs_handle = ast_install_timing_functions(&timerfd_timing_functions))) {
+ ao2_ref(timerfd_timers, -1);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+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;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Timerfd Timing Interface");