summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclCmdMZ.c4
-rw-r--r--generic/tclInt.h1
-rw-r--r--generic/tclInterp.c43
-rw-r--r--generic/tclTimer.c14
-rwxr-xr-xunix/configure55
-rw-r--r--unix/tcl.m45
-rw-r--r--unix/tclConfig.h.in6
-rw-r--r--unix/tclUnixChan.c4
-rw-r--r--unix/tclUnixEvent.c4
-rw-r--r--unix/tclUnixNotfy.c47
-rw-r--r--unix/tclUnixPort.h20
-rw-r--r--unix/tclUnixThrd.c29
-rw-r--r--unix/tclUnixTime.c61
-rwxr-xr-xwin/configure18
-rw-r--r--win/configure.ac9
-rw-r--r--win/tclWinInit.c84
-rw-r--r--win/tclWinNotify.c17
-rw-r--r--win/tclWinTime.c2
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, &notifierMutex, &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, &notifierMutex, &ptime);
} else {
- pthread_cond_wait(&tsdPtr->waitCV, &notifierMutex);
+ pthread_cond_wait(&tsdPtr->waitCV, &notifierMutex);
}
#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
}
/*