/* * 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 * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * Timo Teräs * * * mod_timerfd.c -- Timer implementation using Linux timerfd * */ #include #include #include 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: */