timing: Improve performance for most timing implementations.

This change allows timing implementation data to be stored directly
on the timer itself thus removing the requirement for many
implementations to do a container lookup for the same information.

This means that API calls into timing implementations can directly
access the information they need instead of having to find it.

Review: https://reviewboard.asterisk.org/r/3175/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@407749 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/97/197/1
Joshua Colp 12 years ago
parent 5fd63e2d0b
commit e8e2f91bba

@ -18,33 +18,33 @@
*/
/*!
\file timing.h
\brief Timing source management
\author Kevin P. Fleming <kpfleming@digium.com>
\author Russell Bryant <russell@digium.com>
\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
a loadable module to provide a timing source for Asterisk and its
modules, so that those modules can request a 'timing handle' when
they require one. These handles are file descriptors, which can be
used with select() or poll().
Portions of Asterisk require a timing source, a periodic trigger
for media handling activities. The functions in this file allow
a loadable module to provide a timing source for Asterisk and its
modules, so that those modules can request a 'timing handle' when
they require one. These handles are file descriptors, which can be
used with select() or poll().
The timing source used by Asterisk must provide the following
features:
The timing source used by Asterisk must provide the following
features:
1) Periodic triggers, with a configurable interval (specified as
number of triggers per second).
1) Periodic triggers, with a configurable interval (specified as
number of triggers per second).
2) Multiple outstanding triggers, each of which must be 'acked'
to clear it. Triggers must also be 'ackable' in quantity.
2) Multiple outstanding triggers, each of which must be 'acked'
to clear it. Triggers must also be 'ackable' in quantity.
3) Continuous trigger mode, which when enabled causes every call
to poll() on the timer handle to immediately return.
3) Continuous trigger mode, which when enabled causes every call
to poll() on the timer handle to immediately return.
4) Multiple 'event types', so that the code using the timer can
know whether the wakeup it received was due to a periodic trigger
or a continuous trigger.
4) Multiple 'event types', so that the code using the timer can
know whether the wakeup it received was due to a periodic trigger
or a continuous trigger.
*/
#ifndef _ASTERISK_TIMING_H
@ -71,14 +71,15 @@ struct ast_timing_interface {
/*! 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);
int (*timer_ack)(int handle, unsigned int quantity);
int (*timer_enable_continuous)(int handle);
int (*timer_disable_continuous)(int handle);
enum ast_timer_event (*timer_get_event)(int handle);
unsigned int (*timer_get_max_rate)(int handle);
void *(*timer_open)(void);
void (*timer_close)(void *data);
int (*timer_set_rate)(void *data, unsigned int rate);
int (*timer_ack)(void *data, unsigned int quantity);
int (*timer_enable_continuous)(void *data);
int (*timer_disable_continuous)(void *data);
enum ast_timer_event (*timer_get_event)(void *data);
unsigned int (*timer_get_max_rate)(void *data);
int (*timer_fd)(void *data);
};
/*!
@ -93,7 +94,7 @@ struct ast_timing_interface {
*/
#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);
struct ast_module *mod);
/*!
* \brief Unregister a previously registered timing interface.

@ -54,7 +54,7 @@ struct timing_holder {
static struct ast_heap *timing_interfaces;
struct ast_timer {
int fd;
void *data;
struct timing_holder *holder;
};
@ -84,7 +84,8 @@ void *_ast_register_timing_interface(struct ast_timing_interface *funcs,
!funcs->timer_get_event ||
!funcs->timer_get_max_rate ||
!funcs->timer_enable_continuous ||
!funcs->timer_disable_continuous) {
!funcs->timer_disable_continuous ||
!funcs->timer_fd) {
return NULL;
}
@ -122,22 +123,22 @@ int ast_unregister_timing_interface(void *handle)
struct ast_timer *ast_timer_open(void)
{
int fd = -1;
void *data = NULL;
struct timing_holder *h;
struct ast_timer *t = NULL;
ast_heap_rdlock(timing_interfaces);
if ((h = ast_heap_peek(timing_interfaces, 1))) {
fd = h->iface->timer_open();
data = h->iface->timer_open();
ast_module_ref(h->mod);
}
if (fd != -1) {
if (data) {
if (!(t = ast_calloc(1, sizeof(*t)))) {
h->iface->timer_close(fd);
h->iface->timer_close(data);
} else {
t->fd = fd;
t->data = data;
t->holder = h;
}
}
@ -149,69 +150,44 @@ struct ast_timer *ast_timer_open(void)
void ast_timer_close(struct ast_timer *handle)
{
handle->holder->iface->timer_close(handle->fd);
handle->fd = -1;
handle->holder->iface->timer_close(handle->data);
ast_module_unref(handle->holder->mod);
ast_free(handle);
}
int ast_timer_fd(const struct ast_timer *handle)
{
return handle->fd;
return handle->holder->iface->timer_fd(handle->data);
}
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
{
int res = -1;
res = handle->holder->iface->timer_set_rate(handle->fd, rate);
return res;
return handle->holder->iface->timer_set_rate(handle->data, rate);
}
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
{
int res = -1;
res = handle->holder->iface->timer_ack(handle->fd, quantity);
return res;
return handle->holder->iface->timer_ack(handle->data, quantity);
}
int ast_timer_enable_continuous(const struct ast_timer *handle)
{
int res = -1;
res = handle->holder->iface->timer_enable_continuous(handle->fd);
return res;
return handle->holder->iface->timer_enable_continuous(handle->data);
}
int ast_timer_disable_continuous(const struct ast_timer *handle)
{
int res = -1;
res = handle->holder->iface->timer_disable_continuous(handle->fd);
return res;
return handle->holder->iface->timer_disable_continuous(handle->data);
}
enum ast_timer_event ast_timer_get_event(const struct ast_timer *handle)
{
enum ast_timer_event res = -1;
res = handle->holder->iface->timer_get_event(handle->fd);
return res;
return handle->holder->iface->timer_get_event(handle->data);
}
unsigned int ast_timer_get_max_rate(const struct ast_timer *handle)
{
unsigned int res = 0;
res = handle->holder->iface->timer_get_max_rate(handle->fd);
return res;
return handle->holder->iface->timer_get_max_rate(handle->data);
}
const char *ast_timer_get_name(const struct ast_timer *handle)

@ -45,14 +45,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
static void *timing_funcs_handle;
static int dahdi_timer_open(void);
static void dahdi_timer_close(int handle);
static int dahdi_timer_set_rate(int handle, unsigned int rate);
static int 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_timer_event dahdi_timer_get_event(int handle);
static unsigned int dahdi_timer_get_max_rate(int handle);
static void *dahdi_timer_open(void);
static void dahdi_timer_close(void *data);
static int dahdi_timer_set_rate(void *data, unsigned int rate);
static int dahdi_timer_ack(void *data, unsigned int quantity);
static int dahdi_timer_enable_continuous(void *data);
static int dahdi_timer_disable_continuous(void *data);
static enum ast_timer_event dahdi_timer_get_event(void *data);
static unsigned int dahdi_timer_get_max_rate(void *data);
static int dahdi_timer_fd(void *data);
static struct ast_timing_interface dahdi_timing = {
.name = "DAHDI",
@ -65,27 +66,48 @@ static struct ast_timing_interface dahdi_timing = {
.timer_disable_continuous = dahdi_timer_disable_continuous,
.timer_get_event = dahdi_timer_get_event,
.timer_get_max_rate = dahdi_timer_get_max_rate,
.timer_fd = dahdi_timer_fd,
};
static int dahdi_timer_open(void)
struct dahdi_timer {
int fd;
};
static void *dahdi_timer_open(void)
{
return open("/dev/dahdi/timer", O_RDWR);
struct dahdi_timer *timer;
if (!(timer = ast_calloc(1, sizeof(*timer)))) {
return NULL;
}
if ((timer->fd = open("/dev/dahdi/timer", O_RDWR)) < 0) {
ast_log(LOG_ERROR, "Failed to create dahdi timer: %s\n", strerror(errno));
ast_free(timer);
return NULL;
}
return timer;
}
static void dahdi_timer_close(int handle)
static void dahdi_timer_close(void *data)
{
close(handle);
struct dahdi_timer *timer = data;
close(timer->fd);
ast_free(timer);
}
static int dahdi_timer_set_rate(int handle, unsigned int rate)
static int dahdi_timer_set_rate(void *data, unsigned int rate)
{
struct dahdi_timer *timer = data;
int samples;
/* DAHDI timers are configured using a number of samples,
* based on an 8 kHz sample rate. */
samples = (unsigned int) roundf((8000.0 / ((float) rate)));
if (ioctl(handle, DAHDI_TIMERCONFIG, &samples)) {
if (ioctl(timer->fd, DAHDI_TIMERCONFIG, &samples)) {
ast_log(LOG_ERROR, "Failed to configure DAHDI timing fd for %u sample timer ticks\n",
samples);
return -1;
@ -94,31 +116,36 @@ static int dahdi_timer_set_rate(int handle, unsigned int rate)
return 0;
}
static int dahdi_timer_ack(int handle, unsigned int quantity)
static int dahdi_timer_ack(void *data, unsigned int quantity)
{
return ioctl(handle, DAHDI_TIMERACK, &quantity) ? -1 : 0;
struct dahdi_timer *timer = data;
return ioctl(timer->fd, DAHDI_TIMERACK, &quantity) ? -1 : 0;
}
static int dahdi_timer_enable_continuous(int handle)
static int dahdi_timer_enable_continuous(void *data)
{
struct dahdi_timer *timer = data;
int flags = 1;
return ioctl(handle, DAHDI_TIMERPING, &flags) ? -1 : 0;
return ioctl(timer->fd, DAHDI_TIMERPING, &flags) ? -1 : 0;
}
static int dahdi_timer_disable_continuous(int handle)
static int dahdi_timer_disable_continuous(void *data)
{
struct dahdi_timer *timer = data;
int flags = -1;
return ioctl(handle, DAHDI_TIMERPONG, &flags) ? -1 : 0;
return ioctl(timer->fd, DAHDI_TIMERPONG, &flags) ? -1 : 0;
}
static enum ast_timer_event dahdi_timer_get_event(int handle)
static enum ast_timer_event dahdi_timer_get_event(void *data)
{
struct dahdi_timer *timer = data;
int res;
int event;
res = ioctl(handle, DAHDI_GETEVENT, &event);
res = ioctl(timer->fd, DAHDI_GETEVENT, &event);
if (res) {
event = DAHDI_EVENT_TIMER_EXPIRED;
@ -133,11 +160,18 @@ static enum ast_timer_event dahdi_timer_get_event(int handle)
}
}
static unsigned int dahdi_timer_get_max_rate(int handle)
static unsigned int dahdi_timer_get_max_rate(void *data)
{
return 1000;
}
static int dahdi_timer_fd(void *data)
{
struct dahdi_timer *timer = data;
return timer->fd;
}
#define SEE_TIMING "For more information on Asterisk timing modules, including ways to potentially fix this problem, please see https://wiki.asterisk.org/wiki/display/AST/Timing+Interfaces\n"
static int dahdi_test_timer(void)

@ -48,14 +48,15 @@
static void *timing_funcs_handle;
static int kqueue_timer_open(void);
static void kqueue_timer_close(int handle);
static int kqueue_timer_set_rate(int handle, unsigned int rate);
static int kqueue_timer_ack(int handle, unsigned int quantity);
static int kqueue_timer_enable_continuous(int handle);
static int kqueue_timer_disable_continuous(int handle);
static enum ast_timer_event kqueue_timer_get_event(int handle);
static unsigned int kqueue_timer_get_max_rate(int handle);
static void *kqueue_timer_open(void);
static void kqueue_timer_close(void *data);
static int kqueue_timer_set_rate(void *data, unsigned int rate);
static int kqueue_timer_ack(void *data, unsigned int quantity);
static int kqueue_timer_enable_continuous(void *data);
static int kqueue_timer_disable_continuous(void *data);
static enum ast_timer_event kqueue_timer_get_event(void *data);
static unsigned int kqueue_timer_get_max_rate(void *data);
static int kqueue_timer_fd(void *data);
static struct ast_timing_interface kqueue_timing = {
.name = "kqueue",
@ -68,10 +69,9 @@ static struct ast_timing_interface kqueue_timing = {
.timer_disable_continuous = kqueue_timer_disable_continuous,
.timer_get_event = kqueue_timer_get_event,
.timer_get_max_rate = kqueue_timer_get_max_rate,
.timer_fd = kqueue_timer_fd,
};
static struct ao2_container *kqueue_timers;
struct kqueue_timer {
int handle;
uint64_t nsecs;
@ -79,73 +79,34 @@ struct kqueue_timer {
unsigned int is_continuous:1;
};
static int kqueue_timer_hash(const void *obj, const int flags)
{
const struct kqueue_timer *timer = obj;
return timer->handle;
}
static int kqueue_timer_cmp(void *obj, void *args, int flags)
{
struct kqueue_timer *timer1 = obj, *timer2 = args;
return timer1->handle == timer2->handle ? CMP_MATCH | CMP_STOP : 0;
}
static void timer_destroy(void *obj)
{
struct kqueue_timer *timer = obj;
close(timer->handle);
}
#define lookup_timer(a) _lookup_timer(a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
static struct kqueue_timer *_lookup_timer(int handle, const char *file, int line, const char *func)
{
struct kqueue_timer *our_timer, find_helper = {
.handle = handle,
};
if (!(our_timer = ao2_find(kqueue_timers, &find_helper, OBJ_POINTER))) {
ast_log(__LOG_ERROR, file, line, func, "Couldn't find timer with handle %d\n", handle);
/* API says we set errno */
errno = ESRCH;
return NULL;
}
return our_timer;
}
static int kqueue_timer_open(void)
static void *kqueue_timer_open(void)
{
struct kqueue_timer *timer;
int handle;
if (!(timer = ao2_alloc(sizeof(*timer), timer_destroy))) {
ast_log(LOG_ERROR, "Could not allocate memory for kqueue_timer structure\n");
return -1;
}
if ((handle = kqueue()) < 0) {
if ((timer->handle = kqueue()) < 0) {
ast_log(LOG_ERROR, "Failed to create kqueue timer: %s\n", strerror(errno));
ao2_ref(timer, -1);
return -1;
}
timer->handle = handle;
ao2_link(kqueue_timers, timer);
/* Get rid of the reference from the allocation */
ao2_ref(timer, -1);
return handle;
return timer;
}
static void kqueue_timer_close(int handle)
static void kqueue_timer_close(void *data)
{
struct kqueue_timer *our_timer;
struct kqueue_timer *timer = data;
if (!(our_timer = lookup_timer(handle))) {
return;
}
ao2_unlink(kqueue_timers, our_timer);
ao2_ref(our_timer, -1);
ao2_ref(timer, -1);
}
static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
@ -183,107 +144,90 @@ static void kqueue_set_nsecs(struct kqueue_timer *our_timer, uint64_t nsecs)
#endif
}
static int kqueue_timer_set_rate(int handle, unsigned int rate)
static int kqueue_timer_set_rate(void *data, unsigned int rate)
{
struct kqueue_timer *our_timer;
struct kqueue_timer *timer = data;
if (!(our_timer = lookup_timer(handle))) {
return -1;
}
kqueue_set_nsecs(our_timer, (our_timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
ao2_ref(our_timer, -1);
kqueue_set_nsecs(timer, (timer->nsecs = rate ? (long) (1000000000 / rate) : 0L));
return 0;
}
static int kqueue_timer_ack(int handle, unsigned int quantity)
static int kqueue_timer_ack(void *data, unsigned int quantity)
{
struct kqueue_timer *our_timer;
struct kqueue_timer *timer = data;
if (!(our_timer = lookup_timer(handle))) {
return -1;
}
if (our_timer->unacked < quantity) {
if (timer->unacked < quantity) {
ast_debug(1, "Acking more events than have expired?!!\n");
our_timer->unacked = 0;
ao2_ref(our_timer, -1);
timer->unacked = 0;
return -1;
} else {
our_timer->unacked -= quantity;
timer->unacked -= quantity;
}
ao2_ref(our_timer, -1);
return 0;
}
static int kqueue_timer_enable_continuous(int handle)
static int kqueue_timer_enable_continuous(void *data)
{
struct kqueue_timer *our_timer;
struct kqueue_timer *timer = data;
if (!(our_timer = lookup_timer(handle))) {
return -1;
}
kqueue_set_nsecs(timer, 1);
timer->is_continuous = 1;
timer->unacked = 0;
kqueue_set_nsecs(our_timer, 1);
our_timer->is_continuous = 1;
our_timer->unacked = 0;
ao2_ref(our_timer, -1);
return 0;
}
static int kqueue_timer_disable_continuous(int handle)
static int kqueue_timer_disable_continuous(void *data)
{
struct kqueue_timer *our_timer;
struct kqueue_timer *timer = data;
if (!(our_timer = lookup_timer(handle))) {
return -1;
}
kqueue_set_nsecs(timer, timer->nsecs);
timer->is_continuous = 0;
timer->unacked = 0;
kqueue_set_nsecs(our_timer, our_timer->nsecs);
our_timer->is_continuous = 0;
our_timer->unacked = 0;
ao2_ref(our_timer, -1);
return 0;
}
static enum ast_timer_event kqueue_timer_get_event(int handle)
static enum ast_timer_event kqueue_timer_get_event(void *data)
{
struct kqueue_timer *timer = data;
enum ast_timer_event res = -1;
struct kqueue_timer *our_timer;
struct timespec sixty_seconds = { 60, 0 };
struct kevent kev;
if (!(our_timer = lookup_timer(handle))) {
return -1;
}
/* If we have non-ACKed events, just return immediately */
if (our_timer->unacked == 0) {
if (kevent(handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
our_timer->unacked += kev.data;
if (timer->unacked == 0) {
if (kevent(timer->handle, NULL, 0, &kev, 1, &sixty_seconds) > 0) {
timer->unacked += kev.data;
}
}
if (our_timer->unacked > 0) {
res = our_timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
if (timer->unacked > 0) {
res = timer->is_continuous ? AST_TIMING_EVENT_CONTINUOUS : AST_TIMING_EVENT_EXPIRED;
}
ao2_ref(our_timer, -1);
return res;
}
static unsigned int kqueue_timer_get_max_rate(int handle)
static unsigned int kqueue_timer_get_max_rate(void *data)
{
/* Actually, the max rate is 2^64-1 seconds, but that's not representable in a 32-bit integer. */
return UINT_MAX;
}
static int kqueue_timer_fd(void *data)
{
struct kqueue_timer *timer = data;
return timer->handle;
}
#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(test_kqueue_timing)
{
int res = AST_TEST_PASS, handle, i;
int res = AST_TEST_PASS, i;
uint64_t diff;
struct pollfd pfd = { 0, POLLIN, 0 };
struct kqueue_timer *kt;
@ -300,14 +244,14 @@ AST_TEST_DEFINE(test_kqueue_timing)
break;
}
if (!(handle = kqueue_timer_open())) {
if (!(kt = kqueue_timer_open())) {
ast_test_status_update(test, "Cannot open timer!\n");
return AST_TEST_FAIL;
}
do {
pfd.fd = handle;
if (kqueue_timer_set_rate(handle, 1000)) {
pfd.fd = ast_timer_fd(kt);
if (kqueue_timer_set_rate(kt, 1000)) {
ast_test_status_update(test, "Cannot set timer rate to 1000/s\n");
res = AST_TEST_FAIL;
break;
@ -322,12 +266,7 @@ AST_TEST_DEFINE(test_kqueue_timing)
res = AST_TEST_FAIL;
break;
}
if (!(kt = lookup_timer(handle))) {
ast_test_status_update(test, "Could not find timer structure in container?!!\n");
res = AST_TEST_FAIL;
break;
}
if (kqueue_timer_get_event(handle) <= 0) {
if (kqueue_timer_get_event(kt) <= 0) {
ast_test_status_update(test, "No events generated after a poll returned successfully?!!\n");
res = AST_TEST_FAIL;
break;
@ -339,7 +278,7 @@ AST_TEST_DEFINE(test_kqueue_timing)
break;
}
#endif
kqueue_timer_enable_continuous(handle);
kqueue_timer_enable_continuous(kt);
start = ast_tvnow();
for (i = 0; i < 100; i++) {
if (ast_poll(&pfd, 1, 1000) < 1) {
@ -347,7 +286,7 @@ AST_TEST_DEFINE(test_kqueue_timing)
res = AST_TEST_FAIL;
break;
}
if (kqueue_timer_get_event(handle) <= 0) {
if (kqueue_timer_get_event(kt) <= 0) {
ast_test_status_update(test, "No events generated in continuous mode after 1 microsecond?!!\n");
res = AST_TEST_FAIL;
break;
@ -362,7 +301,7 @@ AST_TEST_DEFINE(test_kqueue_timing)
}
*/
} while (0);
kqueue_timer_close(handle);
kqueue_timer_close(kt);
return res;
}
#endif
@ -379,12 +318,7 @@ AST_TEST_DEFINE(test_kqueue_timing)
*/
static int load_module(void)
{
if (!(kqueue_timers = ao2_container_alloc(563, kqueue_timer_hash, kqueue_timer_cmp))) {
return AST_MODULE_LOAD_DECLINE;
}
if (!(timing_funcs_handle = ast_register_timing_interface(&kqueue_timing))) {
ao2_ref(kqueue_timers, -1);
return AST_MODULE_LOAD_DECLINE;
}
@ -394,15 +328,9 @@ static int load_module(void)
static int unload_module(void)
{
int res;
AST_TEST_UNREGISTER(test_kqueue_timing);
if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
ao2_ref(kqueue_timers, -1);
kqueue_timers = NULL;
}
return res;
return ast_unregister_timing_interface(timing_funcs_handle);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "KQueue Timing Interface",

@ -45,14 +45,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
static void *timing_funcs_handle;
static int pthread_timer_open(void);
static void pthread_timer_close(int handle);
static int pthread_timer_set_rate(int handle, unsigned int rate);
static int 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_timer_event pthread_timer_get_event(int handle);
static unsigned int pthread_timer_get_max_rate(int handle);
static void *pthread_timer_open(void);
static void pthread_timer_close(void *data);
static int pthread_timer_set_rate(void *data, unsigned int rate);
static int pthread_timer_ack(void *data, unsigned int quantity);
static int pthread_timer_enable_continuous(void *data);
static int pthread_timer_disable_continuous(void *data);
static enum ast_timer_event pthread_timer_get_event(void *data);
static unsigned int pthread_timer_get_max_rate(void *data);
static int pthread_timer_fd(void *data);
static struct ast_timing_interface pthread_timing = {
.name = "pthread",
@ -65,6 +66,7 @@ static struct ast_timing_interface pthread_timing = {
.timer_disable_continuous = pthread_timer_disable_continuous,
.timer_get_event = pthread_timer_get_event,
.timer_get_max_rate = pthread_timer_get_max_rate,
.timer_fd = pthread_timer_fd,
};
/* 1 tick / 10 ms */
@ -97,7 +99,6 @@ struct pthread_timer {
};
static void pthread_timer_destructor(void *obj);
static struct pthread_timer *find_timer(int handle, int unlinkobj);
static void signal_pipe(struct pthread_timer *timer);
static void unsignal_pipe(struct pthread_timer *timer);
static void ack_ticks(struct pthread_timer *timer, unsigned int num);
@ -112,15 +113,14 @@ static struct {
unsigned int stop:1;
} timing_thread;
static int pthread_timer_open(void)
static void *pthread_timer_open(void)
{
struct pthread_timer *timer;
int fd;
int i;
if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
errno = ENOMEM;
return -1;
return NULL;
}
timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
@ -128,7 +128,7 @@ static int pthread_timer_open(void)
if (pipe(timer->pipe)) {
ao2_ref(timer, -1);
return -1;
return NULL;
}
for (i = 0; i < ARRAY_LEN(timer->pipe); ++i) {
@ -146,32 +146,19 @@ static int pthread_timer_open(void)
ao2_link(pthread_timers, timer);
ao2_unlock(pthread_timers);
fd = timer->pipe[PIPE_READ];
ao2_ref(timer, -1);
return fd;
return timer;
}
static void pthread_timer_close(int handle)
static void pthread_timer_close(void *data)
{
struct pthread_timer *timer;
if (!(timer = find_timer(handle, 1))) {
return;
}
struct pthread_timer *timer = data;
ao2_ref(timer, -1);
}
static int pthread_timer_set_rate(int handle, unsigned int rate)
static int pthread_timer_set_rate(void *data, unsigned int rate)
{
struct pthread_timer *timer;
if (!(timer = find_timer(handle, 0))) {
errno = EINVAL;
return -1;
}
struct pthread_timer *timer = data;
if (rate > MAX_RATE) {
ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a "
@ -195,38 +182,25 @@ static int pthread_timer_set_rate(int handle, unsigned int rate)
ao2_unlock(timer);
ao2_ref(timer, -1);
return 0;
}
static int pthread_timer_ack(int handle, unsigned int quantity)
static int pthread_timer_ack(void *data, unsigned int quantity)
{
struct pthread_timer *timer;
struct pthread_timer *timer = data;
ast_assert(quantity > 0);
if (!(timer = find_timer(handle, 0))) {
return -1;
}
ao2_lock(timer);
ack_ticks(timer, quantity);
ao2_unlock(timer);
ao2_ref(timer, -1);
return 0;
}
static int pthread_timer_enable_continuous(int handle)
static int pthread_timer_enable_continuous(void *data)
{
struct pthread_timer *timer;
if (!(timer = find_timer(handle, 0))) {
errno = EINVAL;
return -1;
}
struct pthread_timer *timer = data;
ao2_lock(timer);
if (!timer->continuous) {
@ -235,19 +209,12 @@ static int pthread_timer_enable_continuous(int handle)
}
ao2_unlock(timer);
ao2_ref(timer, -1);
return 0;
}
static int pthread_timer_disable_continuous(int handle)
static int pthread_timer_disable_continuous(void *data)
{
struct pthread_timer *timer;
if (!(timer = find_timer(handle, 0))) {
errno = EINVAL;
return -1;
}
struct pthread_timer *timer = data;
ao2_lock(timer);
if (timer->continuous) {
@ -256,54 +223,33 @@ static int pthread_timer_disable_continuous(int handle)
}
ao2_unlock(timer);
ao2_ref(timer, -1);
return 0;
}
static enum ast_timer_event pthread_timer_get_event(int handle)
static enum ast_timer_event pthread_timer_get_event(void *data)
{
struct pthread_timer *timer;
struct pthread_timer *timer = data;
enum ast_timer_event res = AST_TIMING_EVENT_EXPIRED;
if (!(timer = find_timer(handle, 0))) {
return res;
}
ao2_lock(timer);
if (timer->continuous) {
res = AST_TIMING_EVENT_CONTINUOUS;
}
ao2_unlock(timer);
ao2_ref(timer, -1);
return res;
}
static unsigned int pthread_timer_get_max_rate(int handle)
static unsigned int pthread_timer_get_max_rate(void *data)
{
return MAX_RATE;
}
static struct pthread_timer *find_timer(int handle, int unlinkobj)
static int pthread_timer_fd(void *data)
{
struct pthread_timer *timer;
struct pthread_timer tmp_timer;
int flags = OBJ_POINTER;
tmp_timer.pipe[PIPE_READ] = handle;
struct pthread_timer *timer = data;
if (unlinkobj) {
flags |= OBJ_UNLINK;
}
if (!(timer = ao2_find(pthread_timers, &tmp_timer, flags))) {
ast_assert(timer != NULL);
return NULL;
}
return timer;
return timer->pipe[PIPE_READ];
}
static void pthread_timer_destructor(void *obj)

@ -41,14 +41,15 @@
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 int 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_timer_event timerfd_timer_get_event(int handle);
static unsigned int timerfd_timer_get_max_rate(int handle);
static void *timerfd_timer_open(void);
static void timerfd_timer_close(void *data);
static int timerfd_timer_set_rate(void *data, unsigned int rate);
static int timerfd_timer_ack(void *data, unsigned int quantity);
static int timerfd_timer_enable_continuous(void *data);
static int timerfd_timer_disable_continuous(void *data);
static enum ast_timer_event timerfd_timer_get_event(void *data);
static unsigned int timerfd_timer_get_max_rate(void *data);
static int timerfd_timer_fd(void *data);
static struct ast_timing_interface timerfd_timing = {
.name = "timerfd",
@ -61,152 +62,93 @@ static struct ast_timing_interface timerfd_timing = {
.timer_disable_continuous = timerfd_timer_disable_continuous,
.timer_get_event = timerfd_timer_get_event,
.timer_get_max_rate = timerfd_timer_get_max_rate,
.timer_fd = timerfd_timer_fd,
};
static struct ao2_container *timerfd_timers;
#define TIMERFD_TIMER_BUCKETS 563
#define TIMERFD_MAX_RATE 1000
struct timerfd_timer {
int handle;
int fd;
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, 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);
timer->handle = -1;
close(timer->fd);
}
static int timerfd_timer_open(void)
static void *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;
return NULL;
}
if ((handle = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) {
if ((timer->fd = 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;
return NULL;
}
timer->handle = handle;
ao2_link(timerfd_timers, timer);
/* Get rid of the reference from the allocation */
ao2_ref(timer, -1);
return handle;
return timer;
}
static void timerfd_timer_close(int handle)
static void timerfd_timer_close(void *data)
{
struct timerfd_timer *our_timer, find_helper = {
.handle = handle,
};
if (handle == -1) {
ast_log(LOG_ERROR, "Attempting to close timerfd handle -1");
return;
}
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, 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);
ao2_ref(data, -1);
}
static int timerfd_timer_set_rate(int handle, unsigned int rate)
static int timerfd_timer_set_rate(void *data, unsigned int rate)
{
struct timerfd_timer *our_timer, find_helper = {
.handle = handle,
};
struct timerfd_timer *timer = data;
int res = 0;
if (handle == -1) {
ast_log(LOG_ERROR, "Attempting to set rate on timerfd handle -1");
return -1;
}
ao2_lock(timer);
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
return -1;
}
ao2_lock(our_timer);
our_timer->saved_timer.it_value.tv_sec = 0;
our_timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
our_timer->saved_timer.it_interval.tv_sec = our_timer->saved_timer.it_value.tv_sec;
our_timer->saved_timer.it_interval.tv_nsec = our_timer->saved_timer.it_value.tv_nsec;
timer->saved_timer.it_value.tv_sec = 0;
timer->saved_timer.it_value.tv_nsec = rate ? (long) (1000000000 / rate) : 0L;
timer->saved_timer.it_interval.tv_sec = timer->saved_timer.it_value.tv_sec;
timer->saved_timer.it_interval.tv_nsec = timer->saved_timer.it_value.tv_nsec;
if (!our_timer->is_continuous) {
res = timerfd_settime(handle, 0, &our_timer->saved_timer, NULL);
if (!timer->is_continuous) {
res = timerfd_settime(timer->fd, 0, &timer->saved_timer, NULL);
}
ao2_unlock(our_timer);
ao2_ref(our_timer, -1);
ao2_unlock(timer);
return res;
}
static int timerfd_timer_ack(int handle, unsigned int quantity)
static int timerfd_timer_ack(void *data, unsigned int quantity)
{
struct timerfd_timer *timer = data;
uint64_t expirations;
int read_result = 0;
int res = 0;
struct timerfd_timer *our_timer, find_helper = {
.handle = handle,
};
if (handle == -1) {
ast_log(LOG_ERROR, "Attempting to ack timerfd handle -1");
return -1;
}
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
ast_log(LOG_ERROR, "Couldn't find a timer with handle %d\n", handle);
return -1;
}
ao2_lock(our_timer);
ao2_lock(timer);
do {
struct itimerspec timer_status;
if (timerfd_gettime(handle, &timer_status)) {
ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", handle, strerror(errno));
if (timerfd_gettime(timer->fd, &timer_status)) {
ast_log(LOG_ERROR, "Call to timerfd_gettime() using handle %d error: %s\n", timer->fd, strerror(errno));
expirations = 0;
res = -1;
break;
}
if (timer_status.it_value.tv_sec == 0 && timer_status.it_value.tv_nsec == 0) {
ast_debug(1, "Avoiding read on disarmed timerfd %d\n", handle);
ast_debug(1, "Avoiding read on disarmed timerfd %d\n", timer->fd);
expirations = 0;
break;
}
read_result = read(handle, &expirations, sizeof(expirations));
read_result = read(timer->fd, &expirations, sizeof(expirations));
if (read_result == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
@ -218,121 +160,93 @@ static int timerfd_timer_ack(int handle, unsigned int quantity)
}
} while (read_result != sizeof(expirations));
ao2_unlock(our_timer);
ao2_ref(our_timer, -1);
ao2_unlock(timer);
if (expirations != quantity) {
ast_debug(2, "Expected to acknowledge %u ticks but got %llu instead\n", quantity, (unsigned long long) expirations);
}
return res;
}
static int timerfd_timer_enable_continuous(int handle)
static int timerfd_timer_enable_continuous(void *data)
{
struct timerfd_timer *timer = data;
int res;
struct itimerspec continuous_timer = {
static const struct itimerspec continuous_timer = {
.it_value.tv_nsec = 1L,
};
struct timerfd_timer *our_timer, find_helper = {
.handle = handle,
};
if (handle == -1) {
ast_log(LOG_ERROR, "Attempting to enable timerfd handle -1");
return -1;
}
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
return -1;
}
ao2_lock(our_timer);
ao2_lock(timer);
if (our_timer->is_continuous) {
if (timer->is_continuous) {
/*It's already in continous mode, no need to do
* anything further
*/
ao2_unlock(our_timer);
ao2_ref(our_timer, -1);
ao2_unlock(timer);
return 0;
}
res = timerfd_settime(handle, 0, &continuous_timer, &our_timer->saved_timer);
our_timer->is_continuous = 1;
ao2_unlock(our_timer);
ao2_ref(our_timer, -1);
res = timerfd_settime(timer->fd, 0, &continuous_timer, &timer->saved_timer);
timer->is_continuous = 1;
ao2_unlock(timer);
return res;
}
static int timerfd_timer_disable_continuous(int handle)
static int timerfd_timer_disable_continuous(void *data)
{
struct timerfd_timer *timer = data;
int res;
struct timerfd_timer *our_timer, find_helper = {
.handle = handle,
};
if (handle == -1) {
ast_log(LOG_ERROR, "Attempting to disable timerfd handle -1");
return -1;
}
ao2_lock(timer);
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
return -1;
}
ao2_lock(our_timer);
if (!our_timer->is_continuous) {
if (!timer->is_continuous) {
/* No reason to do anything if we're not
* in continuous mode
*/
ao2_unlock(our_timer);
ao2_ref(our_timer, -1);
ao2_unlock(timer);
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_unlock(our_timer);
ao2_ref(our_timer, -1);
res = timerfd_settime(timer->fd, 0, &timer->saved_timer, NULL);
timer->is_continuous = 0;
memset(&timer->saved_timer, 0, sizeof(timer->saved_timer));
ao2_unlock(timer);
return res;
}
static enum ast_timer_event timerfd_timer_get_event(int handle)
static enum ast_timer_event timerfd_timer_get_event(void *data)
{
struct timerfd_timer *timer = data;
enum ast_timer_event res;
struct timerfd_timer *our_timer, find_helper = {
.handle = handle,
};
if (handle == -1) {
ast_log(LOG_ERROR, "Attempting to get event from timerfd handle -1");
return -1;
}
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
return -1;
}
ao2_lock(our_timer);
ao2_lock(timer);
if (our_timer->is_continuous) {
if (timer->is_continuous) {
res = AST_TIMING_EVENT_CONTINUOUS;
} else {
res = AST_TIMING_EVENT_EXPIRED;
}
ao2_unlock(our_timer);
ao2_ref(our_timer, -1);
ao2_unlock(timer);
return res;
}
static unsigned int timerfd_timer_get_max_rate(int handle)
static unsigned int timerfd_timer_get_max_rate(void *data)
{
return TIMERFD_MAX_RATE;
}
static int timerfd_timer_fd(void *data)
{
struct timerfd_timer *timer = data;
return timer->fd;
}
static int load_module(void)
{
int fd;
@ -345,12 +259,7 @@ static int load_module(void)
close(fd);
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_register_timing_interface(&timerfd_timing))) {
ao2_ref(timerfd_timers, -1);
return AST_MODULE_LOAD_DECLINE;
}
@ -359,14 +268,7 @@ static int load_module(void)
static int unload_module(void)
{
int res;
if (!(res = ast_unregister_timing_interface(timing_funcs_handle))) {
ao2_ref(timerfd_timers, -1);
timerfd_timers = NULL;
}
return res;
return ast_unregister_timing_interface(timing_funcs_handle);
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Timerfd Timing Interface",

Loading…
Cancel
Save