diff options
Diffstat (limited to 'generic/tclTimer.c')
-rw-r--r-- | generic/tclTimer.c | 134 |
1 files changed, 88 insertions, 46 deletions
diff --git a/generic/tclTimer.c b/generic/tclTimer.c index 33838ec..f70d60f 100644 --- a/generic/tclTimer.c +++ b/generic/tclTimer.c @@ -72,7 +72,7 @@ typedef struct AfterAssocData { */ typedef struct IdleHandler { - Tcl_IdleProc (*proc); /* Function to call. */ + Tcl_IdleProc *proc; /* Function to call. */ ClientData clientData; /* Value to pass to proc. */ int generation; /* Used to distinguish older handlers from * recently-created ones. */ @@ -127,6 +127,25 @@ static Tcl_ThreadDataKey dataKey; (1000*((Tcl_WideInt)(t1).sec - (Tcl_WideInt)(t2).sec) + \ ((long)(t1).usec - (long)(t2).usec)/1000) +#define TCL_TIME_DIFF_MS_CEILING(t1, t2) \ + (1000*((Tcl_WideInt)(t1).sec - (Tcl_WideInt)(t2).sec) + \ + ((long)(t1).usec - (long)(t2).usec + 999)/1000) + +/* + * Sleeps under that number of milliseconds don't get double-checked + * and are done in exactly one Tcl_Sleep(). This to limit gettimeofday()s. + */ + +#define SLEEP_OFFLOAD_GETTIMEOFDAY 20 + +/* + * The maximum number of milliseconds for each Tcl_Sleep call in AfterDelay. + * This is used to limit the maximum lag between interp limit and script + * cancellation checks. + */ + +#define TCL_TIME_MAXIMUM_SLICE 500 + /* * Prototypes for functions referenced only in this file: */ @@ -287,7 +306,7 @@ TclCreateAbsoluteTimerHandler( * Fill in fields for the event. */ - memcpy((void *)&timerHandlerPtr->time, (void *)timePtr, sizeof(Tcl_Time)); + memcpy(&timerHandlerPtr->time, timePtr, sizeof(Tcl_Time)); timerHandlerPtr->proc = proc; timerHandlerPtr->clientData = clientData; tsdPtr->lastTimerId++; @@ -396,7 +415,6 @@ TimerSetupProc( blockTime.sec = 0; blockTime.usec = 0; - } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) { /* * Compute the timeout for the next timer on the list. @@ -574,8 +592,8 @@ TimerHandlerEventProc( * potential reentrancy problems. */ - (*nextPtrPtr) = timerHandlerPtr->nextPtr; - (*timerHandlerPtr->proc)(timerHandlerPtr->clientData); + *nextPtrPtr = timerHandlerPtr->nextPtr; + timerHandlerPtr->proc(timerHandlerPtr->clientData); ckfree((char *) timerHandlerPtr); } TimerSetupProc(NULL, TCL_TIMER_EVENTS); @@ -733,7 +751,7 @@ TclServiceIdle(void) if (tsdPtr->idleList == NULL) { tsdPtr->lastIdlePtr = NULL; } - (*idlePtr->proc)(idlePtr->clientData); + idlePtr->proc(idlePtr->clientData); ckfree((char *) idlePtr); } if (tsdPtr->idleList) { @@ -767,23 +785,23 @@ Tcl_AfterObjCmd( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_WideInt ms; /* Number of milliseconds to wait */ + Tcl_WideInt ms = 0; /* Number of milliseconds to wait */ Tcl_Time wakeup; AfterInfo *afterPtr; AfterAssocData *assocPtr; int length; int index; char buf[16 + TCL_INTEGER_SPACE]; - static CONST char *afterSubCmds[] = { + static const char *const afterSubCmds[] = { "cancel", "idle", "info", NULL }; enum afterSubCmds {AFTER_CANCEL, AFTER_IDLE, AFTER_INFO}; ThreadSpecificData *tsdPtr = InitTimer(); if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } @@ -797,8 +815,7 @@ Tcl_AfterObjCmd( assocPtr = (AfterAssocData *) ckalloc(sizeof(AfterAssocData)); assocPtr->interp = interp; assocPtr->firstAfterPtr = NULL; - Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc, - (ClientData) assocPtr); + Tcl_SetAssocData(interp, "tclAfter", AfterCleanupProc, assocPtr); } /* @@ -807,22 +824,21 @@ Tcl_AfterObjCmd( if (objv[1]->typePtr == &tclIntType #ifndef NO_WIDE_TYPE - || objv[1]->typePtr == &tclWideIntType + || objv[1]->typePtr == &tclWideIntType #endif - || objv[1]->typePtr == &tclBignumType - || ( Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "", 0, - &index) != TCL_OK )) { + || objv[1]->typePtr == &tclBignumType + || (Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "", 0, + &index) != TCL_OK)) { index = -1; if (Tcl_GetWideIntFromObj(NULL, objv[1], &ms) != TCL_OK) { Tcl_AppendResult(interp, "bad argument \"", - Tcl_GetString(objv[1]), - "\": must be cancel, idle, info, or an integer", - NULL); + Tcl_GetString(objv[1]), + "\": must be cancel, idle, info, or an integer", NULL); return TCL_ERROR; } } - /* + /* * At this point, either index = -1 and ms contains the number of ms * to wait, or else index is the index of a subcommand. */ @@ -840,7 +856,7 @@ Tcl_AfterObjCmd( if (objc == 3) { afterPtr->commandPtr = objv[2]; } else { - afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2); + afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2); } Tcl_IncrRefCount(afterPtr->commandPtr); @@ -863,8 +879,8 @@ Tcl_AfterObjCmd( wakeup.sec++; wakeup.usec -= 1000000; } - afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup, AfterProc, - (ClientData) afterPtr); + afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup, + AfterProc, afterPtr); afterPtr->nextPtr = assocPtr->firstAfterPtr; assocPtr->firstAfterPtr = afterPtr; Tcl_SetObjResult(interp, Tcl_ObjPrintf("after#%d", afterPtr->id)); @@ -872,7 +888,7 @@ Tcl_AfterObjCmd( } case AFTER_CANCEL: { Tcl_Obj *commandPtr; - char *command, *tempCommand; + const char *command, *tempCommand; int tempLength; if (objc < 3) { @@ -890,8 +906,7 @@ Tcl_AfterObjCmd( tempCommand = Tcl_GetStringFromObj(afterPtr->commandPtr, &tempLength); if ((length == tempLength) - && (memcmp((void*) command, (void*) tempCommand, - (unsigned) length) == 0)) { + && !memcmp(command, tempCommand, (unsigned) length)) { break; } } @@ -905,7 +920,7 @@ Tcl_AfterObjCmd( if (afterPtr->token != NULL) { Tcl_DeleteTimerHandler(afterPtr->token); } else { - Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); + Tcl_CancelIdleCall(AfterProc, afterPtr); } FreeAfterPtr(afterPtr); } @@ -913,7 +928,7 @@ Tcl_AfterObjCmd( } case AFTER_IDLE: if (objc < 3) { - Tcl_WrongNumArgs(interp, 2, objv, "script script ..."); + Tcl_WrongNumArgs(interp, 2, objv, "script ?script ...?"); return TCL_ERROR; } afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo))); @@ -929,7 +944,7 @@ Tcl_AfterObjCmd( afterPtr->token = NULL; afterPtr->nextPtr = assocPtr->firstAfterPtr; assocPtr->firstAfterPtr = afterPtr; - Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr); + Tcl_DoWhenIdle(AfterProc, afterPtr); Tcl_SetObjResult(interp, Tcl_ObjPrintf("after#%d", afterPtr->id)); break; case AFTER_INFO: { @@ -958,7 +973,7 @@ Tcl_AfterObjCmd( resultListPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr); Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj( - (afterPtr->token == NULL) ? "idle" : "timer", -1)); + (afterPtr->token == NULL) ? "idle" : "timer", -1)); Tcl_SetObjResult(interp, resultListPtr); break; } @@ -978,7 +993,7 @@ Tcl_AfterObjCmd( * * Results: * Standard Tcl result code (with error set if an error occurred due to a - * time limit being exceeded). + * time limit being exceeded or being canceled). * * Side effects: * May adjust the time limit granularity marker. @@ -996,7 +1011,8 @@ AfterDelay( Tcl_Time endTime, now; Tcl_WideInt diff; - Tcl_GetTime(&endTime); + Tcl_GetTime(&now); + endTime = now; endTime.sec += (long)(ms/1000); endTime.usec += ((int)(ms%1000))*1000; if (endTime.usec >= 1000000) { @@ -1005,25 +1021,37 @@ AfterDelay( } do { - Tcl_GetTime(&now); + if (Tcl_AsyncReady()) { + if (Tcl_AsyncInvoke(interp, TCL_OK) != TCL_OK) { + return TCL_ERROR; + } + } + if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) { + return TCL_ERROR; + } if (iPtr->limit.timeEvent != NULL - && TCL_TIME_BEFORE(iPtr->limit.time, now)) { + && TCL_TIME_BEFORE(iPtr->limit.time, now)) { 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); + || TCL_TIME_BEFORE(endTime, iPtr->limit.time)) { + diff = TCL_TIME_DIFF_MS_CEILING(endTime, now); #ifndef TCL_WIDE_INT_IS_LONG if (diff > LONG_MAX) { diff = LONG_MAX; } #endif - if (diff > 0) { - Tcl_Sleep((long)diff); + if (diff > TCL_TIME_MAXIMUM_SLICE) { + diff = TCL_TIME_MAXIMUM_SLICE; } + if (diff == 0 && TCL_TIME_BEFORE(now, endTime)) diff = 1; + if (diff > 0) { + Tcl_Sleep((long) diff); + if (diff < SLEEP_OFFLOAD_GETTIMEOFDAY) break; + } else break; } else { diff = TCL_TIME_DIFF_MS(iPtr->limit.time, now); #ifndef TCL_WIDE_INT_IS_LONG @@ -1031,13 +1059,25 @@ AfterDelay( diff = LONG_MAX; } #endif + if (diff > TCL_TIME_MAXIMUM_SLICE) { + diff = TCL_TIME_MAXIMUM_SLICE; + } if (diff > 0) { - Tcl_Sleep((long)diff); + Tcl_Sleep((long) diff); + } + if (Tcl_AsyncReady()) { + if (Tcl_AsyncInvoke(interp, TCL_OK) != TCL_OK) { + return TCL_ERROR; + } + } + if (Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG) == TCL_ERROR) { + return TCL_ERROR; } if (Tcl_LimitCheck(interp) != TCL_OK) { return TCL_ERROR; } } + Tcl_GetTime(&now); } while (TCL_TIME_BEFORE(now, endTime)); return TCL_OK; } @@ -1067,7 +1107,7 @@ GetAfterEvent( * this interpreter. */ Tcl_Obj *commandPtr) { - char *cmdString; /* Textual identifier for after event, such as + const char *cmdString; /* Textual identifier for after event, such as * "after#6". */ AfterInfo *afterPtr; int id; @@ -1114,7 +1154,7 @@ static void AfterProc( ClientData clientData) /* Describes command to execute. */ { - AfterInfo *afterPtr = (AfterInfo *) clientData; + AfterInfo *afterPtr = clientData; AfterAssocData *assocPtr = afterPtr->assocPtr; AfterInfo *prevPtr; int result; @@ -1141,13 +1181,13 @@ AfterProc( */ interp = assocPtr->interp; - Tcl_Preserve((ClientData) interp); + Tcl_Preserve(interp); result = Tcl_EvalObjEx(interp, afterPtr->commandPtr, TCL_EVAL_GLOBAL); if (result != TCL_OK) { Tcl_AddErrorInfo(interp, "\n (\"after\" script)"); - TclBackgroundException(interp, result); + Tcl_BackgroundException(interp, result); } - Tcl_Release((ClientData) interp); + Tcl_Release(interp); /* * Free the memory for the callback. @@ -1219,7 +1259,7 @@ AfterCleanupProc( * interpreter. */ Tcl_Interp *interp) /* Interpreter that is being deleted. */ { - AfterAssocData *assocPtr = (AfterAssocData *) clientData; + AfterAssocData *assocPtr = clientData; AfterInfo *afterPtr; while (assocPtr->firstAfterPtr != NULL) { @@ -1228,7 +1268,7 @@ AfterCleanupProc( if (afterPtr->token != NULL) { Tcl_DeleteTimerHandler(afterPtr->token); } else { - Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr); + Tcl_CancelIdleCall(AfterProc, afterPtr); } Tcl_DecrRefCount(afterPtr->commandPtr); ckfree((char *) afterPtr); @@ -1241,5 +1281,7 @@ AfterCleanupProc( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ |