From e1bed6530aea1d09e8256dd2fa27f5682d04a72e Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Mon, 26 Sep 2016 09:35:44 +0000 Subject: [3328635] Latest patch from Christian Werner --- generic/tclCmdMZ.c | 4 +-- generic/tclInt.h | 1 + generic/tclInterp.c | 43 +++++++++++++++++++++++++-- generic/tclTimer.c | 14 ++++----- unix/configure | 55 ++++++++++++++++++++++++++++++++++- unix/tcl.m4 | 5 +++- unix/tclConfig.h.in | 6 ++++ unix/tclUnixChan.c | 4 +-- unix/tclUnixEvent.c | 4 +-- unix/tclUnixNotfy.c | 47 +++++++++++++++++++++++++----- unix/tclUnixPort.h | 20 +++++++++++++ unix/tclUnixThrd.c | 29 ++++++++++++++++++ unix/tclUnixTime.c | 61 ++++++++++++++++++++++++++++++++++++++ win/configure | 18 ++++++++++++ win/configure.ac | 9 ++++++ win/tclWinInit.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++ win/tclWinNotify.c | 17 +++++++++-- win/tclWinTime.c | 2 ++ 18 files changed, 396 insertions(+), 27 deletions(-) diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index 885a0bc..cf7c686 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -4141,7 +4141,7 @@ Tcl_TimeObjCmd( objPtr = objv[1]; i = count; #ifndef TCL_WIDE_CLICKS - Tcl_GetTime(&start); + TclpGetMonotonicTime(&start); #else start = TclpGetWideClicks(); #endif @@ -4152,7 +4152,7 @@ Tcl_TimeObjCmd( } } #ifndef TCL_WIDE_CLICKS - Tcl_GetTime(&stop); + TclpGetMonotonicTime(&stop); totalMicroSec = ((double) (stop.sec - start.sec)) * 1.0e6 + (stop.usec - start.usec); #else diff --git a/generic/tclInt.h b/generic/tclInt.h index da1b5c5..bb67ba0 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -3183,6 +3183,7 @@ MODULE_SCOPE void TclpThreadSetMasterTSD(void *tsdKeyPtr, void *ptr); MODULE_SCOPE void * TclpThreadGetMasterTSD(void *tsdKeyPtr); MODULE_SCOPE void TclErrorStackResetIf(Tcl_Interp *interp, const char *msg, int length); +MODULE_SCOPE int TclpGetMonotonicTime(Tcl_Time *timePtr); /* *---------------------------------------------------------------- diff --git a/generic/tclInterp.c b/generic/tclInterp.c index a2de658..6c7ab64 100644 --- a/generic/tclInterp.c +++ b/generic/tclInterp.c @@ -3393,7 +3393,7 @@ Tcl_LimitCheck( (ticker % iPtr->limit.timeGranularity == 0))) { Tcl_Time now; - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); if (iPtr->limit.time.sec < now.sec || (iPtr->limit.time.sec == now.sec && iPtr->limit.time.usec < now.usec)) { @@ -3948,7 +3948,25 @@ Tcl_LimitSetTime( Tcl_Time *timeLimitPtr) { Interp *iPtr = (Interp *) interp; - Tcl_Time nextMoment; + Tcl_Time nextMoment, mono, real, limit; + + if (TclpGetMonotonicTime(&mono)) { + Tcl_GetTime(&real); + limit = *timeLimitPtr; + limit.sec -= real.sec; + limit.usec -= real.usec; + if (limit.usec < 0) { + limit.sec -= 1; + limit.usec += 1000000; + } + limit.sec += mono.sec; + limit.usec += mono.usec; + if (limit.usec >= 1000000) { + limit.sec += 1; + limit.usec -= 1000000; + } + timeLimitPtr = &limit; + } memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time)); if (iPtr->limit.timeEvent != NULL) { @@ -4033,7 +4051,26 @@ Tcl_LimitGetTime( Tcl_Time *timeLimitPtr) { Interp *iPtr = (Interp *) interp; - + Tcl_Time mono, real, limit; + + if (TclpGetMonotonicTime(&mono)) { + Tcl_GetTime(&real); + limit = iPtr->limit.time; + limit.sec -= mono.sec; + limit.usec -= mono.usec; + if (limit.usec < 0) { + limit.sec -= 1; + limit.usec += 1000000; + } + limit.sec += real.sec; + limit.usec += real.usec; + if (limit.usec >= 1000000) { + limit.sec += 1; + limit.usec -= 1000000; + } + memcpy(timeLimitPtr, &limit, sizeof(Tcl_Time)); + return; + } memcpy(timeLimitPtr, &iPtr->limit.time, sizeof(Tcl_Time)); } diff --git a/generic/tclTimer.c b/generic/tclTimer.c index 6d3938b..adc36ce 100644 --- a/generic/tclTimer.c +++ b/generic/tclTimer.c @@ -259,7 +259,7 @@ Tcl_CreateTimerHandler( * Compute when the event should fire. */ - Tcl_GetTime(&time); + TclpGetMonotonicTime(&time); time.sec += milliseconds/1000; time.usec += (milliseconds%1000)*1000; if (time.usec >= 1000000) { @@ -417,7 +417,7 @@ TimerSetupProc( * Compute the timeout for the next timer on the list. */ - Tcl_GetTime(&blockTime); + TclpGetMonotonicTime(&blockTime); blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec; blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec - blockTime.usec; @@ -468,7 +468,7 @@ TimerCheckProc( * Compute the timeout for the next timer on the list. */ - Tcl_GetTime(&blockTime); + TclpGetMonotonicTime(&blockTime); blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec; blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec - blockTime.usec; @@ -564,7 +564,7 @@ TimerHandlerEventProc( tsdPtr->timerPending = 0; currentTimerId = tsdPtr->lastTimerId; - Tcl_GetTime(&time); + TclpGetMonotonicTime(&time); while (1) { nextPtrPtr = &tsdPtr->firstTimerHandlerPtr; timerHandlerPtr = tsdPtr->firstTimerHandlerPtr; @@ -872,7 +872,7 @@ Tcl_AfterObjCmd( afterPtr->id = tsdPtr->afterId; tsdPtr->afterId += 1; - Tcl_GetTime(&wakeup); + TclpGetMonotonicTime(&wakeup); wakeup.sec += (long)(ms / 1000); wakeup.usec += ((long)(ms % 1000)) * 1000; if (wakeup.usec > 1000000) { @@ -1017,7 +1017,7 @@ AfterDelay( Tcl_Time endTime, now; Tcl_WideInt diff; - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); endTime = now; endTime.sec += (long)(ms/1000); endTime.usec += ((int)(ms%1000))*1000; @@ -1083,7 +1083,7 @@ AfterDelay( return TCL_ERROR; } } - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); } while (TCL_TIME_BEFORE(now, endTime)); return TCL_OK; } diff --git a/unix/configure b/unix/configure index e999455..068503c 100755 --- a/unix/configure +++ b/unix/configure @@ -4209,7 +4209,7 @@ $as_echo "$as_me: WARNING: Don't know how to find pthread lib on your system - y ac_saved_libs=$LIBS LIBS="$LIBS $THREADS_LIBS" - for ac_func in pthread_attr_setstacksize pthread_atfork + for ac_func in pthread_attr_setstacksize pthread_atfork pthread_condattr_setclock do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -4509,6 +4509,59 @@ fi fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 +$as_echo_n "checking for clock_gettime in -lrt... " >&6; } +if ${ac_cv_lib_rt_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_clock_gettime=yes +else + ac_cv_lib_rt_clock_gettime=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 +$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; } +if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then : + LIBS="$LIBS -lrt" +fi + + for ac_func in clock_gettime clock_nanosleep +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + # Add the threads support libraries LIBS="$LIBS$THREADS_LIBS" diff --git a/unix/tcl.m4 b/unix/tcl.m4 index f5aa84e..f2827c6 100644 --- a/unix/tcl.m4 +++ b/unix/tcl.m4 @@ -678,7 +678,7 @@ AC_DEFUN([SC_ENABLE_THREADS], [ ac_saved_libs=$LIBS LIBS="$LIBS $THREADS_LIBS" - AC_CHECK_FUNCS(pthread_attr_setstacksize pthread_atfork) + AC_CHECK_FUNCS(pthread_attr_setstacksize pthread_atfork pthread_condattr_setclock) LIBS=$ac_saved_libs else TCL_THREADS=0 @@ -2539,6 +2539,9 @@ AC_DEFUN([SC_TCL_LINK_LIBS], [ fi AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname, [LIBS="$LIBS -lnsl"])]) + + AC_CHECK_LIB(rt, clock_gettime, [LIBS="$LIBS -lrt"]) + AC_CHECK_FUNCS(clock_gettime clock_nanosleep) ]) #-------------------------------------------------------------------- diff --git a/unix/tclConfig.h.in b/unix/tclConfig.h.in index a4ab3e5..6c2f47d 100644 --- a/unix/tclConfig.h.in +++ b/unix/tclConfig.h.in @@ -19,6 +19,9 @@ /* Define to 1 if you have the `chflags' function. */ #undef HAVE_CHFLAGS +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + /* Define to 1 if you have the `copyfile' function. */ #undef HAVE_COPYFILE @@ -169,6 +172,9 @@ /* Define to 1 if you have the `pthread_attr_setstacksize' function. */ #undef HAVE_PTHREAD_ATTR_SETSTACKSIZE +/* Define to 1 if you have the `pthread_condattr_setclock' function. */ +#undef HAVE_PTHREAD_CONDATTR_SETCLOCK + /* Does putenv() copy strings or incorporate them by reference? */ #undef HAVE_PUTENV_THAT_COPIES diff --git a/unix/tclUnixChan.c b/unix/tclUnixChan.c index 6418f48..1f8c6a9 100644 --- a/unix/tclUnixChan.c +++ b/unix/tclUnixChan.c @@ -1787,7 +1787,7 @@ TclUnixWaitForFile( */ if (timeout > 0) { - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); abortTime.sec = now.sec + timeout/1000; abortTime.usec = now.usec + (timeout%1000)*1000; if (abortTime.usec >= 1000000) { @@ -1876,7 +1876,7 @@ TclUnixWaitForFile( * The select returned early, so we need to recompute the timeout. */ - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); if ((abortTime.sec < now.sec) || (abortTime.sec==now.sec && abortTime.usec<=now.usec)) { break; diff --git a/unix/tclUnixEvent.c b/unix/tclUnixEvent.c index 40aac6f..f7b675b 100644 --- a/unix/tclUnixEvent.c +++ b/unix/tclUnixEvent.c @@ -42,7 +42,7 @@ Tcl_Sleep( * time really has elapsed. If it's too early, go back to sleep again. */ - Tcl_GetTime(&before); + TclpGetMonotonicTime(&before); after = before; after.sec += ms/1000; after.usec += (ms%1000)*1000; @@ -81,7 +81,7 @@ Tcl_Sleep( } (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0, (SELECT_MASK *) 0, &delay); - Tcl_GetTime(&before); + TclpGetMonotonicTime(&before); } } diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index 9f9301f..6ed9443 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -100,6 +100,9 @@ typedef struct ThreadSpecificData { pthread_cond_t waitCV; /* Any other thread alerts a notifier that an * event is ready to be processed by signaling * this condition variable. */ +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + int useMonoTime; /* When true use CLOCK_MONOTONIC */ +#endif #endif /* __CYGWIN__ */ int waitCVinitialized; /* Variable to flag initialization of the structure */ int eventReady; /* True if an event is ready to be processed. @@ -353,7 +356,24 @@ Tcl_InitNotifier(void) tsdPtr->event = CreateEventW(NULL, 1 /* manual */, 0 /* !signaled */, NULL); #else +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + pthread_condattr_t attr; + + pthread_condattr_init(&attr); + tsdPtr->useMonoTime = + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0; + if (tsdPtr->useMonoTime) { + if (pthread_cond_init(&tsdPtr->waitCV, &attr)) { + tsdPtr->useMonoTime = 0; + pthread_cond_init(&tsdPtr->waitCV, NULL); + } + } else { + pthread_cond_init(&tsdPtr->waitCV, NULL); + } + pthread_condattr_destroy(&attr); +#else pthread_cond_init(&tsdPtr->waitCV, NULL); +#endif #endif /* __CYGWIN__ */ tsdPtr->waitCVinitialized = 1; } @@ -995,16 +1015,29 @@ Tcl_WaitForEvent( } #else if (timePtr != NULL) { - Tcl_Time now; - struct timespec ptime; + struct timespec ptime; - Tcl_GetTime(&now); - ptime.tv_sec = timePtr->sec + now.sec + (timePtr->usec + now.usec) / 1000000; - ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000); +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + if (tsdPtr->useMonoTime) { + clock_gettime(CLOCK_MONOTONIC, &ptime); + } else { + clock_gettime(CLOCK_REALTIME, &ptime); + } + ptime.tv_sec += timePtr->sec + + (timePtr->usec * 1000 + ptime.tv_nsec) / 1000000000; + ptime.tv_nsec = (timePtr->usec * 1000 + ptime.tv_nsec) % + 1000000000; +#else + Tcl_Time now; - pthread_cond_timedwait(&tsdPtr->waitCV, ¬ifierMutex, &ptime); + Tcl_GetTime(&now); + ptime.tv_sec = timePtr->sec + now.sec + + (timePtr->usec + now.usec) / 1000000; + ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000); +#endif + pthread_cond_timedwait(&tsdPtr->waitCV, ¬ifierMutex, &ptime); } else { - pthread_cond_wait(&tsdPtr->waitCV, ¬ifierMutex); + pthread_cond_wait(&tsdPtr->waitCV, ¬ifierMutex); } #endif /* __CYGWIN__ */ } diff --git a/unix/tclUnixPort.h b/unix/tclUnixPort.h index 2728957..d0f9cf9 100644 --- a/unix/tclUnixPort.h +++ b/unix/tclUnixPort.h @@ -640,6 +640,26 @@ extern char ** environ; /* *--------------------------------------------------------------------------- + * Use clock_gettime() only if _POSIX_MONOTONIC_CLOCK present. + *--------------------------------------------------------------------------- + */ + +#if defined(HAVE_CLOCK_GETTIME) && !defined(_POSIX_MONOTONIC_CLOCK) +# undef HAVE_CLOCK_GETTIME +#endif + +#ifdef TCL_THREADS +# ifndef HAVE_CLOCK_GETTIME +# undef HAVE_PTHREAD_CONDATTR_SETCLOCK +# endif +# ifndef HAVE_PTHREAD_CONDATTR_SETCLOCK +# undef HAVE_CLOCK_GETTIME +# endif +#endif + + +/* + *--------------------------------------------------------------------------- * The following macros and declarations represent the interface between * generic and unix-specific parts of Tcl. Some of the macros may override * functions declared in tclInt.h. diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c index 8f8eb7f..02f255f 100644 --- a/unix/tclUnixThrd.c +++ b/unix/tclUnixThrd.c @@ -520,6 +520,9 @@ Tcl_ConditionWait( pthread_cond_t *pcondPtr; pthread_mutex_t *pmutexPtr; struct timespec ptime; +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + int *monoFlagPtr; +#endif if (*condPtr == NULL) { pthread_mutex_lock(&masterLock); @@ -530,8 +533,26 @@ Tcl_ConditionWait( */ if (*condPtr == NULL) { +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + pthread_condattr_t attr; + + pcondPtr = ckalloc(sizeof(pthread_cond_t) + sizeof(int)); + monoFlagPtr = (int *) (pcondPtr + 1); + pthread_condattr_init(&attr); + *monoFlagPtr = (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0); + if (*monoFlagPtr) { + if (pthread_cond_init(pcondPtr, &attr)) { + *monoFlagPtr = 0; + pthread_cond_init(pcondPtr, NULL); + } + } else { + pthread_cond_init(pcondPtr, NULL); + } + pthread_condattr_destroy(&attr); +#else pcondPtr = ckalloc(sizeof(pthread_cond_t)); pthread_cond_init(pcondPtr, NULL); +#endif *condPtr = (Tcl_Condition) pcondPtr; TclRememberCondition(condPtr); } @@ -542,6 +563,13 @@ Tcl_ConditionWait( if (timePtr == NULL) { pthread_cond_wait(pcondPtr, pmutexPtr); } else { +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + monoFlagPtr = (int *) (pcondPtr + 1); + clock_gettime(*monoFlagPtr ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ptime); + ptime.tv_sec += timePtr->sec + + (timePtr->usec * 1000 + ptime.tv_nsec) / 1000000000; + ptime.tv_nsec = (timePtr->usec * 1000 + ptime.tv_nsec) % 1000000000; +#else Tcl_Time now; /* @@ -553,6 +581,7 @@ Tcl_ConditionWait( ptime.tv_sec = timePtr->sec + now.sec + (timePtr->usec + now.usec) / 1000000; ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000); +#endif pthread_cond_timedwait(pcondPtr, pmutexPtr, &ptime); } } diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c index df759d8..ae758de 100644 --- a/unix/tclUnixTime.c +++ b/unix/tclUnixTime.c @@ -105,6 +105,12 @@ TclpGetClicks(void) { unsigned long now; +#ifdef HAVE_CLOCK_GETTIME + Tcl_Time time; + + TclpGetMonotonicTime(&time); + now = time.sec*1000000 + time.usec; +#else #ifdef NO_GETTOD if (tclGetTimeProcPtr != NativeGetTime) { Tcl_Time time; @@ -125,6 +131,7 @@ TclpGetClicks(void) tclGetTimeProcPtr(&time, tclTimeClientData); now = time.sec*1000000 + time.usec; #endif +#endif return now; } @@ -376,9 +383,11 @@ Tcl_SetTimeProc( Tcl_ScaleTimeProc *scaleProc, ClientData clientData) { +#ifndef HAVE_CLOCK_GETTIME tclGetTimeProcPtr = getProc; tclScaleTimeProcPtr = scaleProc; tclTimeClientData = clientData; +#endif } /* @@ -533,6 +542,58 @@ CleanupMemory( } /* + *---------------------------------------------------------------------- + * + * TclpGetMononoticTime -- + * + * Like Tcl_GetTime() but return a monotonic clock source, + * if possible. Otherwise fall back to real (wall clock) time. + * + * Results: + * 1 if monotonic, 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclpGetMonotonicTime(Tcl_Time *timePtr) +{ +#ifdef HAVE_CLOCK_GETTIME + int ret; + struct timespec ts; + static int useMonoClock = -1; + + if (useMonoClock) { + ret = (clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + if (useMonoClock < 0) { + useMonoClock = ret; + if (!ret) { + (void) clock_gettime(CLOCK_REALTIME, &ts); + } + } else if (!ret) { + Tcl_Panic("clock_gettime(CLOCK_MONOTONIC) failed"); + } + } else { + (void) clock_gettime(CLOCK_REALTIME, &ts); + ret = 0; + } + timePtr->sec = ts.tv_sec; + timePtr->usec = ts.tv_nsec / 1000; + return ret; +#else + struct timeval tv; + + (void) gettimeofday(&tv, NULL); + timePtr->sec = tv.tv_sec; + timePtr->usec = tv.tv_usec; + return 0; +#endif +} + +/* * Local Variables: * mode: c * c-basic-offset: 4 diff --git a/win/configure b/win/configure index 73d6d9f..b8c4d0f 100755 --- a/win/configure +++ b/win/configure @@ -769,6 +769,7 @@ enable_shared enable_64bit enable_wince with_celib +with_tickcount enable_symbols enable_embedded_manifest ' @@ -1401,6 +1402,7 @@ Optional Packages: --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-encoding encoding for configuration values --with-celib=DIR use Windows/CE support library from DIR + --with-tickcount use GetTickCount for timers, turns off TIP #233 Some influential environment variables: CC C compiler command @@ -4723,6 +4725,22 @@ fi $as_echo "#define HAVE_ZLIB 1" >>confdefs.h + +# Check whether --with-tickcount was given. +if test "${with_tickcount+set}" = set; then : + withval=$with_tickcount; tcl_ok=$withval +else + tcl_ok=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $tcl_ok" >&5 +$as_echo "$tcl_ok" >&6; } +if test $tcl_ok = yes; then + +$as_echo "#define WIN32_USE_TICKCOUNT 1" >>confdefs.h + +fi + ac_fn_c_check_type "$LINENO" "intptr_t" "ac_cv_type_intptr_t" "$ac_includes_default" if test "x$ac_cv_type_intptr_t" = xyes; then : diff --git a/win/configure.ac b/win/configure.ac index 7405bf4..71038db 100644 --- a/win/configure.ac +++ b/win/configure.ac @@ -142,6 +142,15 @@ AS_IF([test "$tcl_ok" = "yes"], [ ]) AC_DEFINE(HAVE_ZLIB, 1, [Is there an installed zlib?]) +AC_ARG_WITH(tickcount, + AC_HELP_STRING([--with-tickcount], + [use GetTickCount for timers, turns off TIP #233]), + [tcl_ok=$withval], [tcl_ok=no]) +AC_MSG_RESULT([$tcl_ok]) +if test $tcl_ok = yes; then + AC_DEFINE(WIN32_USE_TICKCOUNT, 1, [Use GetTickCount for timers?]) +fi + AC_CHECK_TYPE([intptr_t], [ AC_DEFINE([HAVE_INTPTR_T], 1, [Do we have the intptr_t type?])], [ AC_CACHE_CHECK([for pointer-size signed integer type], tcl_cv_intptr_t, [ diff --git a/win/tclWinInit.c b/win/tclWinInit.c index b5a07aa..66d971b 100644 --- a/win/tclWinInit.c +++ b/win/tclWinInit.c @@ -107,6 +107,15 @@ static ProcessGlobalValue sourceLibraryDir = static void AppendEnvironment(Tcl_Obj *listPtr, const char *lib); static int ToUtf(const WCHAR *wSrc, char *dst); + +#ifdef WIN32_USE_TICKCOUNT +typedef ULONGLONG WINAPI (GetTickCount64Proc)(void); +static GetTickCount64Proc *GetTickCount64ProcPtr = NULL; +static CRITICAL_SECTION TickMutex; +static DWORD TickOffset, LastTick; +static ULONGLONG TickUpper; +#endif + /* *--------------------------------------------------------------------------- @@ -132,6 +141,9 @@ TclpInitPlatform(void) { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); +#ifdef WIN32_USE_TICKCOUNT + HMODULE handle; +#endif tclPlatform = TCL_PLATFORM_WINDOWS; @@ -141,6 +153,29 @@ TclpInitPlatform(void) */ WSAStartup(wVersionRequested, &wsaData); +#ifdef WIN32_USE_TICKCOUNT + /* + * Check for availability of the GetTickCount64() API. + */ + + handle = GetModuleHandleA("KERNEL32"); + if (handle != NULL) { + GetTickCount64ProcPtr = (GetTickCount64Proc *) + GetProcAddress(handle, "GetTickCount64"); + } + + InitializeCriticalSection(&TickMutex); + + /* + * Force a wrap around within the first few seconds when + * we need to fall back to GetTickCount(), e.g. on XP. + */ + + TickOffset = 0xffffe000 - GetTickCount(); + LastTick = TickOffset; + TickUpper = 0; +#endif + #ifdef STATIC_BUILD /* * If we are in a statically linked executable, then we need to explicitly @@ -713,6 +748,55 @@ TclpFindVariable( } /* + *---------------------------------------------------------------------- + * + * TclpGetMononoticTime -- + * + * Like Tcl_GetTime() but return a monotonic clock source, + * if possible. Otherwise fall back to real (wall clock) time. + * + * Results: + * 1 if monotonic, 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclpGetMonotonicTime(Tcl_Time *timePtr) +{ +#ifdef WIN32_USE_TICKCOUNT + ULONGLONG ms; + DWORD tick; + + if (GetTickCount64ProcPtr != NULL) { + ms = GetTickCount64ProcPtr(); + } else { + /* + * Emulate 64 bit wide tick counter, e.g. on XP. + */ + + EnterCriticalSection(&TickMutex); + tick = GetTickCount() + TickOffset; + if ((LastTick & 0x80000000) && !(tick & 0x80000000)) { + TickUpper += 0x100000000ULL; + } + LastTick = tick; + ms = TickUpper | LastTick; + LeaveCriticalSection(&TickMutex); + } + timePtr->sec = (long)(ms/1000); + timePtr->usec = ((long)(ms%1000))*1000; + return 1; +#else + Tcl_GetTime(timePtr); + return 0; +#endif +} + +/* * Local Variables: * mode: c * c-basic-offset: 4 diff --git a/win/tclWinNotify.c b/win/tclWinNotify.c index 28c8445..b3e5237 100644 --- a/win/tclWinNotify.c +++ b/win/tclWinNotify.c @@ -455,6 +455,12 @@ Tcl_WaitForEvent( */ if (timePtr) { +#ifdef WIN32_USE_TICKCOUNT + timeout = timePtr->sec * 1000 + timePtr->usec / 1000; + if (timeout == INFINITE) { + timeout--; + } +#else /* * TIP #233 (Virtualized Time). Convert virtual domain delay to * real-time. @@ -470,6 +476,7 @@ Tcl_WaitForEvent( } timeout = myTime.sec * 1000 + myTime.usec / 1000; +#endif } else { timeout = INFINITE; } @@ -577,7 +584,7 @@ Tcl_Sleep( vdelay.sec = ms / 1000; vdelay.usec = (ms % 1000) * 1000; - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); desired.sec = now.sec + vdelay.sec; desired.usec = now.usec + vdelay.usec; if (desired.usec > 1000000) { @@ -591,10 +598,13 @@ Tcl_Sleep( tclScaleTimeProcPtr(&vdelay, tclTimeClientData); sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000; + if (sleepTime == INFINITE) { + --sleepTime; + } for (;;) { SleepEx(sleepTime, TRUE); - Tcl_GetTime(&now); + TclpGetMonotonicTime(&now); if (now.sec > desired.sec) { break; } else if ((now.sec == desired.sec) && (now.usec >= desired.usec)) { @@ -606,6 +616,9 @@ Tcl_Sleep( tclScaleTimeProcPtr(&vdelay, tclTimeClientData); sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000; + if (sleepTime == INFINITE) { + --sleepTime; + } } } diff --git a/win/tclWinTime.c b/win/tclWinTime.c index 7b5c18a..e0d4219 100644 --- a/win/tclWinTime.c +++ b/win/tclWinTime.c @@ -1118,9 +1118,11 @@ Tcl_SetTimeProc( Tcl_ScaleTimeProc *scaleProc, ClientData clientData) { +#ifndef WIN32_USE_TICKCOUNT tclGetTimeProcPtr = getProc; tclScaleTimeProcPtr = scaleProc; tclTimeClientData = clientData; +#endif } /* -- cgit v0.12