FS-3731 --resolve
This commit is contained in:
parent
eae86e0a13
commit
9d7e9e6742
|
@ -120,6 +120,7 @@ say/mod_say_en
|
||||||
#say/mod_say_th
|
#say/mod_say_th
|
||||||
#say/mod_say_he
|
#say/mod_say_he
|
||||||
#timers/mod_timerfd
|
#timers/mod_timerfd
|
||||||
|
#timers/mod_posix_timer
|
||||||
|
|
||||||
## Experimental Modules (don't cry if they're broken)
|
## Experimental Modules (don't cry if they're broken)
|
||||||
#../../contrib/mod/xml_int/mod_xml_odbc
|
#../../contrib/mod/xml_int/mod_xml_odbc
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
|
|
||||||
<!-- Timers -->
|
<!-- Timers -->
|
||||||
<!-- <load module="mod_timerfd"/> -->
|
<!-- <load module="mod_timerfd"/> -->
|
||||||
|
<!-- <load module="mod_posix_timer"/> -->
|
||||||
|
|
||||||
<!-- Languages -->
|
<!-- Languages -->
|
||||||
<load module="mod_spidermonkey"/>
|
<load module="mod_spidermonkey"/>
|
||||||
|
|
|
@ -0,0 +1,288 @@
|
||||||
|
/*
|
||||||
|
* 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 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):
|
||||||
|
* Chris Rienzo <chris@rienzo.net>
|
||||||
|
* Timo Teräs <timo.teras@iki.fi> (based on mod_timerfd.c)
|
||||||
|
*
|
||||||
|
* mod_posix_timer.c -- soft timer implemented with POSIX timers (timer_create/timer_settime/timer_getoverrun)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <switch.h>
|
||||||
|
#include <sys/signal.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
SWITCH_MODULE_LOAD_FUNCTION(mod_posix_timer_load);
|
||||||
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_posix_timer_shutdown);
|
||||||
|
SWITCH_MODULE_DEFINITION(mod_posix_timer, mod_posix_timer_load, mod_posix_timer_shutdown, NULL);
|
||||||
|
|
||||||
|
#define MAX_INTERVAL 2000 /* ms */
|
||||||
|
#define TIMERS_PER_INTERVAL 4
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int users;
|
||||||
|
timer_t timer;
|
||||||
|
switch_size_t tick;
|
||||||
|
switch_mutex_t *mutex;
|
||||||
|
switch_thread_cond_t *cond;
|
||||||
|
} interval_timer_t;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
switch_memory_pool_t *pool;
|
||||||
|
int shutdown;
|
||||||
|
interval_timer_t interval_timers[MAX_INTERVAL + 1][TIMERS_PER_INTERVAL];
|
||||||
|
int next_interval_timer_id[MAX_INTERVAL + 1];
|
||||||
|
switch_mutex_t *interval_timers_mutex;
|
||||||
|
} globals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notified by POSIX timer of a tick
|
||||||
|
*/
|
||||||
|
static void posix_timer_notify(sigval_t data)
|
||||||
|
{
|
||||||
|
interval_timer_t *it = (interval_timer_t *)data.sival_ptr;
|
||||||
|
switch_mutex_lock(it->mutex);
|
||||||
|
it->tick += 1 + timer_getoverrun(it->timer);
|
||||||
|
switch_thread_cond_broadcast(it->cond);
|
||||||
|
switch_mutex_unlock(it->mutex);
|
||||||
|
|
||||||
|
if (globals.shutdown) {
|
||||||
|
timer_delete(it->timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new timer
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_start_interval(interval_timer_t *it, int interval)
|
||||||
|
{
|
||||||
|
struct sigevent sigev;
|
||||||
|
struct itimerspec val;
|
||||||
|
|
||||||
|
if (globals.shutdown) {
|
||||||
|
return SWITCH_STATUS_GENERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->users++;
|
||||||
|
if (it->users > 1) {
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->tick = 0;
|
||||||
|
|
||||||
|
switch_mutex_init(&it->mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||||
|
switch_thread_cond_create(&it->cond, globals.pool);
|
||||||
|
|
||||||
|
/* create the POSIX timer. Will notify the posix_timer_notify thread on ticks. */
|
||||||
|
memset(&sigev, 0, sizeof(sigev));
|
||||||
|
sigev.sigev_notify = SIGEV_THREAD;
|
||||||
|
sigev.sigev_notify_function = posix_timer_notify;
|
||||||
|
sigev.sigev_value.sival_ptr = (void *)it;
|
||||||
|
if (timer_create(CLOCK_MONOTONIC, &sigev, &it->timer) == -1) {
|
||||||
|
return SWITCH_STATUS_GENERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start the timer to tick at interval */
|
||||||
|
memset(&val, 0, sizeof(val));
|
||||||
|
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 (timer_settime(it->timer, 0, &val, NULL) == -1) {
|
||||||
|
return SWITCH_STATUS_GENERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a timer
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_stop_interval(interval_timer_t *it)
|
||||||
|
{
|
||||||
|
it->users--;
|
||||||
|
if (it->users > 0)
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
|
||||||
|
timer_delete(it->timer);
|
||||||
|
memset(&it->timer, 0, sizeof(it->timer));
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer module interface: start a new timer
|
||||||
|
* @param timer the timer
|
||||||
|
* @return SWITCH_STATUS_SUCCESS if successful otherwise SWITCH_STATUS_GENERR
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_init(switch_timer_t *timer)
|
||||||
|
{
|
||||||
|
interval_timer_t *it;
|
||||||
|
switch_status_t status;
|
||||||
|
int interval_timer_id;
|
||||||
|
|
||||||
|
if (timer->interval < 1 || timer->interval > MAX_INTERVAL) {
|
||||||
|
return SWITCH_STATUS_GENERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_mutex_lock(globals.interval_timers_mutex);
|
||||||
|
interval_timer_id = globals.next_interval_timer_id[timer->interval]++;
|
||||||
|
if (globals.next_interval_timer_id[timer->interval] >= TIMERS_PER_INTERVAL) {
|
||||||
|
globals.next_interval_timer_id[timer->interval] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = &globals.interval_timers[timer->interval][interval_timer_id];
|
||||||
|
status = posix_timer_start_interval(it, timer->interval);
|
||||||
|
timer->private_info = it;
|
||||||
|
switch_mutex_unlock(globals.interval_timers_mutex);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer module interface: step the timer
|
||||||
|
* @param timer the timer
|
||||||
|
* @return SWITCH_STATUS_SUCCESS
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_step(switch_timer_t *timer)
|
||||||
|
{
|
||||||
|
timer->tick++;
|
||||||
|
timer->samplecount += timer->samples;
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer module interface: wait for next tick
|
||||||
|
* @param timer the timer
|
||||||
|
* @return SWITCH_STATUS_SUCCESS if successful
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_next(switch_timer_t *timer)
|
||||||
|
{
|
||||||
|
interval_timer_t *it = timer->private_info;
|
||||||
|
|
||||||
|
if ((int)(timer->tick - it->tick) < -1) {
|
||||||
|
timer->tick = it->tick;
|
||||||
|
}
|
||||||
|
posix_timer_step(timer);
|
||||||
|
|
||||||
|
switch_mutex_lock(it->mutex);
|
||||||
|
while ((int)(timer->tick - it->tick) > 0 && !globals.shutdown) {
|
||||||
|
switch_thread_cond_timedwait(it->cond, it->mutex, 20 * 1000);
|
||||||
|
}
|
||||||
|
switch_mutex_unlock(it->mutex);
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer module interface: sync tick count
|
||||||
|
* @param timer the timer
|
||||||
|
* @return SWITCH_STATUS_SUCCESS
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_sync(switch_timer_t *timer)
|
||||||
|
{
|
||||||
|
interval_timer_t *it = timer->private_info;
|
||||||
|
timer->tick = it->tick;
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer module interface: check if synched
|
||||||
|
* @param timer the timer
|
||||||
|
* @param step true if timer should be stepped
|
||||||
|
* @return SWITCH_STATUS_SUCCESS if synched, SWITCH_STATUS_FALSE otherwise
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_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;
|
||||||
|
}
|
||||||
|
/* timer pending */
|
||||||
|
timer->diff = 0;
|
||||||
|
if (step) {
|
||||||
|
posix_timer_step(timer);
|
||||||
|
}
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer module interface: destroy a timer
|
||||||
|
* @param timer the timer
|
||||||
|
* @return SWITCH_STATUS_SUCCESS if successful
|
||||||
|
*/
|
||||||
|
static switch_status_t posix_timer_destroy(switch_timer_t *timer)
|
||||||
|
{
|
||||||
|
interval_timer_t *it = timer->private_info;
|
||||||
|
switch_status_t status;
|
||||||
|
|
||||||
|
switch_mutex_lock(globals.interval_timers_mutex);
|
||||||
|
status = posix_timer_stop_interval(it);
|
||||||
|
switch_mutex_unlock(globals.interval_timers_mutex);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWITCH_MODULE_LOAD_FUNCTION(mod_posix_timer_load)
|
||||||
|
{
|
||||||
|
switch_timer_interface_t *timer_interface;
|
||||||
|
|
||||||
|
memset(&globals, 0, sizeof(globals));
|
||||||
|
|
||||||
|
globals.pool = pool;
|
||||||
|
switch_mutex_init(&globals.interval_timers_mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||||
|
|
||||||
|
/* connect my internal structure to the blank pointer passed to me */
|
||||||
|
*module_interface = switch_loadable_module_create_module_interface(globals.pool, modname);
|
||||||
|
timer_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_TIMER_INTERFACE);
|
||||||
|
timer_interface->interface_name = "posix";
|
||||||
|
timer_interface->timer_init = posix_timer_init;
|
||||||
|
timer_interface->timer_next = posix_timer_next;
|
||||||
|
timer_interface->timer_step = posix_timer_step;
|
||||||
|
timer_interface->timer_sync = posix_timer_sync;
|
||||||
|
timer_interface->timer_check = posix_timer_check;
|
||||||
|
timer_interface->timer_destroy = posix_timer_destroy;
|
||||||
|
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_posix_timer_shutdown)
|
||||||
|
{
|
||||||
|
globals.shutdown = 1;
|
||||||
|
return SWITCH_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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:
|
||||||
|
*/
|
Loading…
Reference in New Issue