summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-07-03 13:29:50 (GMT)
committersebres <sebres@users.sourceforge.net>2017-07-03 13:29:50 (GMT)
commit345ea34a7e160b62787cd3397d80dce03a4d1ee6 (patch)
tree5072425b2285daf27f7eca35ea92239e7ae3b4ea
parent62e00681cf398709d6a32eaa1ae0ccae3a5da9ef (diff)
downloadtcl-345ea34a7e160b62787cd3397d80dce03a4d1ee6.zip
tcl-345ea34a7e160b62787cd3397d80dce03a4d1ee6.tar.gz
tcl-345ea34a7e160b62787cd3397d80dce03a4d1ee6.tar.bz2
interim commit: trying to resolve time-freezes with new facilities timeJump/timeJumpEpoch
-rw-r--r--generic/tclEvent.c67
-rw-r--r--generic/tclInt.h11
-rw-r--r--generic/tclInterp.c11
-rw-r--r--generic/tclTimer.c239
-rw-r--r--unix/tclUnixTime.c28
-rw-r--r--win/tclWinNotify.c212
-rw-r--r--win/tclWinTime.c64
7 files changed, 362 insertions, 270 deletions
diff --git a/generic/tclEvent.c b/generic/tclEvent.c
index 53668d0..9a20cc2 100644
--- a/generic/tclEvent.c
+++ b/generic/tclEvent.c
@@ -1384,9 +1384,10 @@ Tcl_VwaitObjCmd(
int flags = TCL_ALL_EVENTS; /* default flags */
char *nameString;
int optc = objc - 2; /* options count without cmd and varname */
- double ms = -1;
- Tcl_Time lastNow, wakeup;
+ Tcl_WideInt usec = -1;
+ Tcl_WideInt lastNow = 0, wakeup = 0;
long tolerance = 0;
+ size_t timeJumpEpoch = 0;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "?options? ?timeout? name");
@@ -1399,7 +1400,8 @@ Tcl_VwaitObjCmd(
* we assume that option is not an integer, try to get numeric timeout
*/
if (!TclObjIsIndexOfTable(objv[optc], updateEventOptions)
- && Tcl_GetDoubleFromObj(NULL, objv[optc], &ms) == TCL_OK) {
+ && TclpGetUTimeFromObj(NULL, objv[optc], &usec) == TCL_OK) {
+ if (usec < 0) { usec = 0; };
optc--;
}
@@ -1414,16 +1416,17 @@ Tcl_VwaitObjCmd(
done = 0;
/* if timeout specified - create timer event or no-wait by 0ms */
- if (ms != -1) {
- if (ms > 0) {
- Tcl_GetTime(&lastNow);
- wakeup = lastNow;
- TclTimeAddMilliseconds(&wakeup, ms);
+ if (usec != -1) {
+ if (usec > 0) {
+ lastNow = TclpGetMicroseconds();
+ timeJumpEpoch = TclpGetLastTimeJumpEpoch();
#ifdef TMR_RES_TOLERANCE
- tolerance = (ms < 1000 ? ms : 1000) *
- (1000 * TMR_RES_TOLERANCE / 100);
+ tolerance = (usec < 1000000 ? usec : 1000000) *
+ TMR_RES_TOLERANCE / 100;
+ usec += tolerance / 3;
#endif
- } else if (ms == 0) {
+ wakeup = lastNow + usec;
+ } else {
flags |= TCL_DONT_WAIT;
}
}
@@ -1437,43 +1440,37 @@ Tcl_VwaitObjCmd(
do {
/* if wait - set blocking time */
- if (ms > 0) {
+ if (usec > 0) {
Tcl_Time blockTime;
- Tcl_GetTime(&blockTime);
+ Tcl_WideInt diff, now = TclpGetMicroseconds();
/*
* Note time can be switched backwards, certainly adjust end-time
* by possible time-jumps back.
*/
- if (TCL_TIME_BEFORE(blockTime, lastNow)) {
- /* backwards time-jump - simply shift wakeup-time */
- wakeup.sec -= (lastNow.sec - blockTime.sec);
- wakeup.usec -= (lastNow.usec - blockTime.usec);
- if (wakeup.usec < 0) {
- wakeup.usec += 1000000;
- wakeup.sec--;
- }
+
+ if ( (diff = TclpGetLastTimeJump(&timeJumpEpoch)) != 0
+ || (diff = (now - lastNow)) < 0
+ ) {
+ /* recognized time-jump - simply shift wakeup-time */
+ wakeup += diff;
}
/* calculate blocking time */
- lastNow = blockTime;
- blockTime.sec = wakeup.sec - blockTime.sec;
- blockTime.usec = wakeup.usec - blockTime.usec;
- if (blockTime.usec < 0) {
- blockTime.usec += 1000000;
- blockTime.sec--;
- }
+ lastNow = now;
+ diff = wakeup - now;
+ diff -= 1; /* overhead for Tcl_TraceVar / Tcl_UntraceVar */
/* be sure process at least one event */
- if ( blockTime.sec < 0
- || (blockTime.sec == 0 && blockTime.usec <= tolerance)
- ) {
+ if (diff <= tolerance) {
/* timeout occurs */
if (checktime) {
done = -1;
break;
}
/* expired, be sure non-negative values here */
- blockTime.usec = blockTime.sec = 0;
+ diff = 0;
checktime = 1;
}
+ blockTime.sec = diff / 1000000;
+ blockTime.usec = diff % 1000000;
Tcl_SetMaxBlockTime(&blockTime);
}
if ((foundEvent = Tcl_DoOneEvent(flags)) <= 0) {
@@ -1485,9 +1482,9 @@ Tcl_VwaitObjCmd(
if (flags & TCL_DONT_WAIT) {
foundEvent = 1;
done = -2;
- } else if (ms > 0 && foundEvent == 0) {
+ } else if (usec > 0 && foundEvent == 0) {
foundEvent = 1;
- }
+ }
/* don't stop wait - no event expected here
* (stop only on error case foundEvent < 0). */
if (foundEvent < 0) {
@@ -1507,7 +1504,7 @@ Tcl_VwaitObjCmd(
VwaitVarProc, (ClientData) &done);
/* if timeout specified (and no errors) */
- if (ms != -1 && foundEvent > 0) {
+ if (usec != -1 && foundEvent > 0) {
Tcl_Obj *objPtr;
/* done - true, timeout false */
diff --git a/generic/tclInt.h b/generic/tclInt.h
index d270042..fcb4752 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2886,19 +2886,28 @@ MODULE_SCOPE double TclpWideClickInMicrosec(void);
# endif
# endif
#endif
+MODULE_SCOPE Tcl_WideInt TclpGetLastTimeJump(size_t *epoch);
+MODULE_SCOPE size_t TclpGetLastTimeJumpEpoch(void);
MODULE_SCOPE Tcl_WideInt TclpGetMicroseconds(void);
+MODULE_SCOPE int TclpGetUTimeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
+ Tcl_WideInt *timePtr);
+MODULE_SCOPE Tcl_WideInt TclpScaleUTime(Tcl_WideInt usec);
+
+MODULE_SCOPE void TclpUSleep(Tcl_WideInt usec);
/*
* Helper macros for working with times. TCL_TIME_BEFORE encodes how to write
* the ordering relation on (normalized) times, and TCL_TIME_DIFF_MS resp.
* TCL_TIME_DIFF_US compute the number of milliseconds or microseconds difference
* between two times. Both macros use both of their arguments multiple times,
* so make sure they are cheap and side-effect free.
+ * Macro TCL_TIME_TO_USEC converts Tcl_Time to microseconds.
* The "prototypes" for these macros are:
*
* static int TCL_TIME_BEFORE(Tcl_Time t1, Tcl_Time t2);
* static Tcl_WideInt TCL_TIME_DIFF_MS(Tcl_Time t1, Tcl_Time t2);
* static Tcl_WideInt TCL_TIME_DIFF_US(Tcl_Time t1, Tcl_Time t2);
+ * static Tcl_WideInt TCL_TIME_TO_USEC(Tcl_Time t)
*/
#define TCL_TIME_BEFORE(t1, t2) \
@@ -2910,6 +2919,8 @@ MODULE_SCOPE Tcl_WideInt TclpGetMicroseconds(void);
#define TCL_TIME_DIFF_US(t1, t2) \
(1000000*((Tcl_WideInt)(t1).sec - (Tcl_WideInt)(t2).sec) + \
((long)(t1).usec - (long)(t2).usec))
+#define TCL_TIME_TO_USEC(t) \
+ ((Tcl_WideInt)(t).sec * 1000000 + (t).usec)
static inline void
TclTimeSetMilliseconds(
diff --git a/generic/tclInterp.c b/generic/tclInterp.c
index b461f40..03a9fe2 100644
--- a/generic/tclInterp.c
+++ b/generic/tclInterp.c
@@ -3718,19 +3718,14 @@ Tcl_LimitSetTime(
Tcl_Time *timeLimitPtr)
{
Interp *iPtr = (Interp *) interp;
- Tcl_Time nextMoment;
+ Tcl_WideInt nextMoment;
memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time));
if (iPtr->limit.timeEvent != NULL) {
TclDeleteTimerEntry(iPtr->limit.timeEvent);
}
- nextMoment.sec = timeLimitPtr->sec;
- nextMoment.usec = timeLimitPtr->usec+10;
- if (nextMoment.usec >= 1000000) {
- nextMoment.sec++;
- nextMoment.usec -= 1000000;
- }
- iPtr->limit.timeEvent = TclCreateTimerHandlerEx(&nextMoment,
+ nextMoment = TCL_TIME_TO_USEC(*timeLimitPtr) + 10;
+ iPtr->limit.timeEvent = TclpCreateTimerHandlerEx(nextMoment,
TimeLimitCallback, TimeLimitDeleteCallback, 0, TCL_ABSTMR_EVENT);
iPtr->limit.timeEvent->clientData = interp;
iPtr->limit.exceeded &= ~TCL_LIMIT_TIME;
diff --git a/generic/tclTimer.c b/generic/tclTimer.c
index 9eaf944..aad8ee3 100644
--- a/generic/tclTimer.c
+++ b/generic/tclTimer.c
@@ -60,8 +60,10 @@ typedef struct AfterAssocData {
*/
typedef struct {
- Tcl_WideInt relTimerBase; /* Time base (offset) of the last known relative,
- * timer, used to revert all events to the new
+ Tcl_WideInt knownTime; /* Last know time */
+ size_t knownTimeJumpEpoch; /* Epoch of the last time-jump */
+ Tcl_WideInt relTimerBase; /* Time base of the first known relative */
+ /* timer, used to revert all events to the new
* base after possible time-jump (adjustment).*/
TimerEntry *relTimerList; /* First event in queue of relative timers. */
TimerEntry *relTimerTail; /* Last event in queue of relative timers. */
@@ -102,7 +104,8 @@ static Tcl_ThreadDataKey dataKey;
static void AfterCleanupProc(ClientData clientData,
Tcl_Interp *interp);
-static int AfterDelay(Tcl_Interp *interp, double ms);
+static int AfterDelay(Tcl_Interp *interp, Tcl_WideInt usec,
+ int absolute);
static void AfterProc(ClientData clientData);
static void FreeAfterPtr(ClientData clientData);
static AfterInfo * GetAfterEvent(AfterAssocData *assocPtr, Tcl_Obj *objPtr);
@@ -366,7 +369,8 @@ TclpCreateTimerHandlerEx(
int flags) /* If TCL_ABSTMR_EVENT, time is absolute */
{
register TimerEntry *entryPtr, *entryPtrPos;
- register TimerHandler *timerPtr, **tmrList, **tmrTail;
+ TimerEntry **tmrList, **tmrTail;
+ register TimerHandler *timerPtr;
ThreadSpecificData *tsdPtr;
tsdPtr = InitTimer();
@@ -391,18 +395,32 @@ TclpCreateTimerHandlerEx(
if (flags & TCL_ABSTMR_EVENT) {
tmrList = &tsdPtr->absTimerList;
- tmrTail = &tsdPte->absTimerTail;
+ tmrTail = &tsdPtr->absTimerTail;
} else {
Tcl_WideInt now = TclpGetMicroseconds();
- tmrList = &tsdPtr->relTimerList;
- tmrTail = &tsdPtr->relTimerTail;
- if (tsdPtr->relTimerList) {
- /* usec is relative current base (to now) */
- usec -= now - tsdPtr->relTimerBase;
- } else {
- tsdPtr->relTimerBase = now;
- }
+ tmrList = &tsdPtr->relTimerList;
+ tmrTail = &tsdPtr->relTimerTail;
+ /*
+ * We should have the ability to ajust end-time of relative events,
+ * for possible time-jumps.
+ */
+ if (tsdPtr->relTimerList) {
+ /*
+ * end-time = now + usec
+ * Adjust value of usec relative current base (to now), so
+ * end-time = base + relative event-time, which corresponds
+ * original end-time.
+ */
+ Tcl_WideInt diff = TclpGetLastTimeJump(&tsdPtr->knownTimeJumpEpoch);
+ if (diff != 0) { /* jump recognized */
+ tsdPtr->relTimerBase += diff; /* shift the base of relative events*/
+ }
+ usec += now - tsdPtr->relTimerBase;
+ } else {
+ tsdPtr->knownTime = tsdPtr->relTimerBase = now;
+ tsdPtr->knownTimeJumpEpoch = TclpGetLastTimeJumpEpoch();
+ }
}
timerPtr->time = usec;
@@ -626,7 +644,7 @@ TclDeleteTimerEntry(
tsdPtr->timerListEpoch++; /* signal-timer list was changed */
if (entryPtr->flags & TCL_ABSTMR_EVENT) {
TclSpliceOutEx(entryPtr, tsdPtr->absTimerList, tsdPtr->absTimerTail);
- } else e
+ } else {
TclSpliceOutEx(entryPtr, tsdPtr->relTimerList, tsdPtr->relTimerTail);
}
}
@@ -645,37 +663,51 @@ TclDeleteTimerEntry(
}
static Tcl_WideInt
-TimerGetFirstTimeOffs(
+TimerGetFirstTime(
ThreadSpecificData *tsdPtr,
+ TimerEntry *relTimerList,
+ TimerEntry *absTimerList,
+ Tcl_WideInt now,
TimerEntry **entryPtr)
{
Tcl_WideInt firstTime = -0x7FFFFFFFFFFFFFFFL;
- Tcl_WideInt now = TclpGetMicroseconds();
- /* consider time-jump back */
- if (tsdPtr->relTimerList) {
- if (now < tsdPtr->relTimerBase) { /* switched back */
+ /*
+ * Consider time-jump back - if time jumped forwards, nothing to be done,
+ * because event will be executed early as specified. But for backwards
+ * jumps we should adjust relative base to avoid too long waiting for
+ * relative events.
+ */
+ if (relTimerList) {
+ Tcl_WideInt diff;
+ if ( (diff = TclpGetLastTimeJump(&tsdPtr->knownTimeJumpEpoch)) != 0
+ || (diff = (now - tsdPtr->knownTime)) < 0 /* switched back */
+ ) {
/*
- * Because the real jump is unknown (resp. too complex to retrieve
+ * If the real jump is unknown (resp. too complex to retrieve
* accross all threads), we simply accept possible small increment
* of the real wait-time.
*/
- tsdPtr->relTimerBase = now; /* just shift the base back */
+ tsdPtr->relTimerBase += diff; /* shift the base */
}
+ tsdPtr->knownTime = now;
+ /* end-time = base + relative event-time */
firstTime = tsdPtr->relTimerBase
- + TimerEntry2TimerHandler(tsdPtr->absTimerList)->time;
- if (entryPtr) { *entryPtr = tsdPtr->relTimerBase; }
+ + TimerEntry2TimerHandler(relTimerList)->time;
+ if (entryPtr) { *entryPtr = relTimerList; }
}
- if ( tsdPtr->absTimerList
- && firstTime < TimerEntry2TimerHandler(tsdPtr->absTimerList)->time
+ if ( absTimerList
+ && firstTime < TimerEntry2TimerHandler(absTimerList)->time
) {
- firstTime = TimerEntry2TimerHandler(tsdPtr->absTimerList)->time;
- if (entryPtr) { *entryPtr = tsdPtr->absTimerList; }
+ /* end-time = absolute event-time */
+ firstTime = TimerEntry2TimerHandler(absTimerList)->time;
+ if (entryPtr) { *entryPtr = absTimerList; }
}
- return firstTime - now;
+ return firstTime;
}
+
/*
*----------------------------------------------------------------------
*
@@ -723,7 +755,9 @@ TimerSetupProc(
* Compute the timeout for the next timer on the list.
*/
- Tcl_WideInt timeOffs = TimerGetFirstTimeOffs(tsdPtr, NULL);
+ Tcl_WideInt now = TclpGetMicroseconds();
+ Tcl_WideInt timeOffs = TimerGetFirstTime(tsdPtr,
+ tsdPtr->relTimerList, tsdPtr->absTimerList, now, NULL) - now;
if (timeOffs > 0) {
blockTime.sec = (long) (timeOffs / 1000000);
@@ -776,7 +810,7 @@ TimerCheckProc(
ClientData data, /* Specific data. */
int flags) /* Event flags as passed to Tcl_DoOneEvent. */
{
- Tcl_WideInt timeOffs;
+ Tcl_WideInt now, timeOffs;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data;
long tolerance = 0;
@@ -794,8 +828,10 @@ TimerCheckProc(
/*
* Verify the first timer on the queue.
*/
- timeOffs = TimerGetFirstTimeOffs(tsdPtr, NULL);
-
+ now = TclpGetMicroseconds();
+ timeOffs = TimerGetFirstTime(tsdPtr,
+ tsdPtr->relTimerList, tsdPtr->absTimerList, now, NULL) - now;
+
#ifdef TMR_RES_TOLERANCE
/* consider timer resolution tolerance (avoid busy wait) */
tolerance = ((timeOffs <= 1000000) ? timeOffs : 1000000) *
@@ -836,8 +872,8 @@ TimerCheckProc(
int
TclServiceTimerEvents(void)
{
- TimerEntry *entryPtr, *nextPtr;
- Tcl_Time time, entrytm;
+ TimerEntry *entryPtr, *relTimerList, *absTimerList;
+ Tcl_WideInt now, entryTime;
size_t currentGeneration, currentEpoch;
int prevTmrPending;
ThreadSpecificData *tsdPtr = InitTimer();
@@ -900,30 +936,28 @@ TclServiceTimerEvents(void)
return -1;
}
- /* Hereafter all timer events with time before now */
- if (!tsdPtr->absTimerList) {
- goto done;
- }
- Tcl_GetTime(&time);
- for (entryPtr = tsdPtr->absTimerList;
- entryPtr != NULLe
- entryPtr = nextPtr
- ) {
- nextPtr = entryPtr->nextPtr;
+ /* Hereafter all relative and absolute timer events with time before now */
+ relTimerList = tsdPtr->relTimerList;
+ absTimerList = tsdPtr->absTimerList;
+ while (relTimerList || absTimerList)
+ {
+ now = TclpGetMicroseconds();
+ entryTime = TimerGetFirstTime(tsdPtr, relTimerList, absTimerList, now, &entryPtr);
- entrytm = TimerEntry2TimerHandler(entryPtr)->time;
#ifdef TMR_RES_TOLERANCE
- entrytm.usec -= ((entrytm.sec <= 0) ? entrytm.usec : 1000000) *
- (TMR_RES_TOLERANCE / 100);
- if (entrytm.usec < 0) {
- entrytm.usec += 1000000;
- entrytm.sec--;
- }
+ entryTime -= ((entryTime <= 1000000) ? entryTime : 1000000) *
+ TMR_RES_TOLERANCE / 100;
#endif
- if (TCL_TIME_BEFORE(time, entrytm)) {
+ if (now < entryTime) {
break;
}
+ /* Current list head to next entry */
+ if (entryPtr == relTimerList) {
+ relTimerList = relTimerList->nextPtr;
+ } else {
+ absTimerList = absTimerList->nextPtr;
+ }
/*
* Bypass timers of newer generation.
*/
@@ -935,16 +969,19 @@ TclServiceTimerEvents(void)
}
tsdPtr->timerListEpoch++; /* signal-timer list was changed */
+ currentEpoch = tsdPtr->timerListEpoch;
/*
* Remove the handler from the queue before invoking it, to avoid
* potential reentrancy problems.
*/
-
- TclSpliceOutEx(entryPtr,
- tsdPtr->absTimerLise, tsdPtr->absTimerTail);
-
- currentEpoch = tsdPtr->timerListEpoch;
+ if (!(entryPtr->flags & TCL_ABSTMR_EVENT)) {
+ TclSpliceOutEx(entryPtr,
+ tsdPtr->relTimerList, tsdPtr->relTimerTail);
+ } else {
+ TclSpliceOutEx(entryPtr,
+ tsdPtr->absTimerList, tsdPtr->absTimerTail);
+ }
/* reset current timer pending (correct process nested wait event) */
prevTmrPending = tsdPtr->timerPending;
@@ -969,17 +1006,15 @@ TclServiceTimerEvents(void)
}
}
-done:
/* pending timer events, so mark (queue) timer events */
if (tsdPtr->timerPending > 1) {
tsdPtr->timerPending = 1;
-
return -1;
}
/* Reset generation if both timer queue are empty */
- if (!tsdPtr->absTimerList) {
- tsdPtr->timerGeneratioe = 0;
+ if (!tsdPtr->relTimerList && !tsdPtr->absTimerList) {
+ tsdPtr->timerGeneration = 0;
}
/* Compute the next timeout (later via TimerSetupProc using the first timer). */
@@ -1239,7 +1274,7 @@ TclServiceIdle(void)
int
TclpGetUTimeFromObj(
Tcl_Interp *interp, /* Current interpreter or NULL. */
- Tcl_Obj *CONST objPtr, /* Object to read numeric time (in milliseconds). */
+ Tcl_Obj *objPtr, /* Object to read numeric time (in milliseconds). */
Tcl_WideInt *timePtr) /* Resulting time if converted (in microseconds). */
{
if (objPtr->typePtr != &tclDoubleType) {
@@ -1255,7 +1290,7 @@ TclpGetUTimeFromObj(
}
if (1) {
double ms;
- if (Tcl_GetDoubleFromObj(interp, objv[1], &ms) == TCL_OK) {
+ if (Tcl_GetDoubleFromObj(interp, objPtr, &ms) == TCL_OK) {
if (ms < 0x7FFFFFFFFFFFFFFFL / 1000) { /* avoid overflow */
/* use precise as possible calculation by double (microseconds) */
*timePtr = ((Tcl_WideInt)ms) * 1000 + (((long)(ms*1000)) % 1000);
@@ -1563,16 +1598,16 @@ Tcl_AfterObjCmd(
static int
AfterDelay(
Tcl_Interp *interp,
- double ms,
+ Tcl_WideInt usec,
int absolute)
{
Interp *iPtr = (Interp *) interp;
- Tcl_Time endTime, now, lastNow;
- Tcl_WideInt diff;
+ Tcl_WideInt endTime, now, lastNow, diff;
long tolerance = 0;
+ size_t timeJumpEpoch = 0;
- if (ms <= 0) {
+ if (usec <= 0) {
/* to cause a context switch only */
Tcl_Sleep(0);
return TCL_OK;
@@ -1580,44 +1615,42 @@ AfterDelay(
/* calculate possible maximal tolerance (in usec) of original wait-time */
#ifdef TMR_RES_TOLERANCE
- tolerance = ((ms < 1000) ? ms : 1000) * (1000 * TMR_RES_TOLERANCE / 100);
+ tolerance = ((usec < 1000000) ? usec : 1000000) * TMR_RES_TOLERANCE / 100;
#endif
- Tcl_GetTime(&now);
- lastNow = endTime = now;
- if (absolute)
- TclTimeAddMilliseconds(&endTime, ms);
+ lastNow = now = TclpGetMicroseconds();
+ endTime = usec;
+ timeJumpEpoch = TclpGetLastTimeJumpEpoch();
+
+ if (!absolute) {
+ endTime += now;
+ if (endTime < now) { /* overflow */
+ endTime = 0x7FFFFFFFFFFFFFFFL;
+ }
+ }
do {
- if (iPtr->limit.timeEvent != NULL
- && TCL_TIME_BEFORE(iPtr->limit.time, now)) {
+ if ( iPtr->limit.timeEvent != NULL
+ && now > TCL_TIME_TO_USEC(iPtr->limit.time)
+ ) {
iPtr->limit.granularityTicker = 0;
if (Tcl_LimitCheck(interp) != TCL_OK) {
return TCL_ERROR;
}
}
- if (iPtr->limit.timeEvent == NULL
- || TCL_TIME_BEFORE(endTime, iPtr->limit.time)) {
- diff = TCL_TIME_DIFF_MS(endTime, now);
-#ifndef TCL_WIDE_INT_IS_LONG
- if (diff > LONG_MAX) {
- diff = LONG_MAX;
- }
-#endif
+ if ( iPtr->limit.timeEvent == NULL
+ || endTime < (diff = TCL_TIME_TO_USEC(iPtr->limit.time))
+ ) {
+ diff = endTime - now;
if (diff > 0) {
- Tcl_Sleep((long)diff);
- Tcl_GetTime(&now);
+ TclpUSleep(diff);
+ now = TclpGetMicroseconds();
}
} else {
- diff = TCL_TIME_DIFF_MS(iPtr->limit.time, now);
-#ifndef TCL_WIDE_INT_IS_LONG
- if (diff > LONG_MAX) {
- diff = LONG_MAX;
- }
-#endif
+ diff -= now;
if (diff > 0) {
- Tcl_Sleep((long)diff);
- Tcl_GetTime(&now);
+ TclpUSleep(diff);
+ now = TclpGetMicroseconds();
}
if (Tcl_LimitCheck(interp) != TCL_OK) {
return TCL_ERROR;
@@ -1626,24 +1659,20 @@ AfterDelay(
/*
* Note time can be switched backwards, certainly adjust end-time
- * by possible time-jumps back.
+ * by possible time-jumps (for relative sleep).
*/
- if (!absolute && TCL_TIME_BEFORE(now, lastNow)) {
- /* backwards time-jump - simply shift wakeup-time */
- endTime.sec -= (lastNow.sec - now.sec);
- endTime.usec -= (lastNow.usec - now.usec);
- if (endTime.usec < 0) {
- endTime.usec += 1000000;
- endTime.sec--;
- }
+ if (!absolute
+ && ( (diff = TclpGetLastTimeJump(&timeJumpEpoch)) != 0
+ || (diff = (now - lastNow)) < 0
+ )
+ ) {
+ /* recognized time-jump - simply shift wakeup-time */
+ endTime += diff;
}
lastNow = now;
/* consider timer resolution tolerance (avoid busy wait) */
- } while (
- (now.sec > endTime.sec)
- || (now.sec == endTime.sec && now.usec >= endTime.usec - tolerance)
- );
+ } while (now < endTime);
return TCL_OK;
}
diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c
index 1b4ea15..7f22fab 100644
--- a/unix/tclUnixTime.c
+++ b/unix/tclUnixTime.c
@@ -632,6 +632,34 @@ NativeScaleTime(
/*
*----------------------------------------------------------------------
*
+ * TclpScaleUTime --
+ *
+ * This procedure scales number of microseconds if expected.
+ *
+ * Results:
+ * Number of microseconds scaled using tclScaleTimeProcPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_WideInt
+TclpScaleUTime(
+ Tcl_WideInt usec)
+{
+ /* Native scale is 1:1. */
+ if (tclScaleTimeProcPtr != NativeScaleTime) {
+ return usec;
+ } else {
+ Tcl_Time scTime;
+ scTime.sec = usec / 1000000;
+ scTime.usec = usec % 1000000;
+ tclScaleTimeProcPtr(&scTime, tclTimeClientData);
+ return ((Tcl_WideInt)scTime.sec) * 1000000 + scTime.usec;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* NativeGetTime --
*
* TIP #233: Gets the current system time in seconds and microseconds
diff --git a/win/tclWinNotify.c b/win/tclWinNotify.c
index d22373e..bd3b384 100644
--- a/win/tclWinNotify.c
+++ b/win/tclWinNotify.c
@@ -631,12 +631,13 @@ Tcl_WaitForEvent(
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
MSG msg;
- DWORD timeout, result = WAIT_TIMEOUT;
+ DWORD timeout = INFINITE, result = WAIT_TIMEOUT;
int status = 0;
- Tcl_Time waitTime = {0, 0};
- Tcl_Time lastNow, endTime;
+ Tcl_WideInt waitTime = 0;
+ Tcl_WideInt lastNow = 0, endTime = 0;
long tolerance = 0;
unsigned long actualResolution = 0;
+ size_t timeJumpEpoch = 0;
/*
* Allow the notifier to be hooked. This may not make sense on windows,
@@ -653,47 +654,44 @@ Tcl_WaitForEvent(
if (timePtr) {
- waitTime.sec = timePtr->sec;
- waitTime.usec = timePtr->usec;
+ waitTime = timePtr->sec * 1000000 + timePtr->usec;
/* if no wait */
- if (waitTime.sec <= 0 && waitTime.usec <= 0) {
+ if (waitTime <= 0) {
result = 0;
goto peek;
}
- #ifdef TMR_RES_TOLERANCE
- /* calculate possible maximal tolerance (in usec) of original wait-time */
- tolerance = ((waitTime.sec <= 0) ? waitTime.usec : 1000000) *
- (TMR_RES_TOLERANCE / 100);
- #endif
-
/* calculate end of wait */
- Tcl_GetTime(&endTime);
- lastNow = endTime;
- endTime.sec += waitTime.sec;
- endTime.usec += waitTime.usec;
- if (endTime.usec > 1000000) {
- endTime.usec -= 1000000;
- endTime.sec++;
- }
+ lastNow = TclpGetMicroseconds();
+ endTime = lastNow + waitTime;
+ timeJumpEpoch = TclpGetLastTimeJumpEpoch();
if (timerResolution.available == -1) {
InitTimerResolution();
}
+ #ifdef TMR_RES_TOLERANCE
+ /* calculate possible maximal tolerance (in usec) of original wait-time */
+ tolerance = ((waitTime <= 1000000) ? waitTime : 1000000) *
+ TMR_RES_TOLERANCE / 100;
+ if (tolerance > timerResolution.maxDelay) {
+ tolerance = timerResolution.maxDelay;
+ }
+ #endif
+
repeat:
/*
* TIP #233 (Virtualized Time). Convert virtual domain delay to
* real-time.
*/
- (*tclScaleTimeProcPtr) (&waitTime, tclTimeClientData);
+ waitTime = TclpScaleUTime(waitTime);
/* No wait if timeout too small (because windows may wait too long) */
- if (!waitTime.sec && waitTime.usec < (long)timerResolution.minDelay) {
+ if (waitTime < (long)timerResolution.minDelay) {
+ timeout = 0;
/* prevent busy wait */
- if (waitTime.usec >= 10) {
- timeout = 0;
+ if (waitTime >= 10) {
goto wait;
}
Sleep(0);
@@ -701,25 +699,23 @@ Tcl_WaitForEvent(
}
if (timerResolution.available) {
- if (waitTime.sec || waitTime.usec + tolerance > timerResolution.maxDelay) {
- long usec;
- timeout = waitTime.sec * 1000;
- usec = (timeout * 1000) + waitTime.usec + tolerance;
- if (usec > 1000000) {
- usec -= 1000000;
- timeout += 1000;
- }
- timeout += (usec - (usec % timerResolution.maxDelay)) / 1000;
+ long overhead = (tolerance < 100 ? tolerance/2 : 50);
+ Tcl_WideInt waitTimeWithOverhead = waitTime + overhead;
+ if (waitTimeWithOverhead > timerResolution.maxDelay) {
+ /* floor (truncate) using max delay as base (follow timeout better) */
+ timeout = (waitTimeWithOverhead
+ / timerResolution.maxDelay)
+ * timerResolution.maxDelay / 1000;
} else {
/* calculate resolution up to 1000 microseconds
* (don't use highest, because of too large CPU load) */
ULONG res;
- if (waitTime.usec >= 10000) {
+ if (waitTimeWithOverhead >= 10000) {
res = 10000 * TMR_RES_MICROSEC;
} else {
res = 1000 * TMR_RES_MICROSEC;
}
- timeout = waitTime.usec / 1000;
+ timeout = waitTimeWithOverhead / 1000;
/* set more precise timer resolution for minimal delay */
if (!actualResolution || res < timerResolution.curRes) {
actualResolution = SetTimerResolution(
@@ -727,11 +723,8 @@ Tcl_WaitForEvent(
}
}
} else {
- timeout = waitTime.sec * 1000 + waitTime.usec / 1000;
+ timeout = waitTime / 1000;
}
-
- } else {
- timeout = INFINITE;
}
/*
@@ -796,31 +789,28 @@ Tcl_WaitForEvent(
else
if (result == WAIT_TIMEOUT && timeout != INFINITE) {
/* Check the wait should be repeated, and correct time for wait */
- Tcl_Time now;
+ Tcl_WideInt now;
- Tcl_GetTime(&now);
+ now = TclpGetMicroseconds();
/*
* Note time can be switched backwards, certainly adjust end-time
- * by possible time-jumps back.
+ * by possible time-jumps.
*/
- if (TCL_TIME_BEFORE(now, lastNow)) {
- /* backwards time-jump - simply shift wakeup-time */
- endTime.sec -= (lastNow.sec - now.sec);
- endTime.usec -= (lastNow.usec - now.usec);
- if (endTime.usec < 0) {
- endTime.usec += 1000000;
- endTime.sec--;
- }
+ if ((waitTime = TclpGetLastTimeJump(&timeJumpEpoch)) != 0) {
+ /* we know time-jump, adjust end-time using this offset */
+ endTime += waitTime;
+ }
+ else
+ if ((waitTime = (now - lastNow)) < 0) {
+ /* recognized backwards time-jump - simply shift wakeup-time *
+ * considering timeout also, assume we've reached it completely */
+ endTime += (waitTime - (timeout * 1000));
}
lastNow = now;
/* calculate new waitTime */
- waitTime.sec = (endTime.sec - now.sec);
- if ((waitTime.usec = (endTime.usec - now.usec)) < 0) {
- waitTime.usec += 1000000;
- waitTime.sec--;
- }
- if (waitTime.sec < 0 || !waitTime.sec && waitTime.usec <= tolerance) {
+ waitTime = endTime - now;
+ if (waitTime <= tolerance) {
goto end;
}
/* Repeat wait with more precise timer resolution (or using sleep) */
@@ -841,9 +831,11 @@ Tcl_WaitForEvent(
/*
*----------------------------------------------------------------------
*
- * Tcl_Sleep --
+ * Tcl_Sleep --, TclpUSleep --
*
- * Delay execution for the specified number of milliseconds.
+ * Delay execution for the specified number of milliseconds (or microsec.).
+ *
+ * TclpUSleep in contrast to Tcl_Sleep is more precise (microseconds).
*
* Results:
* None.
@@ -858,6 +850,13 @@ void
Tcl_Sleep(
int ms) /* Number of milliseconds to sleep. */
{
+ TclpUSleep((Tcl_WideInt)ms * 1000);
+}
+
+void
+TclpUSleep(
+ Tcl_WideInt usec) /* Number of microseconds to sleep. */
+{
/*
* Simply calling 'Sleep' for the requisite number of milliseconds can
* make the process appear to wake up early because it isn't synchronized
@@ -868,72 +867,67 @@ Tcl_Sleep(
* requisite amount.
*/
- Tcl_Time lastNow, now; /* Current wall clock time. */
- Tcl_Time desired; /* Desired wakeup time. */
- Tcl_Time vdelay; /* Time to sleep, for scaling virtual ->
- * real. */
+ Tcl_WideInt lastNow, now; /* Current wall clock time. */
+ Tcl_WideInt desired; /* Desired wakeup time. */
DWORD sleepTime; /* Time to sleep, real-time */
long tolerance = 0;
unsigned long actualResolution = 0;
+ size_t timeJumpEpoch = 0;
- if (ms <= 0) {
- /* causes context switch only */
- Sleep(0);
- return;
+ if (usec <= 9) { /* too short to start whole sleep process */
+ do {
+ /* causes context switch only (shortest waiting) */
+ Sleep(0);
+ } while (--usec > 0);
+ return;
}
if (timerResolution.available == -1) {
InitTimerResolution();
}
- vdelay.sec = ms / 1000;
- vdelay.usec = (ms % 1000) * 1000;
-
- Tcl_GetTime(&now);
- lastNow = now;
- desired.sec = now.sec + vdelay.sec;
- desired.usec = now.usec + vdelay.usec;
- if (desired.usec > 1000000) {
- desired.usec -= 1000000;
- desired.sec++;
- }
+ lastNow = now = TclpGetMicroseconds();
+ desired = now + usec;
+ timeJumpEpoch = TclpGetLastTimeJumpEpoch();
#ifdef TMR_RES_TOLERANCE
/* calculate possible maximal tolerance (in usec) of original wait-time */
- tolerance = ((vdelay.sec <= 0) ? vdelay.usec : 1000000) *
- (TMR_RES_TOLERANCE / 100);
+ tolerance = ((usec <= 1000000) ? usec : 1000000) *
+ TMR_RES_TOLERANCE / 100;
+ if (tolerance > timerResolution.maxDelay) {
+ tolerance = timerResolution.maxDelay;
+ }
#endif
- /*
- * TIP #233: Scale delay from virtual to real-time.
- */
-
for (;;) {
- tclScaleTimeProcPtr(&vdelay, tclTimeClientData);
+ /*
+ * TIP #233: Scale delay from virtual to real-time.
+ */
+ usec = TclpScaleUTime(usec);
/* No wait if sleep time too small (because windows may wait too long) */
- if (!vdelay.sec && vdelay.usec < (long)timerResolution.minDelay) {
+ if (usec < (long)timerResolution.minDelay) {
sleepTime = 0;
goto wait;
}
if (timerResolution.available) {
- if (vdelay.sec || vdelay.usec > timerResolution.maxDelay) {
- long usec;
- sleepTime = vdelay.sec * 1000;
- usec = ((sleepTime * 1000) + vdelay.usec) % 1000000;
- sleepTime += (usec - (usec % timerResolution.maxDelay)) / 1000;
+ if (usec > timerResolution.maxDelay) {
+ /* floor (truncate) using max delay as base (follow timeout better) */
+ sleepTime = (usec
+ / timerResolution.maxDelay)
+ * timerResolution.maxDelay / 1000;
} else {
/* calculate resolution up to 1000 microseconds
* (don't use highest, because of too large CPU load) */
ULONG res;
- if (vdelay.usec >= 10000) {
+ if (usec >= 10000) {
res = 10000 * TMR_RES_MICROSEC;
} else {
res = 1000 * TMR_RES_MICROSEC;
}
- sleepTime = vdelay.usec / 1000;
+ sleepTime = usec / 1000;
/* set more precise timer resolution for minimal delay */
if (!actualResolution || res < timerResolution.curRes) {
actualResolution = SetTimerResolution(
@@ -941,37 +935,29 @@ Tcl_Sleep(
}
}
} else {
- sleepTime = vdelay.sec * 1000 + vdelay.usec / 1000;
+ sleepTime = usec / 1000;
}
wait:
Sleep(sleepTime);
- Tcl_GetTime(&now);
+ now = TclpGetMicroseconds();
/*
* Note time can be switched backwards, certainly adjust end-time
- * by possible time-jumps back.
+ * by possible time-jumps.
*/
- if (TCL_TIME_BEFORE(now, lastNow)) {
- /* backwards time-jump - simply shift wakeup-time */
- desired.sec -= (lastNow.sec - now.sec);
- desired.usec -= (lastNow.usec - now.usec);
- if (desired.usec < 0) {
- desired.usec += 1000000;
- desired.sec--;
- }
+ if ((usec = TclpGetLastTimeJump(&timeJumpEpoch)) != 0) {
+ /* we know time-jump, adjust end-time using this offset */
+ desired += usec;
}
- lastNow = now;
-
- vdelay.sec = desired.sec - now.sec;
- vdelay.usec = desired.usec - now.usec;
- if (vdelay.usec < 0) {
- vdelay.usec += 1000000;
- vdelay.sec--;
+ else
+ if ((usec = (now - lastNow)) < 0) {
+ /* recognized backwards time-jump - simply shift wakeup-time *
+ * considering sleep-time also, assume we've reached it completely */
+ desired += (usec - (sleepTime * 1000));
}
+ lastNow = now;
- if (vdelay.sec < 0) {
- break;
- } else if ((vdelay.sec == 0) && (vdelay.usec <= tolerance)) {
+ if ((usec = (desired - now)) <= tolerance) {
break;
}
}
diff --git a/win/tclWinTime.c b/win/tclWinTime.c
index 3a2ba8d..4835bf1 100644
--- a/win/tclWinTime.c
+++ b/win/tclWinTime.c
@@ -45,15 +45,15 @@ typedef struct TimeCalibInfo {
LONGLONG perfCounter; /* QPC value of last calibrated virtual time */
Tcl_WideInt virtTimeBase; /* Last virtual time base (in 100-ns) */
Tcl_WideInt sysTime; /* Last real system time (in 100-ns),
- truncated to VT_SYSTMR_DIST (100ms) */
+ truncated to VT_SYSTMR_DIST (100ms) */
} TimeCalibInfo;
/* Milliseconds <-> 100-ns ticks */
-#define MsToT100ns(ms) (ms * 10000)
-#define T100nsToMs(ms) (ms / 10000)
+#define MsToT100ns(ms) ((ms) * 10000)
+#define T100nsToMs(ms) ((ms) / 10000)
/* Microseconds <-> 100-ns ticks */
-#define UsToT100ns(ms) (ms * 10)
-#define T100nsToUs(ms) (ms / 10)
+#define UsToT100ns(ms) ((ms) * 10)
+#define T100nsToUs(ms) ((ms) / 10)
/*
@@ -103,7 +103,8 @@ typedef struct TimeInfo {
size_t lastUsedTime; /* Last known (caller) offset to virtual time
* (used to avoid back-drifts after calibrate) */
-
+ size_t lastTimeJumpEpoch; /* Last known epoch since last time-jump. */
+ Tcl_WideInt lastTimeJump; /* Last known time-jump of thread. */
} TimeInfo;
static TimeInfo timeInfo = {
@@ -415,7 +416,7 @@ TclpWideClickInMicrosec(void)
Tcl_WideInt
TclpGetMicroseconds(void)
{
-#if 0
+#if 1
/* Use high resolution timer if possible */
if (tclGetTimeProcPtr == NativeGetTime) {
return NativeGetMicroseconds();
@@ -428,7 +429,7 @@ TclpGetMicroseconds(void)
Tcl_Time now;
tclGetTimeProcPtr(&now, tclTimeClientData); /* Tcl_GetTime inlined */
- return (((Tcl_WideInt)now.sec) * 1000000) + now.usec;
+ return TCL_TIME_TO_USEC(now);
}
#else
@@ -563,6 +564,34 @@ NativeScaleTime(
/*
*----------------------------------------------------------------------
*
+ * TclpScaleUTime --
+ *
+ * This procedure scales number of microseconds if expected.
+ *
+ * Results:
+ * Number of microseconds scaled using tclScaleTimeProcPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_WideInt
+TclpScaleUTime(
+ Tcl_WideInt usec)
+{
+ /* Native scale is 1:1. */
+ if (tclScaleTimeProcPtr == NativeScaleTime) {
+ return usec;
+ } else {
+ Tcl_Time scTime;
+ scTime.sec = usec / 1000000;
+ scTime.usec = usec % 1000000;
+ tclScaleTimeProcPtr(&scTime, tclTimeClientData);
+ return ((Tcl_WideInt)scTime.sec) * 1000000 + scTime.usec;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* NativeGetMicroseconds --
*
* Gets the current system time in microseconds since the beginning
@@ -719,7 +748,7 @@ NativeGetMicroseconds(void)
timeInfo.lastCI.sysTime =
timeInfo.lastCI.virtTimeBase = GetSystemTimeAsVirtual();
-
+ timeInfo.lastTimeJumpEpoch = 1; /* let the caller know we've epoch */
}
timeInfo.initialized = TRUE;
}
@@ -826,6 +855,8 @@ NativeGetMicroseconds(void)
* The time-jump (reset or initial), we should use system time
* instead of virtual to recalibrate offsets (let the time jump).
*/
+ timeInfo.lastTimeJump = T100nsToUs(sysTime - vt0); /* 100-ns */;
+ timeInfo.lastTimeJumpEpoch++;
vt0 = sysTime;
//!!! printf("************* reset time: %I64d *****************\n", vt0);
}
@@ -1342,6 +1373,21 @@ Tcl_QueryTimeProc(
}
}
+Tcl_WideInt
+TclpGetLastTimeJump(size_t *epoch)
+{
+ if (timeInfo.lastTimeJumpEpoch > *epoch) {
+ *epoch = timeInfo.lastTimeJumpEpoch;
+ return timeInfo.lastTimeJump;
+ };
+ return 0;
+}
+size_t
+TclpGetLastTimeJumpEpoch(void)
+{
+ return timeInfo.lastTimeJumpEpoch;
+}
+
/*
* Local Variables:
* mode: c