From 597ce610007c1a6670af69b1738f5b263e87f723 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Wed, 28 Oct 2015 13:49:25 +0000 Subject: [PATCH] ITS#8295 fix Windows microsecond timer Also add ldap_pvt_gettimeofday() to emulate gettimeofday on Windows --- include/ldap_pvt.h | 6 ++ libraries/libldap/util-int.c | 187 +++++++++++++++++++++++++++-------- 2 files changed, 151 insertions(+), 42 deletions(-) diff --git a/include/ldap_pvt.h b/include/ldap_pvt.h index ea4c763305..141e1e7982 100644 --- a/include/ldap_pvt.h +++ b/include/ldap_pvt.h @@ -127,6 +127,12 @@ struct lutil_tm; LDAP_F( void ) ldap_pvt_gettime LDAP_P(( struct lutil_tm * )); +#ifdef _WIN32 +#define gettimeofday(tv,tz) ldap_pvt_gettimeofday(tv,tz) +LDAP_F( int ) +ldap_pvt_gettimeofday LDAP_P(( struct timeval *tv, void *unused )); +#endif + /* use this macro to allocate buffer for ldap_pvt_csnstr */ #define LDAP_PVT_CSNSTR_BUFSIZE 64 LDAP_F( size_t ) diff --git a/libraries/libldap/util-int.c b/libraries/libldap/util-int.c index 6b7aca4db7..c5c9218ab1 100644 --- a/libraries/libldap/util-int.c +++ b/libraries/libldap/util-int.c @@ -174,93 +174,195 @@ ldap_pvt_localtime( const time_t *timep, struct tm *result ) } #endif /* !USE_LOCALTIME_R */ -/* return a broken out time, with microseconds - */ +static int _ldap_pvt_gt_subs; + #ifdef _WIN32 /* Windows SYSTEMTIME only has 10 millisecond resolution, so we * also need to use a high resolution timer to get microseconds. * This is pretty clunky. */ -void -ldap_pvt_gettime( struct lutil_tm *tm ) +static LARGE_INTEGER _ldap_pvt_gt_freq; +static LARGE_INTEGER _ldap_pvt_gt_prev; +static int _ldap_pvt_gt_offset; + +#define SEC_TO_UNIX_EPOCH 11644473600LL +#define TICKS_PER_SECOND 10000000 + +static int +ldap_pvt_gettimeusec(int *sec) { - static LARGE_INTEGER cFreq; - static LARGE_INTEGER prevCount; - static int subs; - static int offset; LARGE_INTEGER count; - SYSTEMTIME st; - GetSystemTime( &st ); QueryPerformanceCounter( &count ); /* It shouldn't ever go backwards, but multiple CPUs might * be able to hit in the same tick. */ LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); - if ( count.QuadPart <= prevCount.QuadPart ) { - subs++; - } else { - subs = 0; - prevCount = count; - } - LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); - /* We assume Windows has at least a vague idea of * when a second begins. So we align our microsecond count * with the Windows millisecond count using this offset. * We retain the submillisecond portion of our own count. * * Note - this also assumes that the relationship between - * the PerformanceCouunter and SystemTime stays constant; + * the PerformanceCounter and SystemTime stays constant; * that assumption breaks if the SystemTime is adjusted by * an external action. */ - if ( !cFreq.QuadPart ) { + if ( !_ldap_pvt_gt_freq.QuadPart ) { + LARGE_INTEGER c2; + ULARGE_INTEGER ut; + FILETIME ft0, ft1; long long t; int usec; - QueryPerformanceFrequency( &cFreq ); - /* just get sub-second portion of counter */ - t = count.QuadPart % cFreq.QuadPart; + /* Initialize our offset */ + QueryPerformanceFrequency( &_ldap_pvt_gt_freq ); + + /* Wait for a tick of the system time: 10-15ms */ + GetSystemTimeAsFileTime( &ft0 ); + do { + GetSystemTimeAsFileTime( &ft1 ); + } while ( ft1.dwLowDateTime == ft0.dwLowDateTime ); + + ut.LowPart = ft1.dwLowDateTime; + ut.HighPart = ft1.dwHighDateTime; + QueryPerformanceCounter( &c2 ); + + /* get second and fraction portion of counter */ + t = c2.QuadPart % (_ldap_pvt_gt_freq.QuadPart*10); /* convert to microseconds */ t *= 1000000; - usec = t / cFreq.QuadPart; + usec = t / _ldap_pvt_gt_freq.QuadPart; - offset = usec - st.wMilliseconds * 1000; + ut.QuadPart /= 10; + ut.QuadPart %= 10000000; + _ldap_pvt_gt_offset = usec - ut.QuadPart; + count = c2; } - - tm->tm_usub = subs; + if ( count.QuadPart <= _ldap_pvt_gt_prev.QuadPart ) { + _ldap_pvt_gt_subs++; + } else { + _ldap_pvt_gt_subs = 0; + _ldap_pvt_gt_prev = count; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); /* convert to microseconds */ - count.QuadPart %= cFreq.QuadPart; + count.QuadPart %= _ldap_pvt_gt_freq.QuadPart*10; count.QuadPart *= 1000000; - count.QuadPart /= cFreq.QuadPart; - count.QuadPart -= offset; + count.QuadPart /= _ldap_pvt_gt_freq.QuadPart; + count.QuadPart -= _ldap_pvt_gt_offset; + + /* We've extracted the 1s and microseconds. + * The 1sec digit is used to detect wraparound in microsecnds. + */ + if (count.QuadPart < 0) + count.QuadPart += 10000000; + else if (count.QuadPart >= 10000000) + count.QuadPart -= 10000000; + + *sec = count.QuadPart / 1000000; + return count.QuadPart % 1000000; +} - tm->tm_usec = count.QuadPart % 1000000; - if ( tm->tm_usec < 0 ) - tm->tm_usec += 1000000; + +/* emulate POSIX gettimeofday */ +int +ldap_pvt_gettimeofday( struct timeval *tv, void *unused ) +{ + FILETIME ft; + ULARGE_INTEGER ut; + int sec, sec0; + + GetSystemTimeAsFileTime( &ft ); + ut.LowPart = ft.dwLowDateTime; + ut.HighPart = ft.dwHighDateTime; + + /* convert to usec */ + ut.QuadPart /= (TICKS_PER_SECOND / 1000000); + + tv->tv_usec = ldap_pvt_gettimeusec(&sec); + tv->tv_sec = ut.QuadPart / 1000000 - SEC_TO_UNIX_EPOCH; + + /* check for carry from microseconds */ + sec0 = tv->tv_sec % 10; + if (sec0 < sec || (sec0 == 9 && !sec)) + tv->tv_sec++; + + return 0; +} + +/* return a broken out time, with microseconds + */ +void +ldap_pvt_gettime( struct lutil_tm *tm ) +{ + SYSTEMTIME st; + int sec, sec0; + static const char daysPerMonth[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + GetSystemTime( &st ); + tm->tm_usec = ldap_pvt_gettimeusec(&sec); + tm->tm_usub = _ldap_pvt_gt_subs; /* any difference larger than microseconds is * already reflected in st */ - tm->tm_sec = st.wSecond; tm->tm_min = st.wMinute; tm->tm_hour = st.wHour; tm->tm_mday = st.wDay; tm->tm_mon = st.wMonth - 1; tm->tm_year = st.wYear - 1900; + + /* check for carry from microseconds */ + sec0 = tm->tm_sec % 10; + if (sec0 < sec || (sec0 == 9 && !sec)) { + tm->tm_sec++; + /* FIXME: we don't handle leap seconds */ + if (tm->tm_sec > 59) { + tm->tm_sec = 0; + tm->tm_min++; + if (tm->tm_min > 59) { + tm->tm_min = 0; + tm->tm_hour++; + if (tm->tm_hour > 23) { + int days = daysPerMonth[tm->tm_mon]; + tm->tm_hour = 0; + tm->tm_mday++; + + /* if it's February of a leap year, + * add 1 day to this month + */ + if (tm->tm_mon == 1 && + ((!(st.wYear % 4) && (st.wYear % 100)) || + !(st.wYear % 400))) + days++; + + if (tm->tm_mday > days) { + tm->tm_mday = 1; + tm->tm_mon++; + if (tm->tm_mon > 11) { + tm->tm_mon = 0; + tm->tm_year++; + } + } + } + } + } + } } #else + +static struct timeval _ldap_pvt_gt_prevTv; + void ldap_pvt_gettime( struct lutil_tm *ltm ) { struct timeval tv; - static struct timeval prevTv; - static int subs; struct tm tm; time_t t; @@ -269,16 +371,17 @@ ldap_pvt_gettime( struct lutil_tm *ltm ) t = tv.tv_sec; LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); - if ( tv.tv_sec < prevTv.tv_sec - || ( tv.tv_sec == prevTv.tv_sec && tv.tv_usec <= prevTv.tv_usec )) { - subs++; + if ( tv.tv_sec < _ldap_pvt_gt_prevTv.tv_sec + || ( tv.tv_sec == _ldap_pvt_gt_prevTv.tv_sec + && tv.tv_usec <= _ldap_pvt_gt_prevTv.tv_usec )) { + _ldap_pvt_gt_subs++; } else { - subs = 0; - prevTv = tv; + _ldap_pvt_gt_subs = 0; + _ldap_pvt_gt_prevTv = tv; } LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); - ltm->tm_usub = subs; + ltm->tm_usub = _ldap_pvt_gt_subs; ldap_pvt_gmtime( &t, &tm ); -- 2.39.2