add mod_timerfd: external timerfd module by Timo Teräs

This commit is contained in:
Anthony Minessale 2011-03-21 20:50:47 -05:00
parent 10174ea6d5
commit 48b1193552

View File

@ -0,0 +1,260 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Timo Teräs <timo.teras@iki.fi>
*
*
* mod_timerfd.c -- Timer implementation using Linux timerfd
*
*/
#include <switch.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_timerfd_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_timerfd_shutdown);
SWITCH_MODULE_RUNTIME_FUNCTION(mod_timerfd_runtime);
SWITCH_MODULE_DEFINITION(mod_timerfd, mod_timerfd_load, mod_timerfd_shutdown, mod_timerfd_runtime);
#define MAX_INTERVAL 2000 /* ms */
struct interval_timer {
int fd;
int users;
switch_size_t tick;
switch_mutex_t *mutex;
switch_thread_cond_t *cond;
};
typedef struct interval_timer interval_timer_t;
static switch_memory_pool_t *module_pool = NULL;
static switch_mutex_t *interval_timers_mutex;
static interval_timer_t interval_timers[MAX_INTERVAL + 1];
static int interval_poll_fd;
static switch_status_t timerfd_start_interval(interval_timer_t *it, int interval)
{
struct itimerspec val;
struct epoll_event e;
int fd;
it->users++;
if (it->users > 1)
return SWITCH_STATUS_SUCCESS;
it->tick = 0;
switch_mutex_init(&it->mutex, SWITCH_MUTEX_NESTED, module_pool);
switch_thread_cond_create(&it->cond, module_pool);
fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
if (fd < 0)
return SWITCH_STATUS_GENERR;
val.it_interval.tv_sec = interval / 1000;
val.it_interval.tv_nsec = (interval % 1000) * 1000000;
val.it_value.tv_sec = 0;
val.it_value.tv_nsec = 100000;
if (timerfd_settime(fd, 0, &val, NULL) < 0) {
close(fd);
return SWITCH_STATUS_GENERR;
}
e.events = EPOLLIN | EPOLLERR;
e.data.ptr = it;
if (epoll_ctl(interval_poll_fd, EPOLL_CTL_ADD, fd, &e) < 0) {
close(fd);
return SWITCH_STATUS_GENERR;
}
it->fd = fd;
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t timerfd_stop_interval(interval_timer_t *it)
{
struct itimerspec val;
int fd;
it->users--;
if (it->users > 0)
return SWITCH_STATUS_SUCCESS;
close(it->fd);
it->fd = -1;
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t timerfd_init(switch_timer_t *timer)
{
interval_timer_t *it;
int rc;
if (timer->interval < 1 || timer->interval > MAX_INTERVAL)
return SWITCH_STATUS_GENERR;
it = &interval_timers[timer->interval];
switch_mutex_lock(interval_timers_mutex);
rc = timerfd_start_interval(it, timer->interval);
timer->private_info = it;
switch_mutex_unlock(interval_timers_mutex);
return rc;
}
static switch_status_t timerfd_step(switch_timer_t *timer)
{
timer->tick++;
timer->samplecount += timer->samples;
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t timerfd_next(switch_timer_t *timer)
{
interval_timer_t *it = timer->private_info;
if ((int)(timer->tick - it->tick) < -1)
timer->tick = it->tick;
timerfd_step(timer);
switch_mutex_lock(it->mutex);
while ((int)(timer->tick - it->tick) > 0)
switch_thread_cond_wait(it->cond, it->mutex);
switch_mutex_unlock(it->mutex);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t timerfd_sync(switch_timer_t *timer)
{
interval_timer_t *it = timer->private_info;
timer->tick = it->tick;
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t timerfd_check(switch_timer_t *timer, switch_bool_t step)
{
interval_timer_t *it = timer->private_info;
int diff = (int)(timer->tick - it->tick);
if (diff > 0) {
/* still pending */
timer->diff = diff;
return SWITCH_STATUS_FALSE;
} else {
/* timer pending */
timer->diff = 0;
if (step)
timerfd_step(timer);
return SWITCH_STATUS_SUCCESS;
}
}
static switch_status_t timerfd_destroy(switch_timer_t *timer)
{
interval_timer_t *it = timer->private_info;
int rc;
switch_mutex_lock(interval_timers_mutex);
rc = timerfd_stop_interval(it);
switch_mutex_unlock(interval_timers_mutex);
return rc;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_timerfd_load)
{
switch_timer_interface_t *timer_interface;
module_pool = pool;
interval_poll_fd = epoll_create(16);
if (interval_poll_fd < 0)
return SWITCH_STATUS_GENERR;
switch_mutex_init(&interval_timers_mutex, SWITCH_MUTEX_NESTED, module_pool);
memset(interval_timers, 0, sizeof(interval_timers));
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
timer_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_TIMER_INTERFACE);
timer_interface->interface_name = "timerfd";
timer_interface->timer_init = timerfd_init;
timer_interface->timer_next = timerfd_next;
timer_interface->timer_step = timerfd_step;
timer_interface->timer_sync = timerfd_sync;
timer_interface->timer_check = timerfd_check;
timer_interface->timer_destroy = timerfd_destroy;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_timerfd_shutdown)
{
close(interval_poll_fd);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_RUNTIME_FUNCTION(mod_timerfd_runtime)
{
struct epoll_event e[16];
interval_timer_t *it;
uint64_t u64;
int i, r;
do {
r = epoll_wait(interval_poll_fd, e, sizeof(e) / sizeof(e[0]), 1000);
if (r < 0)
break;
for (i = 0; i < r; i++) {
it = e[i].data.ptr;
if ((e[i].events & EPOLLIN) &&
read(it->fd, &u64, sizeof(u64)) == sizeof(u64)) {
switch_mutex_lock(it->mutex);
it->tick += u64;
switch_thread_cond_broadcast(it->cond);
switch_mutex_unlock(it->mutex);
}
}
} while (1);
return SWITCH_STATUS_TERM;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/