2006-01-03 22:13:59 +00:00
/*
* FreeSWITCH Modular Media Switching Software Library / Soft - Switch Application
2011-01-05 16:08:55 +00:00
* Copyright ( C ) 2005 - 2011 , Anthony Minessale II < anthm @ freeswitch . org >
2006-01-03 22:13:59 +00:00
*
* 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
2009-02-04 21:20:54 +00:00
* Anthony Minessale II < anthm @ freeswitch . org >
2006-01-03 22:13:59 +00:00
* Portions created by the Initial Developer are Copyright ( C )
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
*
2009-02-04 21:20:54 +00:00
* Anthony Minessale II < anthm @ freeswitch . org >
2008-09-29 16:58:31 +00:00
* Massimo Cetra < devel @ navynet . it > - Timezone functionality
2006-01-03 22:13:59 +00:00
*
*
2007-10-03 16:44:11 +00:00
* softtimer . c - - Software Timer Module
2006-01-03 22:13:59 +00:00
*
*/
2008-01-27 17:42:51 +00:00
2006-01-03 22:13:59 +00:00
# include <switch.h>
# include <stdio.h>
2007-10-03 16:44:11 +00:00
# include "private/switch_core_pvt.h"
2011-03-22 01:49:39 +00:00
# ifdef HAVE_TIMERFD_CREATE
# include <sys/timerfd.h>
# endif
2006-01-03 22:13:59 +00:00
2009-05-14 00:57:52 +00:00
//#if defined(DARWIN)
2008-11-13 16:36:55 +00:00
# define DISABLE_1MS_COND
2009-05-14 00:57:52 +00:00
//#endif
2008-11-13 16:36:55 +00:00
2007-03-06 01:19:41 +00:00
# ifndef UINT32_MAX
# define UINT32_MAX 0xffffffff
# endif
# define MAX_TICK UINT32_MAX - 1024
2008-12-17 01:53:46 +00:00
2009-12-30 20:55:04 +00:00
# define MAX_ELEMENTS 3600
# define IDLE_SPEED 100
2010-07-29 01:44:45 +00:00
/* In Windows, enable the montonic timer for better timer accuracy on Windows 2003 Server, XP and older */
/* GetSystemTimeAsFileTime does not update on timeBeginPeriod on these OS. */
/* Flag SCF_USE_WIN32_MONOTONIC must be enabled to activate it (start parameter -monotonic-clock) */
# if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
2009-12-30 20:55:04 +00:00
static int MONO = 1 ;
# else
static int MONO = 0 ;
# endif
2011-03-22 01:49:39 +00:00
# if defined(HAVE_TIMERFD_CREATE)
// We'll default this to 1 after we have had some positive feedback that it works well
static int TFD = 0 ;
# else
static int TFD = 0 ;
# endif
2009-12-30 20:55:04 +00:00
static int NANO = 0 ;
static int OFFSET = 0 ;
2010-03-01 19:25:27 +00:00
static int COND = 1 ;
2009-12-30 20:55:04 +00:00
static int MATRIX = 1 ;
2010-07-15 13:50:45 +00:00
# ifdef WIN32
static switch_time_t win32_tick_time_since_start = - 1 ;
static DWORD win32_last_get_time_tick = 0 ;
CRITICAL_SECTION timer_section ;
# endif
2010-03-01 19:25:27 +00:00
static int STEP_MS = 1 ;
static int STEP_MIC = 1000 ;
2010-03-03 16:23:22 +00:00
static uint32_t TICK_PER_SEC = 1000 ;
2010-03-02 20:57:55 +00:00
static int MS_PER_TICK = 10 ;
2008-12-17 01:53:46 +00:00
2006-09-16 20:18:24 +00:00
static switch_memory_pool_t * module_pool = NULL ;
static struct {
int32_t RUNNING ;
2007-06-24 23:09:28 +00:00
int32_t STARTED ;
2008-11-10 18:26:41 +00:00
int32_t use_cond_yield ;
2006-09-16 20:18:24 +00:00
switch_mutex_t * mutex ;
2010-03-01 19:25:27 +00:00
uint32_t timer_count ;
2006-09-16 20:18:24 +00:00
} globals ;
2006-01-03 22:13:59 +00:00
2007-10-12 23:34:30 +00:00
# ifdef WIN32
2007-10-12 22:54:18 +00:00
# undef SWITCH_MOD_DECLARE_DATA
# define SWITCH_MOD_DECLARE_DATA __declspec(dllexport)
2007-10-12 23:34:30 +00:00
# endif
2007-10-12 22:54:18 +00:00
2007-10-03 16:44:11 +00:00
SWITCH_MODULE_LOAD_FUNCTION ( softtimer_load ) ;
SWITCH_MODULE_SHUTDOWN_FUNCTION ( softtimer_shutdown ) ;
SWITCH_MODULE_RUNTIME_FUNCTION ( softtimer_runtime ) ;
2008-02-20 20:26:07 +00:00
SWITCH_MODULE_DEFINITION ( CORE_SOFTTIMER_MODULE , softtimer_load , softtimer_shutdown , softtimer_runtime ) ;
2007-06-13 17:06:10 +00:00
2006-01-03 22:13:59 +00:00
struct timer_private {
2007-03-29 22:31:56 +00:00
switch_size_t reference ;
switch_size_t start ;
2007-03-06 01:19:41 +00:00
uint32_t roll ;
2007-03-07 23:24:09 +00:00
uint32_t ready ;
2006-01-03 22:13:59 +00:00
} ;
2006-09-16 20:18:24 +00:00
typedef struct timer_private timer_private_t ;
2006-01-03 22:13:59 +00:00
2006-09-16 20:18:24 +00:00
struct timer_matrix {
2007-03-05 20:53:54 +00:00
switch_size_t tick ;
2006-09-16 20:18:24 +00:00
uint32_t count ;
2007-03-06 01:19:41 +00:00
uint32_t roll ;
2008-11-10 18:26:41 +00:00
switch_mutex_t * mutex ;
switch_thread_cond_t * cond ;
2009-12-29 23:21:20 +00:00
switch_thread_rwlock_t * rwlock ;
2006-09-16 20:18:24 +00:00
} ;
typedef struct timer_matrix timer_matrix_t ;
2006-01-03 22:13:59 +00:00
2007-03-29 22:31:56 +00:00
static timer_matrix_t TIMER_MATRIX [ MAX_ELEMENTS + 1 ] ;
2006-01-03 22:13:59 +00:00
2009-12-30 20:55:04 +00:00
static void os_yield ( void )
{
# if defined(WIN32)
SwitchToThread ( ) ;
# else
sched_yield ( ) ;
# endif
}
2008-11-13 16:36:55 +00:00
static void do_sleep ( switch_interval_time_t t )
{
2009-12-29 23:21:20 +00:00
# if defined(HAVE_CLOCK_NANOSLEEP) || defined(DARWIN)
struct timespec ts ;
# endif
2008-11-13 16:36:55 +00:00
# if defined(WIN32)
if ( t < 1000 ) {
t = 1000 ;
}
# endif
2009-12-29 23:21:20 +00:00
# if !defined(DARWIN)
2009-12-30 20:55:04 +00:00
if ( t > 100000 | | ! NANO ) {
2009-12-29 23:21:20 +00:00
apr_sleep ( t ) ;
2009-12-30 20:55:04 +00:00
return ;
2009-12-29 23:21:20 +00:00
}
# endif
2010-02-06 03:38:24 +00:00
2009-12-29 23:21:20 +00:00
# if defined(HAVE_CLOCK_NANOSLEEP)
2009-12-30 20:55:04 +00:00
t - = OFFSET ;
ts . tv_sec = t / 1000000 ;
ts . tv_nsec = ( ( t % 1000000 ) * 1000 ) ;
2009-12-29 23:21:20 +00:00
clock_nanosleep ( CLOCK_MONOTONIC , 0 , & ts , NULL ) ;
2009-12-30 20:55:04 +00:00
2009-12-29 23:21:20 +00:00
# elif defined(DARWIN)
2009-02-26 18:28:26 +00:00
ts . tv_sec = t / APR_USEC_PER_SEC ;
ts . tv_nsec = ( t % APR_USEC_PER_SEC ) * 1000 ;
nanosleep ( & ts , NULL ) ;
2008-11-13 16:36:55 +00:00
# else
apr_sleep ( t ) ;
# endif
2009-12-29 23:21:20 +00:00
# if defined(DARWIN)
2010-02-06 03:38:24 +00:00
sched_yield ( ) ;
2009-12-29 23:21:20 +00:00
# endif
2008-11-13 16:36:55 +00:00
}
2009-12-30 20:55:04 +00:00
static switch_interval_time_t average_time ( switch_interval_time_t t , int reps )
{
int x = 0 ;
switch_time_t start , stop , sum = 0 ;
2010-02-06 03:38:24 +00:00
for ( x = 0 ; x < reps ; x + + ) {
2010-07-15 13:50:45 +00:00
start = switch_time_ref ( ) ;
2009-12-30 20:55:04 +00:00
do_sleep ( t ) ;
2010-07-15 13:50:45 +00:00
stop = switch_time_ref ( ) ;
2009-12-30 20:55:04 +00:00
sum + = ( stop - start ) ;
}
return sum / reps ;
2010-02-06 03:38:24 +00:00
2009-12-30 20:55:04 +00:00
}
2010-01-13 22:36:13 +00:00
# define calc_step() if (step > 11) step -= 10; else if (step > 1) step--
2010-01-14 16:02:46 +00:00
SWITCH_DECLARE ( void ) switch_time_calibrate_clock ( void )
2009-12-30 20:55:04 +00:00
{
int x ;
switch_interval_time_t avg , val = 1000 , want = 1000 ;
2010-02-22 22:42:29 +00:00
int over = 0 , under = 0 , good = 0 , step = 50 , diff = 0 , retry = 0 , lastgood = 0 , one_k = 0 ;
2010-01-13 21:07:39 +00:00
# ifdef HAVE_CLOCK_GETRES
struct timespec ts ;
2010-02-22 22:42:29 +00:00
long res = 0 ;
2010-01-13 21:07:39 +00:00
clock_getres ( CLOCK_MONOTONIC , & ts ) ;
2010-02-22 22:42:29 +00:00
res = ts . tv_nsec / 1000 ;
if ( res > 900 & & res < 1100 ) {
one_k = 1 ;
}
if ( res > 1500 ) {
2010-01-13 22:36:13 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING ,
2010-01-14 16:02:46 +00:00
" Timer resolution of %ld microseconds detected! \n "
2010-12-27 21:35:10 +00:00
" Do you have your kernel timer frequency set to lower than 1,000Hz? "
" You may experience audio problems. Step MS %d \n " , ts . tv_nsec / 1000 , STEP_MS ) ;
2010-01-14 16:02:46 +00:00
do_sleep ( 5000000 ) ;
switch_time_set_cond_yield ( SWITCH_TRUE ) ;
2010-01-13 21:07:39 +00:00
return ;
}
# endif
2009-12-30 20:55:04 +00:00
2010-02-06 03:38:24 +00:00
top :
2010-01-19 18:23:18 +00:00
val = 1000 ;
step = 50 ;
over = under = good = 0 ;
2010-01-14 16:02:46 +00:00
OFFSET = 0 ;
2010-02-06 03:38:24 +00:00
2010-01-15 21:09:51 +00:00
for ( x = 0 ; x < 100 ; x + + ) {
2010-01-14 16:02:46 +00:00
avg = average_time ( val , 50 ) ;
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Test: %ld Average: %ld Step: %d \n " , ( long ) val , ( long ) avg , step ) ;
2010-01-13 18:02:42 +00:00
2010-02-06 03:38:24 +00:00
diff = abs ( ( int ) ( want - avg ) ) ;
2010-01-22 04:04:34 +00:00
if ( diff > 1500 ) {
2010-02-06 03:38:24 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_WARNING ,
2010-01-13 22:36:13 +00:00
" Abnormally large timer gap %d detected! \n "
2010-03-31 17:28:50 +00:00
" Do you have your kernel timer frequency set to lower than 1,000Hz? You may experience audio problems. \n " , diff ) ;
2010-01-14 16:02:46 +00:00
do_sleep ( 5000000 ) ;
switch_time_set_cond_yield ( SWITCH_TRUE ) ;
2010-01-13 21:07:39 +00:00
return ;
}
2010-02-06 03:38:24 +00:00
2010-01-22 03:58:16 +00:00
if ( diff < = 100 ) {
2010-01-22 14:55:33 +00:00
lastgood = ( int ) val ;
2010-02-06 03:38:24 +00:00
}
2010-01-13 21:07:39 +00:00
if ( diff < = 2 ) {
2010-01-13 18:02:42 +00:00
under = over = 0 ;
2010-01-22 14:55:33 +00:00
lastgood = ( int ) val ;
2009-12-30 20:55:04 +00:00
if ( + + good > 10 ) {
break ;
}
} else if ( avg > want ) {
2010-02-06 03:38:24 +00:00
if ( under ) {
calc_step ( ) ;
}
2010-01-14 16:02:46 +00:00
under = good = 0 ;
2010-01-19 18:23:18 +00:00
if ( ( val - step ) < 0 ) {
2010-02-06 03:38:24 +00:00
if ( + + retry > 2 )
break ;
2010-01-19 18:23:18 +00:00
goto top ;
}
2010-01-13 18:02:42 +00:00
val - = step ;
2010-01-13 22:36:13 +00:00
over + + ;
2009-12-30 20:55:04 +00:00
} else if ( avg < want ) {
2010-02-06 03:38:24 +00:00
if ( over ) {
calc_step ( ) ;
}
2010-01-14 16:02:46 +00:00
over = good = 0 ;
2010-01-19 18:23:18 +00:00
if ( ( val - step ) < 0 ) {
2010-02-06 03:38:24 +00:00
if ( + + retry > 2 )
break ;
2010-01-19 18:23:18 +00:00
goto top ;
}
2010-01-13 18:02:42 +00:00
val + = step ;
2010-01-13 22:36:13 +00:00
under + + ;
2009-12-30 20:55:04 +00:00
}
}
2010-01-15 21:09:51 +00:00
if ( good > = 10 ) {
2010-02-06 03:38:24 +00:00
OFFSET = ( int ) ( want - val ) ;
2010-01-15 21:09:51 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Timer offset of %d calculated \n " , OFFSET ) ;
2010-01-22 03:58:16 +00:00
} else if ( lastgood ) {
2010-02-06 03:38:24 +00:00
OFFSET = ( int ) ( want - lastgood ) ;
2010-01-22 03:58:16 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Timer offset of %d calculated (fallback) \n " , OFFSET ) ;
2010-01-22 04:04:34 +00:00
switch_time_set_cond_yield ( SWITCH_TRUE ) ;
2010-02-22 22:42:29 +00:00
} else if ( one_k ) {
OFFSET = 900 ;
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Timer offset CANNOT BE DETECTED, forcing OFFSET to 900 \n " ) ;
switch_time_set_cond_yield ( SWITCH_TRUE ) ;
2010-01-15 21:09:51 +00:00
} else {
2010-01-22 04:04:34 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Timer offset NOT calculated \n " ) ;
switch_time_set_cond_yield ( SWITCH_TRUE ) ;
2010-01-15 21:09:51 +00:00
}
2009-12-30 20:55:04 +00:00
}
2009-01-25 21:23:07 +00:00
SWITCH_DECLARE ( switch_time_t ) switch_micro_time_now ( void )
2008-01-11 00:43:49 +00:00
{
2009-03-02 15:45:00 +00:00
return ( globals . RUNNING = = 1 & & runtime . timestamp ) ? runtime . timestamp : switch_time_now ( ) ;
2008-01-11 00:43:49 +00:00
}
2009-01-25 21:23:07 +00:00
SWITCH_DECLARE ( time_t ) switch_epoch_time_now ( time_t * t )
2008-01-11 00:43:49 +00:00
{
2009-01-25 21:23:07 +00:00
time_t now = switch_micro_time_now ( ) / APR_USEC_PER_SEC ;
2008-01-11 00:43:49 +00:00
if ( t ) {
* t = now ;
}
return now ;
}
2008-05-27 04:30:03 +00:00
SWITCH_DECLARE ( void ) switch_time_set_monotonic ( switch_bool_t enable )
2008-04-16 22:02:06 +00:00
{
2011-03-22 01:49:39 +00:00
# if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
2008-04-16 22:02:06 +00:00
MONO = enable ? 1 : 0 ;
switch_time_sync ( ) ;
2011-03-22 01:49:39 +00:00
# else
MONO = 0 ;
# endif
}
SWITCH_DECLARE ( void ) switch_time_set_timerfd ( switch_bool_t enable )
{
# if defined(HAVE_TIMERFD_CREATE)
TFD = enable ? 1 : 0 ;
switch_time_sync ( ) ;
# else
TFD = 0 ;
# endif
2008-04-16 22:02:06 +00:00
}
2009-12-30 20:55:04 +00:00
SWITCH_DECLARE ( void ) switch_time_set_matrix ( switch_bool_t enable )
{
MATRIX = enable ? 1 : 0 ;
switch_time_sync ( ) ;
}
2009-12-29 23:21:20 +00:00
SWITCH_DECLARE ( void ) switch_time_set_nanosleep ( switch_bool_t enable )
{
2010-02-06 03:38:24 +00:00
# if defined(HAVE_CLOCK_NANOSLEEP)
2009-12-29 23:21:20 +00:00
NANO = enable ? 1 : 0 ;
2009-12-30 20:55:04 +00:00
# endif
2009-12-29 23:21:20 +00:00
}
2009-12-30 15:47:31 +00:00
SWITCH_DECLARE ( void ) switch_time_set_cond_yield ( switch_bool_t enable )
{
2009-12-30 20:55:04 +00:00
COND = enable ? 1 : 0 ;
if ( COND ) {
MATRIX = 1 ;
}
2010-01-14 16:02:46 +00:00
switch_time_sync ( ) ;
2009-12-30 15:47:31 +00:00
}
2008-01-11 00:43:49 +00:00
static switch_time_t time_now ( int64_t offset )
{
switch_time_t now ;
2010-07-29 01:44:45 +00:00
# if (defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)) || defined(WIN32)
2008-01-23 15:26:57 +00:00
if ( MONO ) {
2010-07-15 13:50:45 +00:00
# ifndef WIN32
2008-01-23 15:26:57 +00:00
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
2008-05-27 04:30:03 +00:00
now = ts . tv_sec * APR_USEC_PER_SEC + ( ts . tv_nsec / 1000 ) + offset ;
2010-07-15 13:50:45 +00:00
# else
DWORD tick_now ;
DWORD tick_diff ;
tick_now = timeGetTime ( ) ;
if ( win32_tick_time_since_start ! = - 1 ) {
EnterCriticalSection ( & timer_section ) ;
/* just add diff (to make it work more than 50 days). */
tick_diff = tick_now - win32_last_get_time_tick ;
win32_tick_time_since_start + = tick_diff ;
win32_last_get_time_tick = tick_now ;
now = ( win32_tick_time_since_start * 1000 ) + offset ;
LeaveCriticalSection ( & timer_section ) ;
} else {
/* If someone is calling us before timer is initialized,
* return the current tick + offset
*/
now = ( tick_now * 1000 ) + offset ;
}
# endif
2008-01-23 15:26:57 +00:00
} else {
# endif
2008-04-16 22:02:06 +00:00
now = switch_time_now ( ) ;
2008-01-23 15:26:57 +00:00
2010-07-29 01:44:45 +00:00
# if (defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)) || defined(WIN32)
2008-01-23 15:26:57 +00:00
}
2008-01-11 00:43:49 +00:00
# endif
return now ;
}
2010-07-15 13:50:45 +00:00
SWITCH_DECLARE ( switch_time_t ) switch_time_ref ( void )
{
return time_now ( 0 ) ;
}
2008-01-11 00:43:49 +00:00
SWITCH_DECLARE ( void ) switch_time_sync ( void )
{
runtime . reference = switch_time_now ( ) ;
runtime . offset = runtime . reference - time_now ( 0 ) ;
runtime . reference = time_now ( runtime . offset ) ;
}
2008-11-12 13:55:22 +00:00
SWITCH_DECLARE ( void ) switch_micro_sleep ( switch_interval_time_t t )
{
2008-11-13 16:36:55 +00:00
do_sleep ( t ) ;
2008-11-12 13:55:22 +00:00
}
2007-11-13 18:47:08 +00:00
SWITCH_DECLARE ( void ) switch_sleep ( switch_interval_time_t t )
{
2008-01-03 21:41:15 +00:00
2010-01-13 18:02:42 +00:00
if ( globals . RUNNING ! = 1 | | t < 1000 | | t > = 10000 ) {
2008-11-13 16:36:55 +00:00
do_sleep ( t ) ;
2008-11-12 13:55:22 +00:00
return ;
}
2008-11-13 16:36:55 +00:00
# ifndef DISABLE_1MS_COND
2008-11-10 18:26:41 +00:00
if ( globals . use_cond_yield = = 1 ) {
2008-11-14 23:31:21 +00:00
switch_cond_yield ( t ) ;
2008-11-10 18:26:41 +00:00
return ;
}
2010-02-06 03:38:24 +00:00
# endif
2008-11-13 16:36:55 +00:00
do_sleep ( t ) ;
2007-11-13 18:47:08 +00:00
}
2008-11-10 18:26:41 +00:00
2008-11-14 23:31:21 +00:00
SWITCH_DECLARE ( void ) switch_cond_next ( void )
2008-11-10 18:26:41 +00:00
{
2010-03-01 19:25:27 +00:00
if ( globals . timer_count > = runtime . tipping_point ) {
2009-12-30 20:55:04 +00:00
os_yield ( ) ;
return ;
}
2008-11-14 23:31:21 +00:00
# ifdef DISABLE_1MS_COND
2009-06-25 00:03:41 +00:00
do_sleep ( 1000 ) ;
2008-11-14 23:31:21 +00:00
# else
2009-03-02 15:45:00 +00:00
if ( globals . RUNNING ! = 1 | | ! runtime . timestamp | | globals . use_cond_yield ! = 1 ) {
2008-11-14 23:31:21 +00:00
do_sleep ( 1000 ) ;
2008-11-12 13:55:22 +00:00
return ;
2008-11-10 18:26:41 +00:00
}
2008-11-11 19:45:28 +00:00
switch_mutex_lock ( TIMER_MATRIX [ 1 ] . mutex ) ;
2008-11-14 23:31:21 +00:00
switch_thread_cond_wait ( TIMER_MATRIX [ 1 ] . cond , TIMER_MATRIX [ 1 ] . mutex ) ;
2008-11-11 19:45:28 +00:00
switch_mutex_unlock ( TIMER_MATRIX [ 1 ] . mutex ) ;
2008-11-14 23:31:21 +00:00
# endif
}
SWITCH_DECLARE ( void ) switch_cond_yield ( switch_interval_time_t t )
{
switch_time_t want ;
2010-02-06 03:38:24 +00:00
if ( ! t )
return ;
2008-11-14 23:31:21 +00:00
2009-03-02 15:45:00 +00:00
if ( globals . RUNNING ! = 1 | | ! runtime . timestamp | | globals . use_cond_yield ! = 1 ) {
2008-11-14 23:31:21 +00:00
do_sleep ( t ) ;
return ;
}
want = runtime . timestamp + t ;
2010-02-06 03:38:24 +00:00
while ( globals . RUNNING = = 1 & & globals . use_cond_yield = = 1 & & runtime . timestamp < want ) {
2008-11-14 23:31:21 +00:00
switch_mutex_lock ( TIMER_MATRIX [ 1 ] . mutex ) ;
if ( runtime . timestamp < want ) {
switch_thread_cond_wait ( TIMER_MATRIX [ 1 ] . cond , TIMER_MATRIX [ 1 ] . mutex ) ;
}
switch_mutex_unlock ( TIMER_MATRIX [ 1 ] . mutex ) ;
}
2008-11-11 19:45:28 +00:00
2008-11-10 18:26:41 +00:00
}
2007-08-20 18:06:08 +00:00
static switch_status_t timer_init ( switch_timer_t * timer )
2006-09-16 20:18:24 +00:00
{
timer_private_t * private_info ;
2007-06-24 23:09:28 +00:00
int sanity = 0 ;
2008-05-27 04:30:03 +00:00
while ( globals . STARTED = = 0 ) {
2008-11-13 16:36:55 +00:00
do_sleep ( 100000 ) ;
2009-10-23 16:33:55 +00:00
if ( + + sanity = = 300 ) {
abort ( ) ;
2007-06-24 23:09:28 +00:00
}
}
2006-09-16 20:18:24 +00:00
2009-01-19 16:53:51 +00:00
if ( globals . RUNNING ! = 1 | | ! globals . mutex | | timer - > interval < 1 ) {
2007-02-28 15:14:39 +00:00
return SWITCH_STATUS_FALSE ;
}
2006-09-16 20:18:24 +00:00
if ( ( private_info = switch_core_alloc ( timer - > memory_pool , sizeof ( * private_info ) ) ) ) {
switch_mutex_lock ( globals . mutex ) ;
2008-11-10 18:26:41 +00:00
if ( ! TIMER_MATRIX [ timer - > interval ] . mutex ) {
switch_mutex_init ( & TIMER_MATRIX [ timer - > interval ] . mutex , SWITCH_MUTEX_NESTED , module_pool ) ;
switch_thread_cond_create ( & TIMER_MATRIX [ timer - > interval ] . cond , module_pool ) ;
}
2006-09-16 20:18:24 +00:00
TIMER_MATRIX [ timer - > interval ] . count + + ;
switch_mutex_unlock ( globals . mutex ) ;
timer - > private_info = private_info ;
2007-03-06 01:19:41 +00:00
private_info - > start = private_info - > reference = TIMER_MATRIX [ timer - > interval ] . tick ;
2010-07-30 07:20:54 +00:00
private_info - > start - = 2 ; /* switch_core_timer_init sets samplecount to samples, this makes first next() step once */
2007-03-06 01:19:41 +00:00
private_info - > roll = TIMER_MATRIX [ timer - > interval ] . roll ;
2007-03-07 23:24:09 +00:00
private_info - > ready = 1 ;
2008-12-17 01:53:46 +00:00
2009-01-19 16:53:51 +00:00
if ( timer - > interval > 0 & & timer - > interval < MS_PER_TICK ) {
2008-12-17 01:53:46 +00:00
MS_PER_TICK = timer - > interval ;
2009-12-30 20:55:04 +00:00
switch_time_sync ( ) ;
2008-12-17 01:53:46 +00:00
}
2010-03-01 19:25:27 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . timer_count + + ;
if ( globals . timer_count = = ( runtime . tipping_point + 1 ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " Crossed tipping point of %u, shifting into high-gear. \n " , runtime . tipping_point ) ;
}
switch_mutex_unlock ( globals . mutex ) ;
2006-09-16 20:18:24 +00:00
return SWITCH_STATUS_SUCCESS ;
2006-01-03 22:13:59 +00:00
}
2006-09-16 20:18:24 +00:00
return SWITCH_STATUS_MEMERR ;
}
2008-04-16 22:02:06 +00:00
# define check_roll() if (private_info->roll < TIMER_MATRIX[timer->interval].roll) { \
private_info - > roll + + ; \
private_info - > reference = private_info - > start = TIMER_MATRIX [ timer - > interval ] . tick ; \
} \
2008-05-27 04:30:03 +00:00
2007-08-20 18:06:08 +00:00
static switch_status_t timer_step ( switch_timer_t * timer )
2006-09-16 20:18:24 +00:00
{
timer_private_t * private_info = timer - > private_info ;
2007-03-06 01:19:41 +00:00
uint64_t samples ;
2006-09-16 20:18:24 +00:00
2007-03-07 23:24:09 +00:00
if ( globals . RUNNING ! = 1 | | private_info - > ready = = 0 ) {
2007-02-28 15:14:39 +00:00
return SWITCH_STATUS_FALSE ;
}
2008-05-27 04:30:03 +00:00
2007-03-06 01:19:41 +00:00
check_roll ( ) ;
samples = timer - > samples * ( private_info - > reference - private_info - > start ) ;
2007-03-29 22:31:56 +00:00
2007-03-06 01:19:41 +00:00
if ( samples > UINT32_MAX ) {
private_info - > start = private_info - > reference ;
samples = timer - > samples ;
}
2007-03-05 20:53:54 +00:00
2007-03-29 22:31:56 +00:00
timer - > samplecount = ( uint32_t ) samples ;
2008-03-11 16:55:58 +00:00
private_info - > reference + + ;
2008-05-27 04:30:03 +00:00
2008-03-11 16:55:58 +00:00
return SWITCH_STATUS_SUCCESS ;
}
static switch_status_t timer_sync ( switch_timer_t * timer )
{
timer_private_t * private_info = timer - > private_info ;
if ( globals . RUNNING ! = 1 | | private_info - > ready = = 0 ) {
return SWITCH_STATUS_FALSE ;
}
2007-03-29 22:31:56 +00:00
2008-03-11 16:55:58 +00:00
/* sync the clock */
private_info - > reference = timer - > tick = TIMER_MATRIX [ timer - > interval ] . tick ;
/* apply timestamp */
2010-07-29 22:41:23 +00:00
timer_step ( timer ) ;
2008-05-27 04:30:03 +00:00
2007-03-29 22:31:56 +00:00
return SWITCH_STATUS_SUCCESS ;
2006-01-03 22:13:59 +00:00
}
2008-03-11 16:55:58 +00:00
2007-08-20 18:06:08 +00:00
static switch_status_t timer_next ( switch_timer_t * timer )
2006-09-12 22:23:45 +00:00
{
2006-09-16 20:18:24 +00:00
timer_private_t * private_info = timer - > private_info ;
2007-02-28 15:14:39 +00:00
2008-11-14 23:31:21 +00:00
# ifdef DISABLE_1MS_COND
int cond_index = timer - > interval ;
2008-11-10 18:26:41 +00:00
# else
2008-11-14 23:31:21 +00:00
int cond_index = 1 ;
# endif
2010-02-06 03:38:24 +00:00
int delta = ( int ) ( private_info - > reference - TIMER_MATRIX [ timer - > interval ] . tick ) ;
2008-11-14 23:31:21 +00:00
2009-04-27 14:14:42 +00:00
/* sync up timer if it's not been called for a while otherwise it will return instantly several times until it catches up */
2010-02-06 03:38:24 +00:00
if ( delta < - 1 ) {
2009-04-27 14:14:42 +00:00
private_info - > reference = timer - > tick = TIMER_MATRIX [ timer - > interval ] . tick ;
}
2008-11-14 23:31:21 +00:00
timer_step ( timer ) ;
2008-12-17 01:53:46 +00:00
2009-12-30 20:55:04 +00:00
if ( ! MATRIX ) {
do_sleep ( 1000 * timer - > interval ) ;
goto end ;
}
2008-11-10 18:26:41 +00:00
while ( globals . RUNNING = = 1 & & private_info - > ready & & TIMER_MATRIX [ timer - > interval ] . tick < private_info - > reference ) {
check_roll ( ) ;
2010-02-06 03:38:24 +00:00
2010-03-01 19:25:27 +00:00
if ( globals . timer_count > = runtime . tipping_point ) {
2010-01-14 16:02:46 +00:00
os_yield ( ) ;
2010-03-01 19:25:27 +00:00
globals . use_cond_yield = 0 ;
2009-12-30 20:55:04 +00:00
} else {
2010-01-14 16:02:46 +00:00
if ( globals . use_cond_yield = = 1 ) {
switch_mutex_lock ( TIMER_MATRIX [ cond_index ] . mutex ) ;
if ( TIMER_MATRIX [ timer - > interval ] . tick < private_info - > reference ) {
2010-02-06 03:38:24 +00:00
switch_thread_cond_wait ( TIMER_MATRIX [ cond_index ] . cond , TIMER_MATRIX [ cond_index ] . mutex ) ;
2010-01-14 16:02:46 +00:00
}
switch_mutex_unlock ( TIMER_MATRIX [ cond_index ] . mutex ) ;
2009-12-29 23:21:20 +00:00
} else {
do_sleep ( 1000 ) ;
2008-11-14 23:31:21 +00:00
}
}
2008-11-10 18:26:41 +00:00
}
2009-12-29 23:21:20 +00:00
2010-02-06 03:38:24 +00:00
end :
2009-12-30 20:55:04 +00:00
return globals . RUNNING = = 1 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE ;
2006-09-12 22:23:45 +00:00
}
2008-03-11 16:55:58 +00:00
static switch_status_t timer_check ( switch_timer_t * timer , switch_bool_t step )
2006-09-12 22:23:45 +00:00
{
2006-09-16 20:18:24 +00:00
timer_private_t * private_info = timer - > private_info ;
2007-02-07 18:44:00 +00:00
switch_status_t status = SWITCH_STATUS_SUCCESS ;
2008-01-03 21:34:44 +00:00
2007-03-07 23:24:09 +00:00
if ( globals . RUNNING ! = 1 | | ! private_info - > ready ) {
2007-02-28 15:14:39 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2007-03-06 01:19:41 +00:00
check_roll ( ) ;
2008-03-11 16:55:58 +00:00
timer - > tick = TIMER_MATRIX [ timer - > interval ] . tick ;
if ( timer - > tick < private_info - > reference ) {
timer - > diff = private_info - > reference - timer - > tick ;
2006-09-12 22:23:45 +00:00
} else {
2008-01-03 21:34:44 +00:00
timer - > diff = 0 ;
2006-09-12 22:23:45 +00:00
}
2008-05-27 04:30:03 +00:00
if ( timer - > diff ) {
2007-03-29 22:31:56 +00:00
status = SWITCH_STATUS_FALSE ;
2008-03-11 16:55:58 +00:00
} else if ( step ) {
2007-03-29 22:31:56 +00:00
timer_step ( timer ) ;
}
2007-02-07 18:44:00 +00:00
2008-03-11 16:55:58 +00:00
2006-09-16 20:18:24 +00:00
return status ;
2006-09-12 22:23:45 +00:00
}
2007-08-20 18:06:08 +00:00
static switch_status_t timer_destroy ( switch_timer_t * timer )
2006-01-03 22:13:59 +00:00
{
2007-03-07 23:24:09 +00:00
timer_private_t * private_info = timer - > private_info ;
2008-08-15 21:57:57 +00:00
if ( timer - > interval < MAX_ELEMENTS ) {
switch_mutex_lock ( globals . mutex ) ;
TIMER_MATRIX [ timer - > interval ] . count - - ;
if ( TIMER_MATRIX [ timer - > interval ] . count = = 0 ) {
TIMER_MATRIX [ timer - > interval ] . tick = 0 ;
}
switch_mutex_unlock ( globals . mutex ) ;
}
if ( private_info ) {
private_info - > ready = 0 ;
2007-03-06 01:19:41 +00:00
}
2010-03-01 19:25:27 +00:00
switch_mutex_lock ( globals . mutex ) ;
if ( globals . timer_count ) {
globals . timer_count - - ;
if ( globals . timer_count = = ( runtime . tipping_point - 1 ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_NOTICE , " Fell Below tipping point of %u, shifting into low-gear. \n " , runtime . tipping_point ) ;
}
}
switch_mutex_unlock ( globals . mutex ) ;
2006-01-03 22:13:59 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2007-10-03 16:44:11 +00:00
SWITCH_MODULE_RUNTIME_FUNCTION ( softtimer_runtime )
2006-09-16 20:18:24 +00:00
{
2008-04-21 14:44:23 +00:00
switch_time_t too_late = STEP_MIC * 1000 ;
2006-09-16 20:18:24 +00:00
uint32_t current_ms = 0 ;
2007-10-03 16:44:11 +00:00
uint32_t x , tick = 0 ;
2008-01-11 00:43:49 +00:00
switch_time_t ts = 0 , last = 0 ;
2008-03-20 19:29:18 +00:00
int fwd_errs = 0 , rev_errs = 0 ;
2010-03-10 20:21:34 +00:00
int profile_tick = 0 ;
2011-03-22 01:49:39 +00:00
int tfd = - 1 ;
# ifdef HAVE_TIMERFD_CREATE
struct itimerspec spec = { { 0 } } ;
if ( MONO & & TFD ) {
tfd = timerfd_create ( CLOCK_MONOTONIC , 0 ) ;
if ( tfd > - 1 ) {
spec . it_interval . tv_sec = 0 ;
spec . it_interval . tv_nsec = 1000000 ;
spec . it_value . tv_sec = spec . it_interval . tv_sec ;
spec . it_value . tv_nsec = spec . it_interval . tv_nsec ;
if ( timerfd_settime ( tfd , TFD_TIMER_ABSTIME , & spec , NULL ) ) {
close ( tfd ) ;
tfd = - 1 ;
}
}
}
# else
tfd = - 1 ;
# endif
2010-03-10 20:21:34 +00:00
runtime . profile_timer = switch_new_profile_timer ( ) ;
switch_get_system_idle_time ( runtime . profile_timer , & runtime . profile_time ) ;
2008-03-20 19:29:18 +00:00
2010-01-12 18:31:13 +00:00
# ifdef HAVE_CPU_SET_MACROS
2010-02-17 19:50:25 +00:00
if ( runtime . timer_affinity > - 1 ) {
cpu_set_t set ;
CPU_ZERO ( & set ) ;
CPU_SET ( 0 , & set ) ;
sched_setaffinity ( runtime . timer_affinity , sizeof ( set ) , & set ) ;
}
2009-12-29 23:21:20 +00:00
# endif
2008-01-11 00:43:49 +00:00
switch_time_sync ( ) ;
2007-10-04 21:35:50 +00:00
2007-06-24 23:09:28 +00:00
globals . STARTED = globals . RUNNING = 1 ;
2007-10-03 16:44:11 +00:00
switch_mutex_lock ( runtime . throttle_mutex ) ;
runtime . sps = runtime . sps_total ;
switch_mutex_unlock ( runtime . throttle_mutex ) ;
2008-01-23 15:26:57 +00:00
if ( MONO ) {
int loops ;
2008-05-27 04:30:03 +00:00
for ( loops = 0 ; loops < 3 ; loops + + ) {
2008-01-23 15:26:57 +00:00
ts = time_now ( 0 ) ;
2008-05-27 04:30:03 +00:00
/* if it returns the same value every time it won't be of much use. */
2008-01-23 15:26:57 +00:00
if ( ts = = last ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Broken MONOTONIC Clock Detected!, Support Disabled. \n " ) ;
MONO = 0 ;
2009-12-29 23:21:20 +00:00
NANO = 0 ;
2008-01-23 15:26:57 +00:00
runtime . reference = switch_time_now ( ) ;
runtime . initiated = runtime . reference ;
2008-04-21 14:44:23 +00:00
break ;
2008-01-23 15:26:57 +00:00
}
2008-11-13 16:36:55 +00:00
do_sleep ( STEP_MIC ) ;
2008-01-23 15:26:57 +00:00
last = ts ;
}
}
ts = 0 ;
last = 0 ;
2008-03-20 19:29:18 +00:00
fwd_errs = rev_errs = 0 ;
2008-05-27 04:30:03 +00:00
2008-11-13 16:36:55 +00:00
# ifndef DISABLE_1MS_COND
2009-12-29 23:21:20 +00:00
if ( ! NANO ) {
switch_mutex_init ( & TIMER_MATRIX [ 1 ] . mutex , SWITCH_MUTEX_NESTED , module_pool ) ;
switch_thread_cond_create ( & TIMER_MATRIX [ 1 ] . cond , module_pool ) ;
}
2008-11-13 16:36:55 +00:00
# endif
2010-01-13 18:02:42 +00:00
2009-12-30 20:55:04 +00:00
switch_time_sync ( ) ;
2010-02-06 03:38:24 +00:00
2009-12-30 20:55:04 +00:00
globals . use_cond_yield = COND ;
globals . RUNNING = 1 ;
2010-02-06 03:38:24 +00:00
2007-03-29 22:31:56 +00:00
while ( globals . RUNNING = = 1 ) {
2008-01-11 00:43:49 +00:00
runtime . reference + = STEP_MIC ;
2011-03-28 18:50:42 +00:00
while ( ( ( ts = time_now ( runtime . offset ) ) + 100 ) < runtime . reference ) {
2008-01-11 00:43:49 +00:00
if ( ts < last ) {
2008-04-16 22:02:06 +00:00
if ( MONO ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Virtual Migration Detected! Syncing Clock \n " ) ;
switch_time_sync ( ) ;
} else {
2008-05-27 04:30:03 +00:00
int64_t diff = ( int64_t ) ( ts - last ) ;
2008-04-16 22:02:06 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Reverse Clock Skew Detected! \n " ) ;
runtime . reference = switch_time_now ( ) ;
current_ms = 0 ;
tick = 0 ;
runtime . initiated + = diff ;
rev_errs + + ;
}
2011-03-28 18:50:42 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT ,
" If you see this message many times try setting the param enable-clock-nanosleep to true in switch.conf.xml or consider a nicer machine to run me on. I AM *FREE* afterall. \n " ) ;
2008-03-20 19:29:18 +00:00
} else {
rev_errs = 0 ;
2008-01-11 00:43:49 +00:00
}
2009-12-30 20:55:04 +00:00
2010-03-01 19:25:27 +00:00
if ( globals . timer_count > = runtime . tipping_point ) {
2009-12-30 20:55:04 +00:00
os_yield ( ) ;
} else {
2011-03-22 01:49:39 +00:00
if ( tfd > - 1 & & globals . RUNNING = = 1 ) {
uint64_t exp ;
int r ;
r = read ( tfd , & exp , sizeof ( exp ) ) ;
} else {
do_sleep ( 1000 ) ;
}
2009-12-30 20:55:04 +00:00
}
2008-01-11 00:43:49 +00:00
last = ts ;
2008-05-27 04:30:03 +00:00
}
2008-01-11 00:43:49 +00:00
if ( ts > ( runtime . reference + too_late ) ) {
2008-04-16 22:02:06 +00:00
if ( MONO ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Virtual Migration Detected! Syncing Clock \n " ) ;
switch_time_sync ( ) ;
} else {
switch_time_t diff = ts - runtime . reference - STEP_MIC ;
2008-10-10 18:38:41 +00:00
# ifndef WIN32
2008-04-16 22:02:06 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Forward Clock Skew Detected! \n " ) ;
2008-10-10 18:38:41 +00:00
# endif
2008-04-16 22:02:06 +00:00
fwd_errs + + ;
runtime . reference = switch_time_now ( ) ;
current_ms = 0 ;
tick = 0 ;
runtime . initiated + = diff ;
}
2008-03-20 19:29:18 +00:00
} else {
fwd_errs = 0 ;
2006-09-16 20:18:24 +00:00
}
2008-05-27 04:30:03 +00:00
2008-03-20 19:29:18 +00:00
if ( fwd_errs > 9 | | rev_errs > 9 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Auto Re-Syncing clock. \n " ) ;
switch_time_sync ( ) ;
fwd_errs = rev_errs = 0 ;
}
2007-10-03 16:44:11 +00:00
runtime . timestamp = ts ;
2006-09-22 02:10:27 +00:00
current_ms + = STEP_MS ;
2007-10-03 16:44:11 +00:00
tick + = STEP_MS ;
2008-05-27 04:30:03 +00:00
2009-12-29 23:21:20 +00:00
if ( tick > = TICK_PER_SEC ) {
2010-03-10 20:21:34 +00:00
if ( + + profile_tick = = 1 ) {
switch_get_system_idle_time ( runtime . profile_timer , & runtime . profile_time ) ;
profile_tick = 0 ;
}
2007-10-03 16:44:11 +00:00
if ( runtime . sps < = 0 ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CRIT , " Over Session Rate of %d! \n " , runtime . sps_total ) ;
}
switch_mutex_lock ( runtime . throttle_mutex ) ;
2007-10-04 21:35:50 +00:00
runtime . sps_last = runtime . sps_total - runtime . sps ;
2007-10-03 16:44:11 +00:00
runtime . sps = runtime . sps_total ;
switch_mutex_unlock ( runtime . throttle_mutex ) ;
tick = 0 ;
}
2008-11-13 16:36:55 +00:00
# ifndef DISABLE_1MS_COND
2008-11-10 18:26:41 +00:00
TIMER_MATRIX [ 1 ] . tick + + ;
if ( switch_mutex_trylock ( TIMER_MATRIX [ 1 ] . mutex ) = = SWITCH_STATUS_SUCCESS ) {
switch_thread_cond_broadcast ( TIMER_MATRIX [ 1 ] . cond ) ;
switch_mutex_unlock ( TIMER_MATRIX [ 1 ] . mutex ) ;
}
if ( TIMER_MATRIX [ 1 ] . tick = = MAX_TICK ) {
TIMER_MATRIX [ 1 ] . tick = 0 ;
TIMER_MATRIX [ 1 ] . roll + + ;
}
2008-11-13 16:36:55 +00:00
# endif
2008-11-10 18:26:41 +00:00
2008-12-17 01:53:46 +00:00
2009-12-30 20:55:04 +00:00
if ( MATRIX & & ( current_ms % MS_PER_TICK ) = = 0 ) {
2008-03-17 19:45:39 +00:00
for ( x = MS_PER_TICK ; x < = MAX_ELEMENTS ; x + = MS_PER_TICK ) {
if ( ( current_ms % x ) = = 0 ) {
if ( TIMER_MATRIX [ x ] . count ) {
TIMER_MATRIX [ x ] . tick + + ;
2008-11-14 23:31:21 +00:00
# ifdef DISABLE_1MS_COND
2010-02-06 03:38:24 +00:00
2009-12-30 20:55:04 +00:00
if ( TIMER_MATRIX [ x ] . mutex & & switch_mutex_trylock ( TIMER_MATRIX [ x ] . mutex ) = = SWITCH_STATUS_SUCCESS ) {
switch_thread_cond_broadcast ( TIMER_MATRIX [ x ] . cond ) ;
switch_mutex_unlock ( TIMER_MATRIX [ x ] . mutex ) ;
2008-11-10 18:26:41 +00:00
}
# endif
2008-03-17 19:45:39 +00:00
if ( TIMER_MATRIX [ x ] . tick = = MAX_TICK ) {
TIMER_MATRIX [ x ] . tick = 0 ;
TIMER_MATRIX [ x ] . roll + + ;
}
}
2007-03-06 01:19:41 +00:00
}
2006-09-16 20:18:24 +00:00
}
}
2008-12-17 01:53:46 +00:00
2006-09-16 20:18:24 +00:00
if ( current_ms = = MAX_ELEMENTS ) {
current_ms = 0 ;
}
}
2008-11-10 18:26:41 +00:00
globals . use_cond_yield = 0 ;
2010-02-06 03:38:24 +00:00
2008-11-10 18:26:41 +00:00
for ( x = MS_PER_TICK ; x < = MAX_ELEMENTS ; x + = MS_PER_TICK ) {
if ( TIMER_MATRIX [ x ] . mutex & & switch_mutex_trylock ( TIMER_MATRIX [ x ] . mutex ) = = SWITCH_STATUS_SUCCESS ) {
switch_thread_cond_broadcast ( TIMER_MATRIX [ x ] . cond ) ;
switch_mutex_unlock ( TIMER_MATRIX [ x ] . mutex ) ;
}
}
2011-03-22 01:49:39 +00:00
if ( tfd > - 1 ) {
close ( tfd ) ;
tfd = - 1 ;
}
2008-11-10 18:26:41 +00:00
2006-09-16 20:18:24 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . RUNNING = 0 ;
switch_mutex_unlock ( globals . mutex ) ;
2010-03-10 20:21:34 +00:00
switch_delete_profile_timer ( & runtime . profile_timer ) ;
2009-03-03 01:52:25 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Soft timer thread exiting. \n " ) ;
2008-11-10 18:26:41 +00:00
2006-09-16 20:18:24 +00:00
return SWITCH_STATUS_TERM ;
}
2008-09-29 16:58:31 +00:00
/*
This converts a struct tm to a switch_time_exp_t
We have to use UNIX structures to do our exams
and use switch_ * functions for the output .
*/
2010-02-06 03:38:24 +00:00
static void tm2switchtime ( struct tm * tm , switch_time_exp_t * xt )
2008-09-29 16:58:31 +00:00
{
if ( ! xt | | ! tm ) {
2010-02-06 03:38:24 +00:00
return ;
2008-09-29 16:58:31 +00:00
}
2010-02-06 03:38:24 +00:00
memset ( xt , 0 , sizeof ( xt ) ) ;
xt - > tm_sec = tm - > tm_sec ;
xt - > tm_min = tm - > tm_min ;
xt - > tm_hour = tm - > tm_hour ;
xt - > tm_mday = tm - > tm_mday ;
xt - > tm_mon = tm - > tm_mon ;
xt - > tm_year = tm - > tm_year ;
xt - > tm_wday = tm - > tm_wday ;
xt - > tm_yday = tm - > tm_yday ;
xt - > tm_isdst = tm - > tm_isdst ;
2008-09-29 16:58:31 +00:00
2009-05-15 18:19:30 +00:00
# if defined(HAVE_STRUCT_TM_TM_GMTOFF)
2010-02-06 03:38:24 +00:00
xt - > tm_gmtoff = tm - > tm_gmtoff ;
2008-09-29 16:58:31 +00:00
# endif
return ;
}
/* **************************************************************************
LOADING OF THE XML DATA - HASH TABLE & MEMORY POOL MANAGEMENT
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct {
switch_memory_pool_t * pool ;
switch_hash_t * hash ;
} switch_timezones_list_t ;
static switch_timezones_list_t TIMEZONES_LIST = { 0 } ;
static switch_event_node_t * NODE = NULL ;
2010-02-06 03:38:24 +00:00
const char * switch_lookup_timezone ( const char * tz_name )
2008-09-29 16:58:31 +00:00
{
char * value = NULL ;
2010-02-06 03:38:24 +00:00
if ( tz_name & & ( value = switch_core_hash_find ( TIMEZONES_LIST . hash , tz_name ) ) = = NULL ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Timezone '%s' not found! \n " , tz_name ) ;
2008-09-29 16:58:31 +00:00
}
2010-02-06 03:38:24 +00:00
2008-09-29 16:58:31 +00:00
return value ;
}
void switch_load_timezones ( switch_bool_t reload )
{
switch_xml_t xml = NULL , x_lists = NULL , x_list = NULL , cfg = NULL ;
unsigned total = 0 ;
if ( TIMEZONES_LIST . hash ) {
switch_core_hash_destroy ( & TIMEZONES_LIST . hash ) ;
}
if ( TIMEZONES_LIST . pool ) {
switch_core_destroy_memory_pool ( & TIMEZONES_LIST . pool ) ;
}
memset ( & TIMEZONES_LIST , 0 , sizeof ( TIMEZONES_LIST ) ) ;
switch_core_new_memory_pool ( & TIMEZONES_LIST . pool ) ;
switch_core_hash_init ( & TIMEZONES_LIST . hash , TIMEZONES_LIST . pool ) ;
if ( ( xml = switch_xml_open_cfg ( " timezones.conf " , & cfg , NULL ) ) ) {
if ( ( x_lists = switch_xml_child ( cfg , " timezones " ) ) ) {
for ( x_list = switch_xml_child ( x_lists , " zone " ) ; x_list ; x_list = x_list - > next ) {
const char * name = switch_xml_attr ( x_list , " name " ) ;
2010-02-06 03:38:24 +00:00
const char * value = switch_xml_attr ( x_list , " value " ) ;
2008-09-29 16:58:31 +00:00
2009-10-23 16:03:42 +00:00
if ( zstr ( name ) ) {
2008-09-29 16:58:31 +00:00
continue ;
}
2009-10-23 16:03:42 +00:00
if ( zstr ( value ) ) {
2008-09-29 16:58:31 +00:00
continue ;
}
2010-02-06 03:38:24 +00:00
switch_core_hash_insert ( TIMEZONES_LIST . hash , name , switch_core_strdup ( TIMEZONES_LIST . pool , value ) ) ;
2008-09-29 16:58:31 +00:00
total + + ;
}
}
2010-02-06 03:38:24 +00:00
2008-09-29 16:58:31 +00:00
switch_xml_free ( xml ) ;
}
2010-02-06 03:38:24 +00:00
2008-09-29 16:58:31 +00:00
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_INFO , " Timezone %sloaded %d definitions \n " , reload ? " re " : " " , total ) ;
}
static void event_handler ( switch_event_t * event )
{
2009-02-11 00:31:58 +00:00
switch_mutex_lock ( globals . mutex ) ;
2008-09-29 16:58:31 +00:00
switch_load_timezones ( 1 ) ;
2009-02-11 00:31:58 +00:00
switch_mutex_unlock ( globals . mutex ) ;
2008-09-29 16:58:31 +00:00
}
2010-02-06 03:38:24 +00:00
static void tztime ( const time_t * const timep , const char * tzstring , struct tm * const tmp ) ;
2008-09-29 16:58:31 +00:00
2008-10-31 20:24:06 +00:00
SWITCH_DECLARE ( switch_status_t ) switch_time_exp_tz_name ( const char * tz , switch_time_exp_t * tm , switch_time_t thetime )
{
struct tm xtm = { 0 } ;
const char * tz_name = tz ;
2009-02-26 18:28:26 +00:00
const char * tzdef ;
2008-10-31 20:24:06 +00:00
time_t timep ;
2010-02-06 03:38:24 +00:00
2008-10-31 20:24:06 +00:00
if ( ! thetime ) {
2009-02-26 18:28:26 +00:00
thetime = switch_micro_time_now ( ) ;
}
2008-10-31 20:24:06 +00:00
2010-02-06 03:38:24 +00:00
timep = ( thetime ) / ( int64_t ) ( 1000000 ) ;
2008-10-31 20:24:06 +00:00
2009-10-23 16:03:42 +00:00
if ( ! zstr ( tz_name ) ) {
2010-02-06 03:38:24 +00:00
tzdef = switch_lookup_timezone ( tz_name ) ;
2008-10-31 20:24:06 +00:00
} else {
/* We set the default timezone to GMT. */
2010-02-06 03:38:24 +00:00
tz_name = " GMT " ;
tzdef = " GMT " ;
2008-10-31 20:24:06 +00:00
}
2010-02-06 03:38:24 +00:00
if ( tzdef ) { /* The lookup of the zone may fail. */
tztime ( & timep , tzdef , & xtm ) ;
tm2switchtime ( & xtm , tm ) ;
2008-10-31 20:24:06 +00:00
return SWITCH_STATUS_SUCCESS ;
}
return SWITCH_STATUS_FALSE ;
}
SWITCH_DECLARE ( switch_status_t ) switch_strftime_tz ( const char * tz , const char * format , char * date , size_t len , switch_time_t thetime )
2008-09-29 16:58:31 +00:00
{
time_t timep ;
const char * tz_name = tz ;
const char * tzdef ;
switch_size_t retsize ;
2008-09-29 17:20:54 +00:00
struct tm tm = { 0 } ;
2008-09-29 16:58:31 +00:00
switch_time_exp_t stm ;
2008-10-31 20:24:06 +00:00
if ( ! thetime ) {
2009-01-25 21:23:07 +00:00
thetime = switch_micro_time_now ( ) ;
2008-10-31 20:24:06 +00:00
}
2008-09-29 16:58:31 +00:00
2010-02-06 03:38:24 +00:00
timep = ( thetime ) / ( int64_t ) ( 1000000 ) ;
2008-09-29 16:58:31 +00:00
2009-10-23 16:03:42 +00:00
if ( ! zstr ( tz_name ) ) {
2010-02-06 03:38:24 +00:00
tzdef = switch_lookup_timezone ( tz_name ) ;
2008-09-29 16:58:31 +00:00
} else {
/* We set the default timezone to GMT. */
2010-02-06 03:38:24 +00:00
tz_name = " GMT " ;
tzdef = " GMT " ;
2008-09-29 16:58:31 +00:00
}
2010-02-06 03:38:24 +00:00
if ( tzdef ) { /* The lookup of the zone may fail. */
tztime ( & timep , tzdef , & tm ) ;
tm2switchtime ( & tm , & stm ) ;
2009-10-23 16:03:42 +00:00
switch_strftime_nocheck ( date , & retsize , len , zstr ( format ) ? " %Y-%m-%d %T " : format , & stm ) ;
if ( ! zstr_buf ( date ) ) {
2008-10-10 20:30:05 +00:00
return SWITCH_STATUS_SUCCESS ;
}
2008-09-29 16:58:31 +00:00
}
2008-10-10 20:30:05 +00:00
return SWITCH_STATUS_FALSE ;
2008-09-29 16:58:31 +00:00
}
2007-10-03 16:44:11 +00:00
SWITCH_MODULE_LOAD_FUNCTION ( softtimer_load )
{
switch_timer_interface_t * timer_interface ;
module_pool = pool ;
2008-11-13 16:36:55 +00:00
# if defined(WIN32)
timeBeginPeriod ( 1 ) ;
2010-07-15 13:50:45 +00:00
InitializeCriticalSection ( & timer_section ) ;
win32_last_get_time_tick = timeGetTime ( ) ;
win32_tick_time_since_start = win32_last_get_time_tick ;
2008-11-13 16:36:55 +00:00
# endif
2010-02-06 03:38:24 +00:00
2008-08-11 18:24:07 +00:00
memset ( & globals , 0 , sizeof ( globals ) ) ;
switch_mutex_init ( & globals . mutex , SWITCH_MUTEX_NESTED , module_pool ) ;
2008-09-29 16:58:31 +00:00
if ( ( switch_event_bind_removable ( modname , SWITCH_EVENT_RELOADXML , NULL , event_handler , NULL , & NODE ) ! = SWITCH_STATUS_SUCCESS ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_ERROR , " Couldn't bind! \n " ) ;
}
switch_load_timezones ( 0 ) ;
2007-10-03 16:44:11 +00:00
/* 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 = " soft " ;
timer_interface - > timer_init = timer_init ;
timer_interface - > timer_next = timer_next ;
timer_interface - > timer_step = timer_step ;
2008-03-11 16:55:58 +00:00
timer_interface - > timer_sync = timer_sync ;
2007-10-03 16:44:11 +00:00
timer_interface - > timer_check = timer_check ;
timer_interface - > timer_destroy = timer_destroy ;
2010-01-15 21:09:51 +00:00
if ( ! switch_test_flag ( ( & runtime ) , SCF_USE_CLOCK_RT ) ) {
switch_time_set_nanosleep ( SWITCH_FALSE ) ;
}
2010-02-06 03:38:24 +00:00
2010-03-01 19:25:27 +00:00
if ( switch_test_flag ( ( & runtime ) , SCF_USE_HEAVY_TIMING ) ) {
switch_time_set_cond_yield ( SWITCH_FALSE ) ;
2010-01-15 21:09:51 +00:00
}
if ( switch_test_flag ( ( & runtime ) , SCF_CALIBRATE_CLOCK ) ) {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Calibrating timer, please wait... \n " ) ;
switch_time_calibrate_clock ( ) ;
} else {
switch_log_printf ( SWITCH_CHANNEL_LOG , SWITCH_LOG_CONSOLE , " Clock calibration disabled. \n " ) ;
}
2010-01-13 18:02:42 +00:00
2010-07-29 01:44:45 +00:00
if ( switch_test_flag ( ( & runtime ) , SCF_USE_WIN32_MONOTONIC ) ) {
MONO = 1 ;
}
2007-10-03 16:44:11 +00:00
/* indicate that the module should continue to be loaded */
2009-03-04 00:06:52 +00:00
return SWITCH_STATUS_SUCCESS ;
2007-10-03 16:44:11 +00:00
}
SWITCH_MODULE_SHUTDOWN_FUNCTION ( softtimer_shutdown )
2006-09-16 20:18:24 +00:00
{
2008-11-14 23:31:21 +00:00
globals . use_cond_yield = 0 ;
2008-11-10 18:26:41 +00:00
if ( globals . RUNNING = = 1 ) {
2006-09-16 20:18:24 +00:00
switch_mutex_lock ( globals . mutex ) ;
globals . RUNNING = - 1 ;
switch_mutex_unlock ( globals . mutex ) ;
2007-03-29 22:31:56 +00:00
2008-11-10 18:26:41 +00:00
while ( globals . RUNNING = = - 1 ) {
2008-11-13 16:36:55 +00:00
do_sleep ( 10000 ) ;
2006-09-16 20:18:24 +00:00
}
}
2008-05-06 13:58:36 +00:00
# if defined(WIN32)
timeEndPeriod ( 1 ) ;
2010-07-15 13:50:45 +00:00
win32_tick_time_since_start = - 1 ; /* we are not initialized anymore */
DeleteCriticalSection ( & timer_section ) ;
2008-05-06 13:58:36 +00:00
# endif
2008-09-29 16:58:31 +00:00
if ( TIMEZONES_LIST . hash ) {
switch_core_hash_destroy ( & TIMEZONES_LIST . hash ) ;
}
if ( TIMEZONES_LIST . pool ) {
switch_core_destroy_memory_pool ( & TIMEZONES_LIST . pool ) ;
}
2008-05-06 13:58:36 +00:00
2010-04-21 15:11:13 +00:00
if ( NODE ) {
switch_event_unbind ( & NODE ) ;
}
2009-03-04 00:06:52 +00:00
return SWITCH_STATUS_SUCCESS ;
2006-09-16 20:18:24 +00:00
}
2006-11-27 22:30:48 +00:00
2008-09-29 15:44:19 +00:00
/*
* This file was originally written for NetBSD and is in the public domain ,
* so clarified as of 1996 - 06 - 05 by Arthur David Olson ( arthur_david_olson @ nih . gov ) .
*
* Iw was modified by Massimo Cetra in order to be used with Callweaver and Freeswitch .
*/
//#define TESTING_IT 1
# include <stdlib.h>
# include <stdio.h>
# include <time.h>
# include <string.h>
# include <assert.h>
# ifdef TESTING_IT
# include <sys/time.h>
# endif
# ifndef TRUE
# define TRUE 1
# endif /* !defined TRUE */
# ifndef FALSE
# define FALSE 0
# endif /* !defined FALSE */
# ifndef TZ_MAX_TIMES
/*
* * The TZ_MAX_TIMES value below is enough to handle a bit more than a
* * year ' s worth of solar time ( corrected daily to the nearest second ) or
* * 138 years of Pacific Presidential Election time
* * ( where there are three time zone transitions every fourth year ) .
*/
# define TZ_MAX_TIMES 370
# endif /* !defined TZ_MAX_TIMES */
# ifndef TZ_MAX_TYPES
# ifndef NOSOLAR
2010-02-06 03:38:24 +00:00
# define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
2008-09-29 15:44:19 +00:00
# endif /* !defined NOSOLAR */
# ifdef NOSOLAR
/*
* * Must be at least 14 for Europe / Riga as of Jan 12 1995 ,
* * as noted by Earl Chew < earl @ hpato . aus . hp . com > .
*/
2010-02-06 03:38:24 +00:00
# define TZ_MAX_TYPES 20 /* Maximum number of local time types */
2008-09-29 15:44:19 +00:00
# endif /* !defined NOSOLAR */
# endif /* !defined TZ_MAX_TYPES */
# ifndef TZ_MAX_CHARS
2010-02-06 03:38:24 +00:00
# define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
2008-09-29 15:44:19 +00:00
/* (limited by what unsigned chars can hold) */
# endif /* !defined TZ_MAX_CHARS */
# ifndef TZ_MAX_LEAPS
2010-02-06 03:38:24 +00:00
# define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
2008-09-29 15:44:19 +00:00
# endif /* !defined TZ_MAX_LEAPS */
# ifdef TZNAME_MAX
# define MY_TZNAME_MAX TZNAME_MAX
# endif /* defined TZNAME_MAX */
# ifndef TZNAME_MAX
# define MY_TZNAME_MAX 255
# endif /* !defined TZNAME_MAX */
# define SECSPERMIN 60
# define MINSPERHOUR 60
# define HOURSPERDAY 24
# define DAYSPERWEEK 7
# define DAYSPERNYEAR 365
# define DAYSPERLYEAR 366
# define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
# define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
# define MONSPERYEAR 12
2010-02-06 03:38:24 +00:00
# define JULIAN_DAY 0 /* Jn - Julian day */
# define DAY_OF_YEAR 1 /* n - day of year */
2008-09-29 15:44:19 +00:00
# define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
# define EPOCH_YEAR 1970
# define EPOCH_WDAY TM_THURSDAY
# ifndef TZ_MAX_TIMES
/*
* * The TZ_MAX_TIMES value below is enough to handle a bit more than a
* * year ' s worth of solar time ( corrected daily to the nearest second ) or
* * 138 years of Pacific Presidential Election time
* * ( where there are three time zone transitions every fourth year ) .
*/
# define TZ_MAX_TIMES 370
# endif /* !defined TZ_MAX_TIMES */
# ifndef TZDEFRULES
# define TZDEFRULES "posixrules"
# endif /* !defined TZDEFRULES */
/*
* * The DST rules to use if TZ has no rules and we can ' t load TZDEFRULES .
* * We default to US rules as of 1999 - 08 - 17.
* * POSIX 1003.1 section 8.1 .1 says that the default DST rules are
* * implementation dependent ; for historical reasons , US rules are a
* * common default .
*/
# ifndef TZDEFRULESTRING
# define TZDEFRULESTRING ",M4.1.0,M10.5.0"
# endif /* !defined TZDEFDST */
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
# define is_digit(c) ((unsigned)(c) - '0' <= 9)
# define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
# define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
* * INITIALIZE ( x )
*/
# ifndef GNUC_or_lint
# ifdef lint
# define GNUC_or_lint
# endif /* defined lint */
# ifndef lint
# ifdef __GNUC__
# define GNUC_or_lint
# endif /* defined __GNUC__ */
# endif /* !defined lint */
# endif /* !defined GNUC_or_lint */
# ifdef WIN32
# define GNUC_or_lint
# endif
# ifndef INITIALIZE
# ifdef GNUC_or_lint
# define INITIALIZE(x) ((x) = 0)
# endif /* defined GNUC_or_lint */
# ifndef GNUC_or_lint
# define INITIALIZE(x)
# endif /* !defined GNUC_or_lint */
# endif /* !defined INITIALIZE */
# define TM_SUNDAY 0
# define TM_MONDAY 1
# define TM_TUESDAY 2
# define TM_WEDNESDAY 3
# define TM_THURSDAY 4
# define TM_FRIDAY 5
# define TM_SATURDAY 6
# define TM_JANUARY 0
# define TM_FEBRUARY 1
# define TM_MARCH 2
# define TM_APRIL 3
# define TM_MAY 4
# define TM_JUNE 5
# define TM_JULY 6
# define TM_AUGUST 7
# define TM_SEPTEMBER 8
# define TM_OCTOBER 9
# define TM_NOVEMBER 10
# define TM_DECEMBER 11
# define TM_YEAR_BASE 1900
# define EPOCH_YEAR 1970
# define EPOCH_WDAY TM_THURSDAY
/* **************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-02-06 03:38:24 +00:00
static const char gmt [ ] = " GMT " ;
2008-09-29 15:44:19 +00:00
# define CHARS_DEF BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), (2 * (MY_TZNAME_MAX + 1)))
struct rule {
2010-02-06 03:38:24 +00:00
int r_type ; /* type of rule--see below */
int r_day ; /* day number of rule */
int r_week ; /* week number of rule */
int r_mon ; /* month number of rule */
long r_time ; /* transition time of rule */
2008-09-29 15:44:19 +00:00
} ;
2010-02-06 03:38:24 +00:00
struct ttinfo { /* time type information */
long tt_gmtoff ; /* UTC offset in seconds */
int tt_isdst ; /* used to set tm_isdst */
int tt_abbrind ; /* abbreviation list index */
int tt_ttisstd ; /* TRUE if transition is std time */
int tt_ttisgmt ; /* TRUE if transition is UTC */
2008-09-29 15:44:19 +00:00
} ;
2010-02-06 03:38:24 +00:00
struct lsinfo { /* leap second information */
time_t ls_trans ; /* transition time */
long ls_corr ; /* correction to apply */
2008-09-29 15:44:19 +00:00
} ;
struct state {
2010-02-06 03:38:24 +00:00
int leapcnt ;
int timecnt ;
int typecnt ;
int charcnt ;
time_t ats [ TZ_MAX_TIMES ] ;
unsigned char types [ TZ_MAX_TIMES ] ;
struct ttinfo ttis [ TZ_MAX_TYPES ] ;
char chars [ /* LINTED constant */ CHARS_DEF ] ;
struct lsinfo lsis [ TZ_MAX_LEAPS ] ;
2008-09-29 15:44:19 +00:00
} ;
2010-02-06 03:38:24 +00:00
static const int mon_lengths [ 2 ] [ MONSPERYEAR ] = {
{ 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 } ,
{ 31 , 29 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 }
2008-09-29 15:44:19 +00:00
} ;
2010-02-06 03:38:24 +00:00
static const int year_lengths [ 2 ] = {
2008-09-29 15:44:19 +00:00
DAYSPERNYEAR , DAYSPERLYEAR
} ;
/* **************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
Given a pointer into a time zone string , scan until a character that is not
a valid character in a zone name is found . Return a pointer to that
character .
*/
static const char * getzname ( register const char * strp )
{
register char c ;
2010-02-06 03:38:24 +00:00
while ( ( c = * strp ) ! = ' \0 ' & & ! is_digit ( c ) & & c ! = ' , ' & & c ! = ' - ' & & c ! = ' + ' )
+ + strp ;
2008-09-29 15:44:19 +00:00
return strp ;
}
/*
Given a pointer into a time zone string , extract a number from that string .
Check that the number is within a specified range ; if it is not , return
NULL .
Otherwise , return a pointer to the first character not part of the number .
*/
2010-02-06 03:38:24 +00:00
static const char * getnum ( register const char * strp , int * const nump , const int min , const int max )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
register char c ;
register int num ;
2008-09-29 15:44:19 +00:00
if ( strp = = NULL | | ! is_digit ( c = * strp ) )
return NULL ;
num = 0 ;
do {
num = num * 10 + ( c - ' 0 ' ) ;
if ( num > max )
2010-02-06 03:38:24 +00:00
return NULL ; /* illegal value */
2008-09-29 15:44:19 +00:00
c = * + + strp ;
} while ( is_digit ( c ) ) ;
if ( num < min )
2010-02-06 03:38:24 +00:00
return NULL ; /* illegal value */
2008-09-29 15:44:19 +00:00
* nump = num ;
return strp ;
}
/*
Given a pointer into a time zone string , extract a number of seconds ,
in hh [ : mm [ : ss ] ] form , from the string .
If any error occurs , return NULL .
Otherwise , return a pointer to the first character not part of the number
of seconds .
*/
2010-02-06 03:38:24 +00:00
static const char * getsecs ( register const char * strp , long * const secsp )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
int num ;
2008-09-29 15:44:19 +00:00
/*
2010-02-06 03:38:24 +00:00
* * ` HOURSPERDAY * DAYSPERWEEK - 1 ' allows quasi - Posix rules like
* * " M10.4.6/26 " , which does not conform to Posix ,
* * but which specifies the equivalent of
* * ` ` 02 : 00 on the first Sunday on or after 23 Oct ' ' .
*/
2008-09-29 15:44:19 +00:00
strp = getnum ( strp , & num , 0 , HOURSPERDAY * DAYSPERWEEK - 1 ) ;
if ( strp = = NULL )
return NULL ;
* secsp = num * ( long ) SECSPERHOUR ;
if ( * strp = = ' : ' ) {
+ + strp ;
strp = getnum ( strp , & num , 0 , MINSPERHOUR - 1 ) ;
if ( strp = = NULL )
return NULL ;
* secsp + = num * SECSPERMIN ;
if ( * strp = = ' : ' ) {
+ + strp ;
/* `SECSPERMIN' allows for leap seconds. */
strp = getnum ( strp , & num , 0 , SECSPERMIN ) ;
if ( strp = = NULL )
return NULL ;
* secsp + = num ;
}
}
return strp ;
}
/*
Given a pointer into a time zone string , extract an offset , in
[ + - ] hh [ : mm [ : ss ] ] form , from the string .
If any error occurs , return NULL .
Otherwise , return a pointer to the first character not part of the time .
*/
2010-02-06 03:38:24 +00:00
static const char * getoffset ( register const char * strp , long * const offsetp )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
register int neg = 0 ;
2008-09-29 15:44:19 +00:00
if ( * strp = = ' - ' ) {
neg = 1 ;
+ + strp ;
} else if ( * strp = = ' + ' )
+ + strp ;
strp = getsecs ( strp , offsetp ) ;
if ( strp = = NULL )
2010-02-06 03:38:24 +00:00
return NULL ; /* illegal time */
2008-09-29 15:44:19 +00:00
if ( neg )
* offsetp = - * offsetp ;
return strp ;
}
/*
Given a pointer into a time zone string , extract a rule in the form
date [ / time ] . See POSIX section 8 for the format of " date " and " time " .
If a valid rule is not found , return NULL .
Otherwise , return a pointer to the first character not part of the rule .
*/
2010-02-06 03:38:24 +00:00
static const char * getrule ( const char * strp , register struct rule * const rulep )
2008-09-29 15:44:19 +00:00
{
if ( * strp = = ' J ' ) {
/*
2010-02-06 03:38:24 +00:00
* * Julian day .
*/
2008-09-29 15:44:19 +00:00
rulep - > r_type = JULIAN_DAY ;
+ + strp ;
strp = getnum ( strp , & rulep - > r_day , 1 , DAYSPERNYEAR ) ;
} else if ( * strp = = ' M ' ) {
/*
2010-02-06 03:38:24 +00:00
* * Month , week , day .
*/
2008-09-29 15:44:19 +00:00
rulep - > r_type = MONTH_NTH_DAY_OF_WEEK ;
+ + strp ;
strp = getnum ( strp , & rulep - > r_mon , 1 , MONSPERYEAR ) ;
if ( strp = = NULL )
return NULL ;
if ( * strp + + ! = ' . ' )
return NULL ;
strp = getnum ( strp , & rulep - > r_week , 1 , 5 ) ;
if ( strp = = NULL )
return NULL ;
if ( * strp + + ! = ' . ' )
return NULL ;
strp = getnum ( strp , & rulep - > r_day , 0 , DAYSPERWEEK - 1 ) ;
} else if ( is_digit ( * strp ) ) {
/*
2010-02-06 03:38:24 +00:00
* * Day of year .
*/
2008-09-29 15:44:19 +00:00
rulep - > r_type = DAY_OF_YEAR ;
strp = getnum ( strp , & rulep - > r_day , 0 , DAYSPERLYEAR - 1 ) ;
2010-02-06 03:38:24 +00:00
} else
return NULL ; /* invalid format */
2008-09-29 15:44:19 +00:00
if ( strp = = NULL )
return NULL ;
if ( * strp = = ' / ' ) {
/*
2010-02-06 03:38:24 +00:00
* * Time specified .
*/
2008-09-29 15:44:19 +00:00
+ + strp ;
strp = getsecs ( strp , & rulep - > r_time ) ;
2010-02-06 03:38:24 +00:00
} else
rulep - > r_time = 2 * SECSPERHOUR ; /* default = 2:00:00 */
2008-09-29 15:44:19 +00:00
return strp ;
}
/*
Given the Epoch - relative time of January 1 , 00 : 00 : 00 UTC , in a year , the
year , a rule , and the offset from UTC at the time that rule takes effect ,
calculate the Epoch - relative time that rule takes effect .
*/
2010-02-06 03:38:24 +00:00
static time_t transtime ( const time_t janfirst , const int year , register const struct rule * const rulep , const long offset )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
register int leapyear ;
register time_t value ;
register int i ;
int d , m1 , yy0 , yy1 , yy2 , dow ;
2008-09-29 15:44:19 +00:00
INITIALIZE ( value ) ;
leapyear = isleap ( year ) ;
switch ( rulep - > r_type ) {
case JULIAN_DAY :
/*
2010-02-06 03:38:24 +00:00
* * Jn - Julian day , 1 = = January 1 , 60 = = March 1 even in leap
* * years .
* * In non - leap years , or if the day number is 59 or less , just
* * add SECSPERDAY times the day number - 1 to the time of
* * January 1 , midnight , to get the day .
*/
2008-09-29 15:44:19 +00:00
value = janfirst + ( rulep - > r_day - 1 ) * SECSPERDAY ;
if ( leapyear & & rulep - > r_day > = 60 )
value + = SECSPERDAY ;
break ;
case DAY_OF_YEAR :
/*
2010-02-06 03:38:24 +00:00
* * n - day of year .
* * Just add SECSPERDAY times the day number to the time of
* * January 1 , midnight , to get the day .
*/
2008-09-29 15:44:19 +00:00
value = janfirst + rulep - > r_day * SECSPERDAY ;
break ;
case MONTH_NTH_DAY_OF_WEEK :
/*
2010-02-06 03:38:24 +00:00
* * Mm . n . d - nth " dth day " of month m .
*/
2008-09-29 15:44:19 +00:00
value = janfirst ;
for ( i = 0 ; i < rulep - > r_mon - 1 ; + + i )
value + = mon_lengths [ leapyear ] [ i ] * SECSPERDAY ;
/*
2010-02-06 03:38:24 +00:00
* * Use Zeller ' s Congruence to get day - of - week of first day of
* * month .
*/
2008-09-29 15:44:19 +00:00
m1 = ( rulep - > r_mon + 9 ) % 12 + 1 ;
yy0 = ( rulep - > r_mon < = 2 ) ? ( year - 1 ) : year ;
yy1 = yy0 / 100 ;
yy2 = yy0 % 100 ;
2010-02-06 03:38:24 +00:00
dow = ( ( 26 * m1 - 2 ) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1 ) % 7 ;
2008-09-29 15:44:19 +00:00
if ( dow < 0 )
dow + = DAYSPERWEEK ;
/*
2010-02-06 03:38:24 +00:00
* * " dow " is the day - of - week of the first day of the month . Get
* * the day - of - month ( zero - origin ) of the first " dow " day of the
* * month .
*/
2008-09-29 15:44:19 +00:00
d = rulep - > r_day - dow ;
if ( d < 0 )
d + = DAYSPERWEEK ;
for ( i = 1 ; i < rulep - > r_week ; + + i ) {
2010-02-06 03:38:24 +00:00
if ( d + DAYSPERWEEK > = mon_lengths [ leapyear ] [ rulep - > r_mon - 1 ] )
break ;
2008-09-29 15:44:19 +00:00
d + = DAYSPERWEEK ;
}
/*
2010-02-06 03:38:24 +00:00
* * " d " is the day - of - month ( zero - origin ) of the day we want .
*/
2008-09-29 15:44:19 +00:00
value + = d * SECSPERDAY ;
break ;
}
/*
2010-02-06 03:38:24 +00:00
* * " value " is the Epoch - relative time of 00 : 00 : 00 UTC on the day in
* * question . To get the Epoch - relative time of the specified local
* * time on that day , add the transition time and the current offset
* * from UTC .
*/
2008-09-29 15:44:19 +00:00
return value + rulep - > r_time + offset ;
}
/*
Given a POSIX section 8 - style TZ string , fill in the rule tables as
appropriate .
*/
2010-02-06 03:38:24 +00:00
static int tzparse ( const char * name , register struct state * const sp , const int lastditch )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
const char * stdname ;
const char * dstname ;
size_t stdlen ;
size_t dstlen ;
long stdoffset ;
long dstoffset ;
register time_t * atp ;
register unsigned char * typep ;
register char * cp ;
2008-09-29 15:44:19 +00:00
INITIALIZE ( dstname ) ;
stdname = name ;
if ( lastditch ) {
stdlen = strlen ( name ) ; /* length of standard zone name */
name + = stdlen ;
if ( stdlen > = sizeof sp - > chars )
stdlen = ( sizeof sp - > chars ) - 1 ;
stdoffset = 0 ;
} else {
name = getzname ( name ) ;
stdlen = name - stdname ;
if ( stdlen < 3 )
return - 1 ;
if ( * name = = ' \0 ' )
return - 1 ;
name = getoffset ( name , & stdoffset ) ;
if ( name = = NULL )
return - 1 ;
}
2010-02-06 03:38:24 +00:00
sp - > leapcnt = 0 ; /* so, we're off a little */
2008-09-29 15:44:19 +00:00
if ( * name ! = ' \0 ' ) {
dstname = name ;
name = getzname ( name ) ;
dstlen = name - dstname ; /* length of DST zone name */
if ( dstlen < 3 )
return - 1 ;
2010-02-06 03:38:24 +00:00
if ( * name ! = ' \0 ' & & * name ! = ' , ' & & * name ! = ' ; ' ) {
2008-09-29 15:44:19 +00:00
name = getoffset ( name , & dstoffset ) ;
if ( name = = NULL )
return - 1 ;
2010-02-06 03:38:24 +00:00
} else
dstoffset = stdoffset - SECSPERHOUR ;
2008-09-29 15:44:19 +00:00
/* Go parsing the daylight saving stuff */
2010-02-06 03:38:24 +00:00
if ( * name = = ' , ' | | * name = = ' ; ' ) {
struct rule start ;
struct rule end ;
register int year ;
register time_t janfirst ;
time_t starttime ;
time_t endtime ;
2008-09-29 15:44:19 +00:00
+ + name ;
if ( ( name = getrule ( name , & start ) ) = = NULL )
return - 1 ;
if ( * name + + ! = ' , ' )
return - 1 ;
if ( ( name = getrule ( name , & end ) ) = = NULL )
return - 1 ;
if ( * name ! = ' \0 ' )
return - 1 ;
sp - > typecnt = 2 ; /* standard time and DST */
/*
2010-02-06 03:38:24 +00:00
* * Two transitions per year , from EPOCH_YEAR to 2037.
*/
2008-09-29 15:44:19 +00:00
sp - > timecnt = 2 * ( 2037 - EPOCH_YEAR + 1 ) ;
if ( sp - > timecnt > TZ_MAX_TIMES )
return - 1 ;
sp - > ttis [ 0 ] . tt_gmtoff = - dstoffset ;
sp - > ttis [ 0 ] . tt_isdst = 1 ;
2010-02-06 03:38:24 +00:00
sp - > ttis [ 0 ] . tt_abbrind = ( int ) ( stdlen + 1 ) ;
2008-09-29 15:44:19 +00:00
sp - > ttis [ 1 ] . tt_gmtoff = - stdoffset ;
sp - > ttis [ 1 ] . tt_isdst = 0 ;
sp - > ttis [ 1 ] . tt_abbrind = 0 ;
atp = sp - > ats ;
typep = sp - > types ;
janfirst = 0 ;
for ( year = EPOCH_YEAR ; year < = 2037 ; + + year ) {
2010-02-06 03:38:24 +00:00
starttime = transtime ( janfirst , year , & start , stdoffset ) ;
endtime = transtime ( janfirst , year , & end , dstoffset ) ;
2008-09-29 15:44:19 +00:00
if ( starttime > endtime ) {
* atp + + = endtime ;
* typep + + = 1 ; /* DST ends */
* atp + + = starttime ;
* typep + + = 0 ; /* DST begins */
} else {
* atp + + = starttime ;
* typep + + = 0 ; /* DST begins */
* atp + + = endtime ;
* typep + + = 1 ; /* DST ends */
}
janfirst + = year_lengths [ isleap ( year ) ] * SECSPERDAY ;
}
} else {
2010-02-06 03:38:24 +00:00
register long theirstdoffset ;
register long theirdstoffset ;
register long theiroffset ;
register int isdst ;
register int i ;
register int j ;
2008-09-29 15:44:19 +00:00
if ( * name ! = ' \0 ' )
return - 1 ;
/*
2010-02-06 03:38:24 +00:00
Initial values of theirstdoffset and theirdstoffset .
*/
2008-09-29 15:44:19 +00:00
theirstdoffset = 0 ;
for ( i = 0 ; i < sp - > timecnt ; + + i ) {
j = sp - > types [ i ] ;
if ( ! sp - > ttis [ j ] . tt_isdst ) {
2010-02-06 03:38:24 +00:00
theirstdoffset = - sp - > ttis [ j ] . tt_gmtoff ;
2008-09-29 15:44:19 +00:00
break ;
}
}
theirdstoffset = 0 ;
for ( i = 0 ; i < sp - > timecnt ; + + i ) {
j = sp - > types [ i ] ;
if ( sp - > ttis [ j ] . tt_isdst ) {
2010-02-06 03:38:24 +00:00
theirdstoffset = - sp - > ttis [ j ] . tt_gmtoff ;
2008-09-29 15:44:19 +00:00
break ;
}
}
/*
2010-02-06 03:38:24 +00:00
* * Initially we ' re assumed to be in standard time .
*/
2008-09-29 15:44:19 +00:00
isdst = FALSE ;
theiroffset = theirstdoffset ;
/*
2010-02-06 03:38:24 +00:00
* * Now juggle transition times and types
* * tracking offsets as you do .
*/
2008-09-29 15:44:19 +00:00
for ( i = 0 ; i < sp - > timecnt ; + + i ) {
j = sp - > types [ i ] ;
2010-02-06 03:38:24 +00:00
sp - > types [ i ] = ( unsigned char ) sp - > ttis [ j ] . tt_isdst ;
2008-09-29 15:44:19 +00:00
if ( sp - > ttis [ j ] . tt_ttisgmt ) {
/* No adjustment to transition time */
} else {
/*
2010-02-06 03:38:24 +00:00
* * If summer time is in effect , and the
* * transition time was not specified as
* * standard time , add the summer time
* * offset to the transition time ;
* * otherwise , add the standard time
* * offset to the transition time .
*/
2008-09-29 15:44:19 +00:00
/*
2010-02-06 03:38:24 +00:00
* * Transitions from DST to DDST
* * will effectively disappear since
* * POSIX provides for only one DST
* * offset .
*/
2008-09-29 15:44:19 +00:00
if ( isdst & & ! sp - > ttis [ j ] . tt_ttisstd ) {
2010-02-06 03:38:24 +00:00
sp - > ats [ i ] + = dstoffset - theirdstoffset ;
2008-09-29 15:44:19 +00:00
} else {
2010-02-06 03:38:24 +00:00
sp - > ats [ i ] + = stdoffset - theirstdoffset ;
2008-09-29 15:44:19 +00:00
}
}
theiroffset = - sp - > ttis [ j ] . tt_gmtoff ;
if ( sp - > ttis [ j ] . tt_isdst )
theirdstoffset = theiroffset ;
2010-02-06 03:38:24 +00:00
else
theirstdoffset = theiroffset ;
2008-09-29 15:44:19 +00:00
}
/*
2010-02-06 03:38:24 +00:00
* * Finally , fill in ttis .
* * ttisstd and ttisgmt need not be handled .
*/
2008-09-29 15:44:19 +00:00
sp - > ttis [ 0 ] . tt_gmtoff = - stdoffset ;
sp - > ttis [ 0 ] . tt_isdst = FALSE ;
sp - > ttis [ 0 ] . tt_abbrind = 0 ;
sp - > ttis [ 1 ] . tt_gmtoff = - dstoffset ;
sp - > ttis [ 1 ] . tt_isdst = TRUE ;
2010-02-06 03:38:24 +00:00
sp - > ttis [ 1 ] . tt_abbrind = ( int ) ( stdlen + 1 ) ;
2008-09-29 15:44:19 +00:00
sp - > typecnt = 2 ;
}
} else {
dstlen = 0 ;
sp - > typecnt = 1 ; /* only standard time */
sp - > timecnt = 0 ;
sp - > ttis [ 0 ] . tt_gmtoff = - stdoffset ;
sp - > ttis [ 0 ] . tt_isdst = 0 ;
sp - > ttis [ 0 ] . tt_abbrind = 0 ;
}
2010-02-06 03:38:24 +00:00
sp - > charcnt = ( int ) ( stdlen + 1 ) ;
2008-09-29 15:44:19 +00:00
if ( dstlen ! = 0 )
2010-02-06 03:38:24 +00:00
sp - > charcnt + = ( int ) ( dstlen + 1 ) ;
2008-09-29 15:44:19 +00:00
if ( ( size_t ) sp - > charcnt > sizeof sp - > chars )
return - 1 ;
cp = sp - > chars ;
( void ) strncpy ( cp , stdname , stdlen ) ;
cp + = stdlen ;
* cp + + = ' \0 ' ;
if ( dstlen ! = 0 ) {
( void ) strncpy ( cp , dstname , dstlen ) ;
* ( cp + dstlen ) = ' \0 ' ;
}
return 0 ;
}
/* **************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# if (_MSC_VER >= 1400) // VC8+
# define switch_assert(expr) assert(expr);__analysis_assume( expr )
# else
# define switch_assert(expr) assert(expr)
# endif
2010-02-06 03:38:24 +00:00
static void timesub ( const time_t * const timep , const long offset , register const struct state * const sp , register struct tm * const tmp )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
register const struct lsinfo * lp ;
register long days ;
register time_t rem ;
register int y ;
register int yleap ;
register const int * ip ;
register long corr ;
register int hit ;
register int i ;
2008-09-29 15:44:19 +00:00
switch_assert ( timep ! = NULL ) ;
switch_assert ( sp ! = NULL ) ;
switch_assert ( tmp ! = NULL ) ;
corr = 0 ;
hit = 0 ;
i = ( sp = = NULL ) ? 0 : sp - > leapcnt ;
while ( - - i > = 0 ) {
lp = & sp - > lsis [ i ] ;
if ( * timep > = lp - > ls_trans ) {
if ( * timep = = lp - > ls_trans ) {
2010-02-06 03:38:24 +00:00
hit = ( ( i = = 0 & & lp - > ls_corr > 0 ) | | ( i > 0 & & lp - > ls_corr > sp - > lsis [ i - 1 ] . ls_corr ) ) ;
2008-09-29 15:44:19 +00:00
if ( hit )
2010-02-06 03:38:24 +00:00
while ( i > 0 & & sp - > lsis [ i ] . ls_trans = = sp - > lsis [ i - 1 ] . ls_trans + 1 & & sp - > lsis [ i ] . ls_corr = = sp - > lsis [ i - 1 ] . ls_corr + 1 ) {
+ + hit ;
- - i ;
2008-09-29 15:44:19 +00:00
}
}
corr = lp - > ls_corr ;
break ;
}
}
2010-02-06 03:38:24 +00:00
days = ( long ) ( * timep / SECSPERDAY ) ;
2008-09-29 15:44:19 +00:00
rem = * timep % SECSPERDAY ;
2010-02-06 03:38:24 +00:00
# ifdef mc68k
2008-09-29 15:44:19 +00:00
/* If this is for CPU bugs workarounds, i would remove this anyway. Who would use it on an old mc68k ? */
if ( * timep = = 0x80000000 ) {
/*
2010-02-06 03:38:24 +00:00
* * A 3 B1 muffs the division on the most negative number .
*/
2008-09-29 15:44:19 +00:00
days = - 24855 ;
rem = - 11648 ;
}
# endif
rem + = ( offset - corr ) ;
while ( rem < 0 ) {
rem + = SECSPERDAY ;
- - days ;
}
while ( rem > = SECSPERDAY ) {
rem - = SECSPERDAY ;
+ + days ;
}
tmp - > tm_hour = ( int ) ( rem / SECSPERHOUR ) ;
rem = rem % SECSPERHOUR ;
tmp - > tm_min = ( int ) ( rem / SECSPERMIN ) ;
/*
2010-02-06 03:38:24 +00:00
* * A positive leap second requires a special
* * representation . This uses " ... ??:59:60 " et seq .
*/
2008-09-29 15:44:19 +00:00
tmp - > tm_sec = ( int ) ( rem % SECSPERMIN ) + hit ;
tmp - > tm_wday = ( int ) ( ( EPOCH_WDAY + days ) % DAYSPERWEEK ) ;
if ( tmp - > tm_wday < 0 )
tmp - > tm_wday + = DAYSPERWEEK ;
y = EPOCH_YEAR ;
# define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
while ( days < 0 | | days > = ( long ) year_lengths [ yleap = isleap ( y ) ] ) {
2010-02-06 03:38:24 +00:00
register int newy ;
2008-09-29 15:44:19 +00:00
2010-02-06 03:38:24 +00:00
newy = ( int ) ( y + days / DAYSPERNYEAR ) ;
2008-09-29 15:44:19 +00:00
if ( days < 0 )
- - newy ;
2010-02-06 03:38:24 +00:00
days - = ( newy - y ) * DAYSPERNYEAR + LEAPS_THRU_END_OF ( newy - 1 ) - LEAPS_THRU_END_OF ( y - 1 ) ;
2008-09-29 15:44:19 +00:00
y = newy ;
}
tmp - > tm_year = y - TM_YEAR_BASE ;
tmp - > tm_yday = ( int ) days ;
ip = mon_lengths [ yleap ] ;
for ( tmp - > tm_mon = 0 ; days > = ( long ) ip [ tmp - > tm_mon ] ; + + ( tmp - > tm_mon ) )
days = days - ( long ) ip [ tmp - > tm_mon ] ;
tmp - > tm_mday = ( int ) ( days + 1 ) ;
tmp - > tm_isdst = 0 ;
2009-05-15 18:19:30 +00:00
# if defined(HAVE_STRUCT_TM_TM_GMTOFF)
2008-09-29 15:44:19 +00:00
tmp - > tm_gmtoff = offset ;
# endif
}
/* **************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-02-06 03:38:24 +00:00
static void tztime ( const time_t * const timep , const char * tzstring , struct tm * const tmp )
2008-09-29 15:44:19 +00:00
{
2010-02-06 03:38:24 +00:00
struct state * tzptr , * sp ;
const time_t t = * timep ;
register int i ;
register const struct ttinfo * ttisp ;
2008-09-29 15:44:19 +00:00
2010-02-06 03:38:24 +00:00
if ( tzstring = = NULL )
2009-02-26 18:28:26 +00:00
tzstring = gmt ;
2008-09-29 15:44:19 +00:00
2010-02-06 03:38:24 +00:00
tzptr = ( struct state * ) malloc ( sizeof ( struct state ) ) ;
2008-09-29 15:44:19 +00:00
sp = tzptr ;
2010-02-06 03:38:24 +00:00
if ( tzptr ! = NULL ) {
2008-09-29 15:44:19 +00:00
memset ( tzptr , 0 , sizeof ( struct state ) ) ;
( void ) tzparse ( tzstring , tzptr , FALSE ) ;
2010-02-06 03:38:24 +00:00
if ( sp - > timecnt = = 0 | | t < sp - > ats [ 0 ] ) {
2008-09-29 15:44:19 +00:00
i = 0 ;
while ( sp - > ttis [ i ] . tt_isdst )
if ( + + i > = sp - > typecnt ) {
i = 0 ;
break ;
}
} else {
for ( i = 1 ; i < sp - > timecnt ; + + i )
if ( t < sp - > ats [ i ] )
break ;
i = sp - > types [ i - 1 ] ; // DST begin or DST end
}
ttisp = & sp - > ttis [ i ] ;
2009-02-26 18:28:26 +00:00
/*
2010-02-06 03:38:24 +00:00
To get ( wrong ) behavior that ' s compatible with System V Release 2.0
you ' d replace the statement below with
t + = ttisp - > tt_gmtoff ;
timesub ( & t , 0L , sp , tmp ) ;
*/
if ( tmp ! = NULL ) { /* Just a check not to assert */
timesub ( & t , ttisp - > tt_gmtoff , sp , tmp ) ;
2008-09-29 15:44:19 +00:00
tmp - > tm_isdst = ttisp - > tt_isdst ;
2009-05-15 18:19:30 +00:00
# if defined(HAVE_STRUCT_TM_TM_ZONE)
2008-09-29 15:44:19 +00:00
tmp - > tm_zone = & sp - > chars [ ttisp - > tt_abbrind ] ;
# endif
}
free ( tzptr ) ;
}
}
2006-11-27 22:30:48 +00:00
/* For Emacs:
* Local Variables :
* mode : c
2008-02-03 22:14:57 +00:00
* indent - tabs - mode : t
2006-11-27 22:30:48 +00:00
* tab - width : 4
* c - basic - offset : 4
* End :
* For VIM :
2008-07-03 19:12:26 +00:00
* vim : set softtabstop = 4 shiftwidth = 4 tabstop = 4 :
2006-11-27 22:30:48 +00:00
*/