]> git.sur5r.net Git - openldap/commitdiff
ITS#8295 fix Windows microsecond timer
authorHoward Chu <hyc@openldap.org>
Wed, 28 Oct 2015 13:49:25 +0000 (13:49 +0000)
committerHoward Chu <hyc@openldap.org>
Wed, 28 Oct 2015 13:49:25 +0000 (13:49 +0000)
Also add ldap_pvt_gettimeofday() to
emulate gettimeofday on Windows

include/ldap_pvt.h
libraries/libldap/util-int.c

index ea4c7633052ed460af73ab05ea5f43cecfa63383..141e1e798200fbb6b7cc6677ff319a86a375236d 100644 (file)
@@ -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 )
index 6b7aca4db753eac8a97cf848d3b557eac7aaa362..c5c9218ab13115cbc45e4653282f600e617f1372 100644 (file)
@@ -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 );