diff options
author | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:32:02 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:32:02 (GMT) |
commit | 39f4dd98cf71565665bae01fadb2e87319dcd6da (patch) | |
tree | c4f60d26ea03dd30a52895d79a2d75e0b55e6b40 /generic/tclTimer.c | |
parent | bb5afa316b0e04d8a04e874a253dabc3babc52de (diff) | |
download | tcl-39f4dd98cf71565665bae01fadb2e87319dcd6da.zip tcl-39f4dd98cf71565665bae01fadb2e87319dcd6da.tar.gz tcl-39f4dd98cf71565665bae01fadb2e87319dcd6da.tar.bz2 |
Introduced monotonic time as ultimate fix for time-jump issue (fixed for windows and unix now, TIP #302 fully implemented now);
Usage of monotonic time instead of adjustment via timeJump/timeJumpEpoch is more precise and effective.
New sub-command "clock monotonic" to provide monotonic time facility for tcl-level.
Diffstat (limited to 'generic/tclTimer.c')
-rw-r--r-- | generic/tclTimer.c | 190 |
1 files changed, 75 insertions, 115 deletions
diff --git a/generic/tclTimer.c b/generic/tclTimer.c index 9c80332..074b61d 100644 --- a/generic/tclTimer.c +++ b/generic/tclTimer.c @@ -60,8 +60,6 @@ typedef struct AfterAssocData { */ typedef struct { - 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).*/ @@ -361,7 +359,7 @@ TimerMakeRelativeTime( ThreadSpecificData *tsdPtr, Tcl_WideInt usec) { - Tcl_WideInt now = TclpGetMicroseconds(); + Tcl_WideInt now = TclpGetUTimeMonotonic(); /* * We should have the ability to ajust end-time of relative events, @@ -374,19 +372,11 @@ TimerMakeRelativeTime( * end-time = base + relative event-time, which corresponds * original end-time. */ - Tcl_WideInt diff; - if ( (diff = TclpGetLastTimeJump(&tsdPtr->knownTimeJumpEpoch)) != 0 - || (diff = (tsdPtr->knownTime - now)) < 0 - ) { /* jump recognized */ - tsdPtr->relTimerBase += diff; /* shift the base of relative events */ - } usec += now - tsdPtr->relTimerBase; } else { /* first event here - initial values (base/epoch) */ tsdPtr->relTimerBase = now; - tsdPtr->knownTimeJumpEpoch = TclpGetLastTimeJumpEpoch(); } - tsdPtr->knownTime = now; return usec; } @@ -850,8 +840,8 @@ TclpProlongTimerEvent( * * TimerGetDueTime -- * - * Find the execution time of first relative or absolute timer starting - * from given heads. + * Find the execution time offset of first relative or absolute timer + * starting from given heads. * * Results: * A wide integer representing the due time (as microseconds) of first @@ -866,33 +856,36 @@ TclpProlongTimerEvent( static Tcl_WideInt TimerGetDueTime( ThreadSpecificData *tsdPtr, - TclTimerEvent *relEvent, - Tcl_WideInt now) + TclTimerEvent *relTimerList, + TclTimerEvent *absTimerList, + TclTimerEvent **dueEventPtr) { - /* - * Consider time-jump (especially back) - if the time jumped forwards (and - * it recognized) the base can be shifted, but not badly needed, because - * event will be nevertheless executed early as specified. But for backwards - * jumps it is very important and we should adjust relative base to avoid - * too long waiting for relative events. - */ - Tcl_WideInt diff; - if ( (diff = TclpGetLastTimeJump(&tsdPtr->knownTimeJumpEpoch)) != 0 - || (diff = (now - tsdPtr->knownTime)) < 0 /* switched back */ - ) { - /* - * 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 += diff; /* shift the base */ + TclTimerEvent *tmrEvent; + Tcl_WideInt timeOffs = 0x7FFFFFFFFFFFFFFFL; + + /* find shortest due-time */ + if ((tmrEvent = relTimerList) != NULL) { + /* offset to now (monotonic base) */ + timeOffs = tsdPtr->relTimerBase + tmrEvent->time + - TclpGetUTimeMonotonic(); + } + if (absTimerList) { + Tcl_WideInt absOffs; + /* offset to now (real-time base) */ + absOffs = absTimerList->time - TclpGetMicroseconds(); + if (!tmrEvent || absOffs < timeOffs) { + tmrEvent = absTimerList; + timeOffs = absOffs; + } } - tsdPtr->knownTime = now; - /* end-time = base + relative event-time */ - return tsdPtr->relTimerBase + relEvent->time; + if (dueEventPtr) { + *dueEventPtr = tmrEvent; + } + return timeOffs; } - + + /* *---------------------------------------------------------------------- * @@ -938,30 +931,20 @@ TimerSetupProc( /* * Compute the timeout for the next timer on the list. */ + Tcl_WideInt timeOffs; - Tcl_WideInt now = TclpGetMicroseconds(); - Tcl_WideInt entryTime = 0x7FFFFFFFFFFFFFFFL; - - if (tsdPtr->relTimerList) { - entryTime = TimerGetDueTime(tsdPtr, - tsdPtr->relTimerList, now); - } - /* if absolute timer before */ - if (tsdPtr->absTimerList && tsdPtr->absTimerList->time < entryTime) { - entryTime = tsdPtr->absTimerList->time; - } - /* as offset to now */ - entryTime -= now; + timeOffs = TimerGetDueTime(tsdPtr, + tsdPtr->relTimerList, tsdPtr->absTimerList, NULL); #ifdef TMR_RES_TOLERANCE /* consider timer resolution tolerance (avoid busy wait) */ - entryTime -= ((entryTime <= 1000000) ? entryTime : 1000000) * + timeOffs -= ((timeOffs <= 1000000) ? timeOffs : 1000000) * TMR_RES_TOLERANCE / 100; #endif - if (entryTime > 0) { - blockTime.sec = (long) (entryTime / 1000000); - blockTime.usec = (unsigned long) (entryTime % 1000000); + if (timeOffs > 0) { + blockTime.sec = (long) (timeOffs / 1000000); + blockTime.usec = (unsigned long) (timeOffs % 1000000); } else { blockTime.sec = 0; @@ -997,7 +980,7 @@ TimerCheckProc( ClientData data, /* Specific data. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { - Tcl_WideInt now, entryTime = 0; + Tcl_WideInt timeOffs = 0; ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data; if (!(flags & TCL_TIMER_EVENTS)) { @@ -1018,29 +1001,20 @@ TimerCheckProc( if (!tsdPtr->relTimerList && !tsdPtr->absTimerList) { return; } - entryTime = 0x7FFFFFFFFFFFFFFFL; - now = TclpGetMicroseconds(); - if (tsdPtr->relTimerList) { - entryTime = TimerGetDueTime(tsdPtr, - tsdPtr->relTimerList, now); - } - /* if absolute timer before */ - if (tsdPtr->absTimerList && tsdPtr->absTimerList->time < entryTime) { - entryTime = tsdPtr->absTimerList->time; - } - /* as offset to now */ - entryTime -= now; - + + timeOffs = TimerGetDueTime(tsdPtr, + tsdPtr->relTimerList, tsdPtr->absTimerList, NULL); + #ifdef TMR_RES_TOLERANCE /* consider timer resolution tolerance (avoid busy wait) */ - entryTime -= ((entryTime <= 1000000) ? entryTime : 1000000) * + timeOffs -= ((timeOffs <= 1000000) ? timeOffs : 1000000) * TMR_RES_TOLERANCE / 100; #endif /* * If the first timer has expired, stick an event on the queue. */ - if (entryTime <= 0) { + if (timeOffs <= 0) { mark: TclSetTimerEventMarker(flags); /* force timer execution */ tsdPtr->timerPending = 1; @@ -1147,25 +1121,18 @@ TclServiceTimerEvents(void) relTimerList = tsdPtr->relTimerList; absTimerList = tsdPtr->absTimerList; while (relTimerList || absTimerList) { - Tcl_WideInt entryTime = 0x7FFFFFFFFFFFFFFFL; - Tcl_WideInt now = TclpGetMicroseconds(); - /* find shortest due-time */ - if ((tmrEvent = relTimerList) != NULL) { - entryTime = TimerGetDueTime(tsdPtr, tmrEvent, now); - } - if (absTimerList && (!tmrEvent || absTimerList->time < entryTime)) { - tmrEvent = absTimerList; - entryTime = absTimerList->time; - } - /* offset to now */ - entryTime -= now; + Tcl_WideInt timeOffs; + + /* find timer (absolute/relative) with shortest due-time */ + timeOffs = TimerGetDueTime(tsdPtr, + relTimerList, absTimerList, &tmrEvent); /* the same tolerance logic as in TimerSetupProc/TimerCheckProc */ #ifdef TMR_RES_TOLERANCE - entryTime -= ((entryTime <= 1000000) ? entryTime : 1000000) * + timeOffs -= ((timeOffs <= 1000000) ? timeOffs : 1000000) * TMR_RES_TOLERANCE / 100; #endif /* still not reached */ - if (entryTime > 0) { + if (timeOffs > 0) { break; } @@ -1788,9 +1755,8 @@ AfterDelay( { Interp *iPtr = (Interp *) interp; - Tcl_WideInt endTime, now, lastNow, diff; + Tcl_WideInt endTime, now, diff, limOffs = 0x7FFFFFFFFFFFFFFFL; long tolerance = 0; - size_t timeJumpEpoch = 0; if (usec <= 0) { /* to cause a context switch only */ @@ -1803,61 +1769,55 @@ AfterDelay( tolerance = ((usec < 1000000) ? usec : 1000000) * TMR_RES_TOLERANCE / 100; #endif - lastNow = now = TclpGetMicroseconds(); - endTime = usec; - timeJumpEpoch = TclpGetLastTimeJumpEpoch(); - if (!absolute) { - endTime += now; - if (endTime < now) { /* overflow */ + /* + * Note the time can be switched (time-jump), so use monotonic time here. + */ + now = TclpGetUTimeMonotonic(); + if ((endTime = (now + usec)) < now) { /* overflow */ endTime = 0x7FFFFFFFFFFFFFFFL; } + } else { + now = TclpGetMicroseconds(); + endTime = usec; } - do { if ( iPtr->limit.timeEvent != NULL - && now > TCL_TIME_TO_USEC(iPtr->limit.time) + && (limOffs = (TCL_TIME_TO_USEC(iPtr->limit.time) + - TclpGetMicroseconds())) <= 0 ) { iPtr->limit.granularityTicker = 0; if (Tcl_LimitCheck(interp) != TCL_OK) { return TCL_ERROR; } } - if ( iPtr->limit.timeEvent == NULL - || endTime < (diff = TCL_TIME_TO_USEC(iPtr->limit.time)) - ) { - diff = endTime - now; + diff = endTime - now; + if (iPtr->limit.timeEvent == NULL || diff < limOffs) { if (diff > 0) { TclpUSleep(diff); - now = TclpGetMicroseconds(); + if (!absolute) { + now = TclpGetUTimeMonotonic(); + } else { + now = TclpGetMicroseconds(); + } } } else { - diff -= now; + diff = limOffs; if (diff > 0) { TclpUSleep(diff); - now = TclpGetMicroseconds(); + if (!absolute) { + now = TclpGetUTimeMonotonic(); + } else { + now = TclpGetMicroseconds(); + } } if (Tcl_LimitCheck(interp) != TCL_OK) { return TCL_ERROR; } } - /* - * Note time can be switched backwards, certainly adjust end-time - * by possible time-jumps (for relative sleep). - */ - 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 < endTime); + } while (now < endTime - tolerance); return TCL_OK; } |