Tcl Source Code

Artifact [fe90054dee]
Login

Artifact fe90054dee32ee1485ce946bd23c1d0ac2e9522a:

Attachment "tip302.patch" to ticket [3328635fff] added by chw 2016-03-25 08:05:54. (unpublished)
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.


diff -ur tcl8.6.5-orig/generic/tclCmdMZ.c tcl8.6.5/generic/tclCmdMZ.c
--- tcl8.6.5-orig/generic/tclCmdMZ.c	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclCmdMZ.c	2016-03-25 07:06:52.000000000 +0100
@@ -4124,11 +4124,24 @@
     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 @@
 
     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 @@
     stop = TclpGetWideClicks();
     totalMicroSec = ((double) TclpWideClicksToNanoseconds(stop - start))/1.0e3;
 #endif
+#endif
+#endif
 
     if (count <= 1) {
 	/*
diff -ur tcl8.6.5-orig/generic/tclInterp.c tcl8.6.5/generic/tclInterp.c
--- tcl8.6.5-orig/generic/tclInterp.c	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclInterp.c	2016-03-25 07:06:02.000000000 +0100
@@ -3416,8 +3416,24 @@
 	    ((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)) {
@@ -3859,6 +3875,12 @@
 {
     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 @@
 	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 -ur tcl8.6.5-orig/generic/tclTimer.c tcl8.6.5/generic/tclTimer.c
--- tcl8.6.5-orig/generic/tclTimer.c	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclTimer.c	2016-03-25 06:34:31.377599772 +0100
@@ -259,7 +259,33 @@
      * 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 @@
 
     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) {
@@ -416,8 +448,46 @@
 	/*
 	 * 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 +499,7 @@
 	    blockTime.sec = 0;
 	    blockTime.usec = 0;
 	}
+#endif
     } else {
 	return;
     }
@@ -467,8 +538,46 @@
 	/*
 	 * 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 +589,7 @@
 	    blockTime.sec = 0;
 	    blockTime.usec = 0;
 	}
+#endif
 
 	/*
 	 * If the first timer has expired, stick an event on the queue.
@@ -526,6 +636,12 @@
     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,18 +680,41 @@
 
     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;
 	if (timerHandlerPtr == NULL) {
 	    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 +984,13 @@
 
     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 +1018,43 @@
 
 	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 +1188,49 @@
     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 +1238,7 @@
 	endTime.sec++;
 	endTime.usec -= 1000000;
     }
+#endif
 
     do {
 	if (Tcl_AsyncReady()) {
@@ -1035,6 +1249,15 @@
 	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 +1306,28 @@
 		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 -ur tcl8.6.5-orig/unix/configure tcl8.6.5/unix/configure
--- tcl8.6.5-orig/unix/configure	2016-02-29 20:13:08.000000000 +0100
+++ tcl8.6.5/unix/configure	2016-03-25 06:51:49.000000000 +0100
@@ -4209,7 +4209,7 @@
 
 	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
 
 
+    { $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 -ur tcl8.6.5-orig/unix/tclConfig.h.in tcl8.6.5/unix/tclConfig.h.in
--- tcl8.6.5-orig/unix/tclConfig.h.in	2016-02-29 20:13:08.000000000 +0100
+++ tcl8.6.5/unix/tclConfig.h.in	2016-03-25 06:40:01.308724179 +0100
@@ -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 -ur tcl8.6.5-orig/unix/tcl.m4 tcl8.6.5/unix/tcl.m4
--- tcl8.6.5-orig/unix/tcl.m4	2016-02-29 20:13:08.000000000 +0100
+++ tcl8.6.5/unix/tcl.m4	2016-03-25 06:36:15.176672761 +0100
@@ -678,7 +678,7 @@
 
 	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 @@
     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 -ur tcl8.6.5-orig/unix/tclUnixChan.c tcl8.6.5/unix/tclUnixChan.c
--- tcl8.6.5-orig/unix/tclUnixChan.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclUnixChan.c	2016-03-25 06:57:44.000000000 +0100
@@ -1763,12 +1763,16 @@
 				 * 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 @@
      */
 
     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 @@
 	 * 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 -ur tcl8.6.5-orig/unix/tclUnixEvent.c tcl8.6.5/unix/tclUnixEvent.c
--- tcl8.6.5-orig/unix/tclUnixEvent.c	2016-02-25 20:31:20.000000000 +0100
+++ tcl8.6.5/unix/tclUnixEvent.c	2016-03-25 06:58:24.000000000 +0100
@@ -42,7 +42,19 @@
      * 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 @@
 	}
 	(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 -ur tcl8.6.5-orig/unix/tclUnixNotfy.c tcl8.6.5/unix/tclUnixNotfy.c
--- tcl8.6.5-orig/unix/tclUnixNotfy.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclUnixNotfy.c	2016-03-25 06:43:09.437918228 +0100
@@ -102,6 +102,9 @@
     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.
@@ -357,7 +360,24 @@
 	    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;
 	}
@@ -997,16 +1017,29 @@
 	    }
 #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, &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 -ur tcl8.6.5-orig/unix/tclUnixPort.h tcl8.6.5/unix/tclUnixPort.h
--- tcl8.6.5-orig/unix/tclUnixPort.h	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclUnixPort.h	2016-03-25 06:43:44.206617623 +0100
@@ -640,6 +640,25 @@
 
 /*
  *---------------------------------------------------------------------------
+ * 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 -ur tcl8.6.5-orig/unix/tclUnixThrd.c tcl8.6.5/unix/tclUnixThrd.c
--- tcl8.6.5-orig/unix/tclUnixThrd.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclUnixThrd.c	2016-03-25 06:46:19.132188295 +0100
@@ -527,6 +527,9 @@
     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) {
 	MASTER_LOCK;
@@ -537,8 +540,26 @@
 	 */
 
 	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);
 	}
@@ -549,6 +570,17 @@
     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;
 
 	/*
@@ -560,6 +592,7 @@
 	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 -ur tcl8.6.5-orig/unix/tclUnixTime.c tcl8.6.5/unix/tclUnixTime.c
--- tcl8.6.5-orig/unix/tclUnixTime.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclUnixTime.c	2016-03-25 06:45:29.889782184 +0100
@@ -105,6 +105,14 @@
 {
     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 @@
     tclGetTimeProcPtr(&time, tclTimeClientData);
     now = time.sec*1000000 + time.usec;
 #endif
+#endif
 
     return now;
 }
@@ -376,9 +385,11 @@
     Tcl_ScaleTimeProc *scaleProc,
     ClientData clientData)
 {
+#ifndef HAVE_CLOCK_GETTIME
     tclGetTimeProcPtr = getProc;
     tclScaleTimeProcPtr = scaleProc;
     tclTimeClientData = clientData;
+#endif
 }
 
 /*
diff -ur tcl8.6.5-orig/win/configure tcl8.6.5/win/configure
--- tcl8.6.5-orig/win/configure	2016-02-29 20:13:09.000000000 +0100
+++ tcl8.6.5/win/configure	2016-03-25 06:55:36.000000000 +0100
@@ -853,6 +853,8 @@
   --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
@@ -4408,6 +4410,24 @@
 _ACEOF
 
 
+
+# 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
+
 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
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, [
diff -ur tcl8.6.5-orig/win/tclWinNotify.c tcl8.6.5/win/tclWinNotify.c
--- tcl8.6.5-orig/win/tclWinNotify.c	2016-02-25 20:31:20.000000000 +0100
+++ tcl8.6.5/win/tclWinNotify.c	2016-03-25 06:48:38.481999574 +0100
@@ -445,6 +445,12 @@
 	 */
 
 	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 @@
 	    }
 
 	    timeout = myTime.sec * 1000 + myTime.usec / 1000;
+#endif
 	} else {
 	    timeout = INFINITE;
 	}
@@ -548,6 +555,46 @@
 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 @@
 	tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
 	sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000;
     }
+#endif
 }
 
 /*
diff -ur tcl8.6.5-orig/win/tclWinTime.c tcl8.6.5/win/tclWinTime.c
--- tcl8.6.5-orig/win/tclWinTime.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/win/tclWinTime.c	2016-03-25 06:50:54.681661871 +0100
@@ -113,7 +113,7 @@
  * 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 @@
 			    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 @@
     if (!timeInfo.initialized) {
 	TclpInitLock();
 	if (!timeInfo.initialized) {
+#ifdef WIN32_USE_TICKCOUNT
+	    timeInfo.perfCounterAvailable = 0;
+#else
 	    timeInfo.perfCounterAvailable =
 		    QueryPerformanceFrequency(&timeInfo.nominalFreq);
 
@@ -387,6 +391,7 @@
 		CloseHandle(timeInfo.readyEvent);
 		Tcl_CreateExitHandler(StopCalibration, NULL);
 	    }
+#endif /* !WIN32_USE_TICKCOUNT */
 	    timeInfo.initialized = TRUE;
 	}
 	TclpInitUnlock();
@@ -452,6 +457,7 @@
     }
 }
 
+#ifndef WIN32_USE_TICKCOUNT
 /*
  *----------------------------------------------------------------------
  *
@@ -485,6 +491,7 @@
     CloseHandle(timeInfo.exitEvent);
     CloseHandle(timeInfo.calibrationThread);
 }
+#endif /* !WIN32_USE_TICKCOUNT */
 
 /*
  *----------------------------------------------------------------------
@@ -707,6 +714,7 @@
     return tmPtr;
 }
 
+#ifndef WIN32_USE_TICKCOUNT
 /*
  *----------------------------------------------------------------------
  *
@@ -1033,6 +1041,7 @@
 	return estFreq;
     }
 }
+#endif /* !WIN32_USE_TICKCOUNT */
 
 /*
  *----------------------------------------------------------------------
@@ -1118,9 +1127,11 @@
     Tcl_ScaleTimeProc *scaleProc,
     ClientData clientData)
 {
+#if !defined(WIN32_USE_TICKCOUNT) || (_WIN32_WINNT >= 0x0600)
     tclGetTimeProcPtr = getProc;
     tclScaleTimeProcPtr = scaleProc;
     tclTimeClientData = clientData;
+#endif
 }
 
 /*