diff options
author | oehhar <harald.oehlmann@elmicron.de> | 2016-03-30 20:03:21 (GMT) |
---|---|---|
committer | oehhar <harald.oehlmann@elmicron.de> | 2016-03-30 20:03:21 (GMT) |
commit | d54d3f124b4a7f2e781821ab9dfc3a7a9e7e299e (patch) | |
tree | 4670cd37f5937aaed3fdab8fecdc7d3fe1cb2ffd | |
parent | 77ccaefbb1482145de9b9276979318e88b9e542e (diff) | |
download | tcl-d54d3f124b4a7f2e781821ab9dfc3a7a9e7e299e.zip tcl-d54d3f124b4a7f2e781821ab9dfc3a7a9e7e299e.tar.gz tcl-d54d3f124b4a7f2e781821ab9dfc3a7a9e7e299e.tar.bz2 |
The following patch against a stock tcl 8.6.5 source tree tries to
implement most of TIP #302 for UN*X and WIN32/64:
* use clock_gettime(CLOCK_MONOTONIC) for relative timing (UN*X)
when available (tested at runtime)
* use PTHREAD_CONDATTR_SETCLOCK et.al. for pthread_cond (UN*X)
when available (tested at runtime)
* use GetTickCount64() for relative timing (WIN64)
* use GetTickCount() for relative timing (WIN32 for WINNT<6.0)
* configure (UN*X) detects availabilty of clock_gettime() and
pthread_condattr_setclock() plus adds optional -lrt to LIBS
* configure (WIN32) adds command line option --with-tickcount
to turn on GetTickCount*() usage
Trade-off: implementation eliminates virtualized time, unfortunately.
Best regards,
Christian Werner
--- missing merge ---
diff -ur tcl8.6.5-orig/win/configure.in tcl8.6.5/win/configure.in
--- tcl8.6.5-orig/win/configure.in 2016-02-29 20:13:09.000000000 +0100
+++ tcl8.6.5/win/configure.in 2016-03-25 06:47:47.712516747 +0100
@@ -142,6 +142,15 @@
])
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 interp time limits]),
+ [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, [
-rwxr-xr-x[-rw-r--r--] | generic/tclCmdMZ.c | 43 | ||||
-rwxr-xr-x[-rw-r--r--] | generic/tclInterp.c | 26 | ||||
-rwxr-xr-x[-rw-r--r--] | generic/tclTimer.c | 251 | ||||
-rwxr-xr-x | unix/configure | 55 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tcl.m4 | 4 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclConfig.h.in | 6 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclUnixChan.c | 21 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclUnixEvent.c | 18 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclUnixNotfy.c | 47 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclUnixPort.h | 19 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclUnixThrd.c | 33 | ||||
-rwxr-xr-x[-rw-r--r--] | unix/tclUnixTime.c | 11 | ||||
-rwxr-xr-x | win/configure | 28 | ||||
-rwxr-xr-x[-rw-r--r--] | win/tclWinNotify.c | 48 | ||||
-rwxr-xr-x[-rw-r--r--] | win/tclWinTime.c | 13 |
15 files changed, 610 insertions, 13 deletions
diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index 13f9e7d..4c83b80 100644..100755 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -4124,11 +4124,24 @@ Tcl_TimeObjCmd( register int i, result; int count; double totalMicroSec; +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + ULONGLONG start, stop; +#else + DWORD start, stop; +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + int monoClock = 1; + struct timespec start, stop; +#else #ifndef TCL_WIDE_CLICKS Tcl_Time start, stop; #else Tcl_WideInt start, stop; #endif +#endif +#endif if (objc == 2) { count = 1; @@ -4144,17 +4157,45 @@ Tcl_TimeObjCmd( objPtr = objv[1]; i = count; +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + start = GetTickCount64(); +#else + start = GetTickCount(); +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) { + clock_gettime(CLOCK_REALTIME, &start); + monoClock = 0; + } +#else #ifndef TCL_WIDE_CLICKS Tcl_GetTime(&start); #else start = TclpGetWideClicks(); #endif +#endif +#endif while (i-- > 0) { result = Tcl_EvalObjEx(interp, objPtr, 0); if (result != TCL_OK) { return result; } } +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + stop = GetTickCount64(); +#else + stop = GetTickCount(); +#endif + totalMicroSec = (stop - start) * 1000.0; +#else +#ifdef HAVE_CLOCK_GETTIME + clock_gettime(monoClock ? CLOCK_MONOTONIC : CLOCK_REALTIME, &stop); + totalMicroSec = ((double) (stop.tv_sec - start.tv_sec)) * 1.0e6 + + (stop.tv_nsec - start.tv_nsec) / 1000.0; +#else #ifndef TCL_WIDE_CLICKS Tcl_GetTime(&stop); totalMicroSec = ((double) (stop.sec - start.sec)) * 1.0e6 @@ -4163,6 +4204,8 @@ Tcl_TimeObjCmd( stop = TclpGetWideClicks(); totalMicroSec = ((double) TclpWideClicksToNanoseconds(stop - start))/1.0e3; #endif +#endif +#endif if (count <= 1) { /* diff --git a/generic/tclInterp.c b/generic/tclInterp.c index cd0dc18..f7dfa3a 100644..100755 --- a/generic/tclInterp.c +++ b/generic/tclInterp.c @@ -3416,8 +3416,24 @@ Tcl_LimitCheck( ((iPtr->limit.timeGranularity == 1) || (ticker % iPtr->limit.timeGranularity == 0))) { Tcl_Time now; +#if (_WIN32_WINNT >= 0x0600) && defined(WIN32_USE_TICKCOUNT) + ULONGLONG ticks = GetTickCount64(); + now.sec = ticks / 1000; + now.usec = (ticks % 1000) * 1000; +#else +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + now.sec = ts.tv_sec; + now.usec = ts.tv_nsec * 1000; +#else Tcl_GetTime(&now); +#endif +#endif if (iPtr->limit.time.sec < now.sec || (iPtr->limit.time.sec == now.sec && iPtr->limit.time.usec < now.usec)) { @@ -3858,6 +3874,12 @@ Tcl_LimitTypeSet( int type) { Interp *iPtr = (Interp *) interp; +#if (_WIN32_WINNT < 0x0600) && defined(WIN32_USE_TICKCOUNT) + if (type == TCL_LIMIT_TIME) { + /* not implemented for _WIN32 */ + return; + } +#endif iPtr->limit.active |= type; } @@ -4382,12 +4404,16 @@ InheritLimitsFromMaster( slavePtr->limit.cmdCount = 0; slavePtr->limit.cmdGranularity = masterPtr->limit.cmdGranularity; } +#if (_WIN32_WINNT < 0x0600) && defined(WIN32_USE_TICKCOUNT) + /* not implemented for _WIN32 */ +#else if (masterPtr->limit.active & TCL_LIMIT_TIME) { slavePtr->limit.active |= TCL_LIMIT_TIME; memcpy(&slavePtr->limit.time, &masterPtr->limit.time, sizeof(Tcl_Time)); slavePtr->limit.timeGranularity = masterPtr->limit.timeGranularity; } +#endif } /* diff --git a/generic/tclTimer.c b/generic/tclTimer.c index c10986a..9fa013f 100644..100755 --- a/generic/tclTimer.c +++ b/generic/tclTimer.c @@ -259,7 +259,33 @@ Tcl_CreateTimerHandler( * Compute when the event should fire. */ +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + ULONGLONG ticks = GetTickCount64(); + + time.sec = (long) ((ticks+milliseconds)/1000); + time.usec = (long) (((ticks+milliseconds)%1000)*1000); +#else + if (milliseconds > 0x7FFFFFFF) { + milliseconds = 0x7FFFFFFF; + } else if (milliseconds < 0) { + milliseconds = 0; + } + time.sec = milliseconds + GetTickCount(); + time.usec = 0; +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + time.sec = ts.tv_sec; + time.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&time); +#endif time.sec += milliseconds/1000; time.usec += (milliseconds%1000)*1000; if (time.usec >= 1000000) { @@ -316,9 +342,15 @@ TclCreateAbsoluteTimerHandler( for (tPtr2 = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL; prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) { +#if (_WIN32_WINNT < 0x0600) && defined(WIN32_USE_TICKCOUNT) + if (timerHandlerPtr->time.sec - tPtr2->time.sec < 0) { + break; + } +#else if (TCL_TIME_BEFORE(timerHandlerPtr->time, tPtr2->time)) { break; } +#endif } timerHandlerPtr->nextPtr = tPtr2; if (prevPtr == NULL) { @@ -417,7 +449,46 @@ TimerSetupProc( * Compute the timeout for the next timer on the list. */ +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + ULONGLONG ticks = GetTickCount64(); + + blockTime.sec = (long) + ((ULONGLONG) tsdPtr->firstTimerHandlerPtr->time.sec - + (ticks / 1000)); + blockTime.usec = (long) + ((ULONGLONG) tsdPtr->firstTimerHandlerPtr->time.usec - + (ticks % 1000) * 1000); + if (blockTime.usec < 0) { + blockTime.sec -= 1; + blockTime.usec += 1000000; + } + if (blockTime.sec < 0) { + blockTime.sec = 0; + blockTime.usec = 0; + } +#else + blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - GetTickCount(); + if (blockTime.sec <= 0) { + blockTime.sec = 0; + blockTime.usec = 0; + } else { + blockTime.usec = (blockTime.sec % 1000) * 1000; + blockTime.sec /= 1000; + } +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + blockTime.sec = ts.tv_sec; + blockTime.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&blockTime); +#endif blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec; blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec - blockTime.usec; @@ -429,6 +500,7 @@ TimerSetupProc( blockTime.sec = 0; blockTime.usec = 0; } +#endif } else { return; } @@ -468,7 +540,46 @@ TimerCheckProc( * Compute the timeout for the next timer on the list. */ +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + ULONGLONG ticks = GetTickCount64(); + + blockTime.sec = (long) + ((ULONGLONG) tsdPtr->firstTimerHandlerPtr->time.sec - + (ticks / 1000)); + blockTime.usec = (long) + ((ULONGLONG) tsdPtr->firstTimerHandlerPtr->time.usec - + (ticks % 1000) * 1000); + if (blockTime.usec < 0) { + blockTime.sec -= 1; + blockTime.usec += 1000000; + } + if (blockTime.sec < 0) { + blockTime.sec = 0; + blockTime.usec = 0; + } +#else + blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - GetTickCount(); + if (blockTime.sec <= 0) { + blockTime.sec = 0; + blockTime.usec = 0; + } else { + blockTime.usec = (blockTime.sec % 1000) * 1000; + blockTime.sec /= 1000; + } +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + blockTime.sec = ts.tv_sec; + blockTime.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&blockTime); +#endif blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec; blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec - blockTime.usec; @@ -480,6 +591,7 @@ TimerCheckProc( blockTime.sec = 0; blockTime.usec = 0; } +#endif /* * If the first timer has expired, stick an event on the queue. @@ -526,6 +638,12 @@ TimerHandlerEventProc( Tcl_Time time; int currentTimerId; ThreadSpecificData *tsdPtr = InitTimer(); +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; +#endif +#if (_WIN32_WINNT >= 0x0600) && defined(WIN32_USE_TICKCOUNT) + ULONGLONG ticks; +#endif /* * Do nothing if timers aren't enabled. This leaves the event on the @@ -564,7 +682,26 @@ TimerHandlerEventProc( tsdPtr->timerPending = 0; currentTimerId = tsdPtr->lastTimerId; +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + ticks = GetTickCount64(); + time.sec = ticks / 1000; + time.usec = (ticks % 1000) * 1000; +#else + time.sec = GetTickCount(); + time.usec = 0; +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + time.sec = ts.tv_sec; + time.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&time); +#endif +#endif while (1) { nextPtrPtr = &tsdPtr->firstTimerHandlerPtr; timerHandlerPtr = tsdPtr->firstTimerHandlerPtr; @@ -572,10 +709,16 @@ TimerHandlerEventProc( break; } +#if (_WIN32_WINNT < 0x0600) && defined(WIN32_USE_TICKCOUNT) + if (time.sec - timerHandlerPtr->time.sec < 0) { + break; + } +#else if (TCL_TIME_BEFORE(time, timerHandlerPtr->time)) { break; } +#endif /* * Bail out if the next timer is of a newer generation. */ @@ -845,6 +988,13 @@ Tcl_AfterObjCmd( switch (index) { case -1: { +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; +#endif +#if (_WIN32_WINNT >= 0x0600) && defined(WIN32_USE_TICKCOUNT) + ULONGLONG ticks; +#endif + if (ms < 0) { ms = 0; } @@ -872,13 +1022,43 @@ Tcl_AfterObjCmd( afterPtr->id = tsdPtr->afterId; tsdPtr->afterId += 1; ++#ifdef WIN32_USE_TICKCOUNT ++#if (_WIN32_WINNT >= 0x0600) ++ ticks = GetTickCount64(); ++ wakeup.sec = ticks / 1000; ++ wakeup.usec = (ticks % 1000) * 1000; ++ wakeup.sec += (long)(ms / 1000); ++ wakeup.usec += ((long)(ms % 1000)) * 1000; ++ if (wakeup.usec > 1000000) { ++ wakeup.sec++; ++ wakeup.usec -= 1000000; ++ } +#else + if (ms > 0x7FFFFFFF) { + ms = 0x7FFFFFFF; + } else if (ms < 0) { + ms = 0; + } + wakeup.sec = ms + GetTickCount(); + wakeup.usec = 0; +#endif +#else +#ifdef HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + wakeup.sec = ts.tv_sec; + wakeup.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&wakeup); +#endif wakeup.sec += (long)(ms / 1000); wakeup.usec += ((long)(ms % 1000)) * 1000; if (wakeup.usec > 1000000) { wakeup.sec++; wakeup.usec -= 1000000; } +#endif afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup, AfterProc, afterPtr); afterPtr->nextPtr = assocPtr->firstAfterPtr; @@ -1012,12 +1192,49 @@ AfterDelay( Tcl_Interp *interp, Tcl_WideInt ms) { + Tcl_Time endTime, now; +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) Interp *iPtr = (Interp *) interp; + ULONGLONG ticks = GetTickCount64(); + Tcl_WideInt diff; - Tcl_Time endTime, now; + now.sec = ticks/1000; + now.usec = (ticks%1000)*1000; + endTime = now; + endTime.sec += (long)(ms/1000); + endTime.usec += ((int)(ms%1000))*1000; + if (endTime.usec >= 1000000) { + endTime.sec++; + endTime.usec -= 1000000; + } +#else + int diff; + + now.sec = GetTickCount(); + now.usec = 0; + if (ms > 0x7FFFFFFF) { + ms = 0x7FFFFFFF; + } else if (ms < 0) { + ms = 0; + } + endTime.sec = now.sec + ms; + endTime.usec = 0; +#endif +#else + Interp *iPtr = (Interp *) interp; Tcl_WideInt diff; +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + now.sec = ts.tv_sec; + now.usec = ts.tv_nsec/1000; +#else Tcl_GetTime(&now); +#endif endTime = now; endTime.sec += (long)(ms/1000); endTime.usec += ((int)(ms%1000))*1000; @@ -1025,6 +1242,7 @@ AfterDelay( endTime.sec++; endTime.usec -= 1000000; } +#endif do { if (Tcl_AsyncReady()) { @@ -1035,6 +1253,15 @@ AfterDelay( if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) { return TCL_ERROR; } +#if (_WIN32_WINNT < 0x0600) && defined(WIN32_USE_TICKCOUNT) + diff = endTime.sec - now.sec; + if (diff > 0) { + Tcl_Sleep((long) diff); + } else { + break; + } + now.sec = GetTickCount(); +#else if (iPtr->limit.timeEvent != NULL && TCL_TIME_BEFORE(iPtr->limit.time, now)) { iPtr->limit.granularityTicker = 0; @@ -1083,8 +1310,28 @@ AfterDelay( return TCL_ERROR; } } +#if (_WIN32_WINNT >= 0x0600) && defined(WIN32_USE_TICKCOUNT) + ticks = GetTickCount64(); + now.sec = ticks / 1000; + now.usec = (ticks % 1000) * 1000; +#else +#ifdef HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + now.sec = ts.tv_sec; + now.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&now); - } while (TCL_TIME_BEFORE(now, endTime)); +#endif +#endif +#endif + } +#if (_WIN32_WINNT < 0x0600) && defined(WIN32_USE_TICKCOUNT) + while (now.sec - endTime.sec < 0); +#else + while (TCL_TIME_BEFORE(now, endTime)); +#endif 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..ff0237c 100644..100755 --- 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,8 @@ 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..100755 --- 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 b4b2739..b253c16 100644..100755 --- a/unix/tclUnixChan.c +++ b/unix/tclUnixChan.c @@ -1763,12 +1763,16 @@ TclUnixWaitForFile( * at all, and a value of -1 means wait * forever. */ { - Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */ + Tcl_Time abortTime = {0, 0}, now = { 0, 0 }; /* silence gcc 4 warning */ struct timeval blockTime, *timeoutPtr; int numFound, result = 0; fd_set readableMask; fd_set writableMask; fd_set exceptionMask; +#ifdef HAVE_CLOCK_GETTIME + int monoClock = 1; + struct timespec ts; +#endif #ifndef _DARWIN_C_SOURCE /* @@ -1787,7 +1791,16 @@ TclUnixWaitForFile( */ if (timeout > 0) { +#ifdef HAVE_CLOCK_GETTIME + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + monoClock = 0; + } + now.sec = ts.tv_sec; + now.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&now); +#endif abortTime.sec = now.sec + timeout/1000; abortTime.usec = now.usec + (timeout%1000)*1000; if (abortTime.usec >= 1000000) { @@ -1876,7 +1889,13 @@ TclUnixWaitForFile( * The select returned early, so we need to recompute the timeout. */ +#ifdef HAVE_CLOCK_GETTIME + clock_gettime(monoClock ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts); + now.sec = ts.tv_sec; + now.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&now); +#endif 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..e31aef2 100644..100755 --- a/unix/tclUnixEvent.c +++ b/unix/tclUnixEvent.c @@ -42,7 +42,19 @@ Tcl_Sleep( * time really has elapsed. If it's too early, go back to sleep again. */ +#ifdef HAVE_CLOCK_GETTIME + int monoClock = 1; + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + monoClock = 0; + } + before.sec = ts.tv_sec; + before.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&before); +#endif after = before; after.sec += ms/1000; after.usec += (ms%1000)*1000; @@ -81,7 +93,13 @@ Tcl_Sleep( } (void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0, (SELECT_MASK *) 0, &delay); +#ifdef HAVE_CLOCK_GETTIME + clock_gettime(monoClock ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts); + before.sec = ts.tv_sec; + before.usec = ts.tv_nsec / 1000; +#else Tcl_GetTime(&before); +#endif } } diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index 3422089..4fbad85 100644..100755 --- 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 monoClock; /* 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->monoClock = + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0; + if (tsdPtr->monoClock) { + if (pthread_cond_init(&tsdPtr->waitCV, &attr)) { + tsdPtr->monoClock = 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->monoClock) { + 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..8429690 100644..100755 --- a/unix/tclUnixPort.h +++ b/unix/tclUnixPort.h @@ -640,6 +640,25 @@ 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 554a2dc..90ac379 100644..100755 --- 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 *flagPtr; +#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)); + flagPtr = (int *) (pcondPtr + 1); + pthread_condattr_init(&attr); + *flagPtr = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0; + if (*flagPtr) { + if (pthread_cond_init(pcondPtr, &attr)) { + *flagPtr = 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,17 @@ Tcl_ConditionWait( if (timePtr == NULL) { pthread_cond_wait(pcondPtr, pmutexPtr); } else { +#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) + flagPtr = (int *) (pcondPtr + 1); + if (*flagPtr) { + 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; /* @@ -553,6 +585,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 315bcf9..a98856d 100644..100755 --- a/unix/tclUnixTime.c +++ b/unix/tclUnixTime.c @@ -105,6 +105,14 @@ TclpGetClicks(void) { unsigned long now; +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + clock_gettime(CLOCK_REALTIME, &ts); + } + now = ts.tv_sec * 1000000 + ts.tv_nsec / 1000; +#else #ifdef NO_GETTOD if (tclGetTimeProcPtr != NativeGetTime) { Tcl_Time time; @@ -125,6 +133,7 @@ TclpGetClicks(void) tclGetTimeProcPtr(&time, tclTimeClientData); now = time.sec*1000000 + time.usec; #endif +#endif return now; } @@ -376,9 +385,11 @@ Tcl_SetTimeProc( Tcl_ScaleTimeProc *scaleProc, ClientData clientData) { +#ifndef HAVE_CLOCK_GETTIME tclGetTimeProcPtr = getProc; tclScaleTimeProcPtr = scaleProc; tclTimeClientData = clientData; +#endif } /* diff --git a/win/configure b/win/configure index 73d6d9f..bc14c2a 100755 --- a/win/configure +++ b/win/configure @@ -1401,6 +1401,8 @@ 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 interp time + limits Some influential environment variables: CC C compiler command @@ -4673,6 +4675,32 @@ case ${host_alias} in ;; esac +# *** HaO 2016-03-30 this may be at the wrong place. +# *** should be in Line 4410 betwen +# _ACEOF +# and +# echo "$as_me:$LINENO: checking for intptr_t" >&5 +# echo $ECHO_N "checking for intptr_t... $ECHO_C" >&6 +# if test "${ac_cv_type_intptr_t+set}" = set; then + +# Check whether --with-tickcount or --without-tickcount was given. +if test "${with_tickcount+set}" = set; then + withval="$with_tickcount" + tcl_ok=$withval +else + tcl_ok=no +fi; +echo "$as_me:$LINENO: result: $tcl_ok" >&5 +echo "${ECHO_T}$tcl_ok" >&6 +if test $tcl_ok = yes; then + +cat >>confdefs.h <<\_ACEOF +#define WIN32_USE_TICKCOUNT 1 +_ACEOF + +fi + + #------------------------------------------------------------------------ # Add stuff for zlib; note that this is mostly done in the makefile now # as we just assume that the platform hasn't got a usable z.lib diff --git a/win/tclWinNotify.c b/win/tclWinNotify.c index ea4035b..209d8a6 100644..100755 --- a/win/tclWinNotify.c +++ b/win/tclWinNotify.c @@ -445,6 +445,12 @@ Tcl_WaitForEvent( */ if (timePtr) { +#if (_WIN32_WINNT < 0x0600) && defined(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. @@ -460,6 +466,7 @@ Tcl_WaitForEvent( } timeout = myTime.sec * 1000 + myTime.usec / 1000; +#endif } else { timeout = INFINITE; } @@ -548,6 +555,46 @@ void Tcl_Sleep( int ms) /* Number of milliseconds to sleep. */ { +#ifdef WIN32_USE_TICKCOUNT +#if (_WIN32_WINNT >= 0x0600) + ULONGLONG now; /* Current wall clock time. */ + ULONGLONG desired; /* Desired wakeup time. */ + ULONGLONG sleepTime; + + now = GetTickCount64(); +#else + DWORD now; /* Current wall clock time. */ + DWORD desired; /* Desired wakeup time. */ + DWORD sleepTime; + + now = GetTickCount(); +#endif + + if (ms < 0) { + ms = 0; +#if (_WIN32_WINNT < 0x0600) + } else if (ms > 0x7FFFFFFF) { + ms = 0x7FFFFFFF; +#endif + } + desired = now + ms; + sleepTime = ms; + for (;;) { + SleepEx(sleepTime, TRUE); +#if (_WIN32_WINNT >= 0x0600) + now = GetTickCount64(); + if (now - desired >= 0) { + break; + } +#else + now = GetTickCount(); + if ((long) now - (long) desired >= 0) { + break; + } +#endif + sleepTime = desired - now; + } +#else /* * Simply calling 'Sleep' for the requisite number of milliseconds can * make the process appear to wake up early because it isn't synchronized @@ -597,6 +644,7 @@ Tcl_Sleep( tclScaleTimeProcPtr(&vdelay, tclTimeClientData); sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000; } +#endif } /* diff --git a/win/tclWinTime.c b/win/tclWinTime.c index 7045c72..74d1770 100644..100755 --- a/win/tclWinTime.c +++ b/win/tclWinTime.c @@ -113,7 +113,7 @@ static TimeInfo timeInfo = { * Declarations for functions defined later in this file. */ -static struct tm * ComputeGMT(const time_t *tp); +#ifndef WIN32_USE_TICKCOUNT static void StopCalibration(ClientData clientData); static DWORD WINAPI CalibrationThread(LPVOID arg); static void UpdateTimeEachSecond(void); @@ -121,6 +121,7 @@ static void ResetCounterSamples(Tcl_WideUInt fileTime, Tcl_WideInt perfCounter, Tcl_WideInt perfFreq); static Tcl_WideInt AccumulateSample(Tcl_WideInt perfCounter, Tcl_WideUInt fileTime); +#endif static void NativeScaleTime(Tcl_Time* timebuf, ClientData clientData); static void NativeGetTime(Tcl_Time* timebuf, @@ -294,6 +295,9 @@ NativeGetTime( if (!timeInfo.initialized) { TclpInitLock(); if (!timeInfo.initialized) { +#ifdef WIN32_USE_TICKCOUNT + timeInfo.perfCounterAvailable = 0; +#else timeInfo.perfCounterAvailable = QueryPerformanceFrequency(&timeInfo.nominalFreq); @@ -387,6 +391,7 @@ NativeGetTime( CloseHandle(timeInfo.readyEvent); Tcl_CreateExitHandler(StopCalibration, NULL); } +#endif /* !WIN32_USE_TICKCOUNT */ timeInfo.initialized = TRUE; } TclpInitUnlock(); @@ -452,6 +457,7 @@ NativeGetTime( } } +#ifndef WIN32_USE_TICKCOUNT /* *---------------------------------------------------------------------- * @@ -485,6 +491,7 @@ StopCalibration( CloseHandle(timeInfo.exitEvent); CloseHandle(timeInfo.calibrationThread); } +#endif /* !WIN32_USE_TICKCOUNT */ /* *---------------------------------------------------------------------- @@ -707,6 +714,7 @@ ComputeGMT( return tmPtr; } +#ifndef WIN32_USE_TICKCOUNT /* *---------------------------------------------------------------------- * @@ -1033,6 +1041,7 @@ AccumulateSample( return estFreq; } } +#endif /* !WIN32_USE_TICKCOUNT */ /* *---------------------------------------------------------------------- @@ -1118,9 +1127,11 @@ Tcl_SetTimeProc( Tcl_ScaleTimeProc *scaleProc, ClientData clientData) { +#if !defined(WIN32_USE_TICKCOUNT) || (_WIN32_WINNT >= 0x0600) tclGetTimeProcPtr = getProc; tclScaleTimeProcPtr = scaleProc; tclTimeClientData = clientData; +#endif } /* |