diff options
author | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:23:45 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:23:45 (GMT) |
commit | df949554f991f8fb4a399bdd248437012deebe40 (patch) | |
tree | 81ee1cffcddb95744155eaf3ba12e8e4941e3588 /generic/tclTimer.c | |
parent | f629f9d07d2b465bba72bc71df2d2c9d3856bca8 (diff) | |
download | tcl-df949554f991f8fb4a399bdd248437012deebe40.zip tcl-df949554f991f8fb4a399bdd248437012deebe40.tar.gz tcl-df949554f991f8fb4a399bdd248437012deebe40.tar.bz2 |
[performance] large performance increase by event servicing cycles (3x - 5x faster now);
[win] prevent listen using PeekMessage twice, and no wait anymore for too short timeouts (because windows can wait too long), compare 0µs with up-to 100µs overhead within MsgWaitForMultipleObjectsEx;
[bad behavior] process idle events only as long as no other events available (now TclPeekEventQueued will be used to check new events are available in service idle cycle);
[enhancement] new option "noidletasks" for command "update", so "update noidle" means "process all events but not idle";
Diffstat (limited to 'generic/tclTimer.c')
-rw-r--r-- | generic/tclTimer.c | 214 |
1 files changed, 91 insertions, 123 deletions
diff --git a/generic/tclTimer.c b/generic/tclTimer.c index 7eee89e..d3aa5aa 100644 --- a/generic/tclTimer.c +++ b/generic/tclTimer.c @@ -121,26 +121,13 @@ static void FreeAfterPtr(ClientData clientData); static AfterInfo * GetAfterEvent(AfterAssocData *assocPtr, Tcl_Obj *objPtr); static ThreadSpecificData *InitTimer(void); static void TimerExitProc(ClientData clientData); -static int TimerHandlerEventProc(Tcl_Event *evPtr, int flags); -#if 0 static void TimerCheckProc(ClientData clientData, int flags); -#endif static void TimerSetupProc(ClientData clientData, int flags); static void AfterObj_DupInternalRep(Tcl_Obj *, Tcl_Obj *); static void AfterObj_FreeInternalRep(Tcl_Obj *); static void AfterObj_UpdateString(Tcl_Obj *); -static inline void -QueueTimerHandlerEvent() -{ - Tcl_Event *timerEvPtr; - - timerEvPtr = (Tcl_Event *) ckalloc(sizeof(Tcl_Event)); - timerEvPtr->proc = TimerHandlerEventProc; - Tcl_QueueEvent(timerEvPtr, TCL_QUEUE_TAIL); -} - /* * Type definition. */ @@ -272,7 +259,7 @@ InitTimer(void) if (tsdPtr == NULL) { tsdPtr = TCL_TSD_INIT(&dataKey); - Tcl_CreateEventSource(TimerSetupProc, NULL, tsdPtr); + Tcl_CreateEventSource(TimerSetupProc, TimerCheckProc, tsdPtr); Tcl_CreateThreadExitHandler(TimerExitProc, NULL); } return tsdPtr; @@ -303,7 +290,7 @@ TimerExitProc( TclThreadDataKeyGet(&dataKey); if (tsdPtr != NULL) { - Tcl_DeleteEventSource(TimerSetupProc, NULL, tsdPtr); + Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, tsdPtr); while ((tsdPtr->lastPromptPtr) != NULL) { TclDeleteTimerEntry(tsdPtr->lastPromptPtr); @@ -596,7 +583,7 @@ TclDeleteTimerEntry( static void TimerSetupProc( - ClientData data, /* Not used. */ + ClientData data, /* Specific data. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { Tcl_Time blockTime, *firstTime; @@ -604,10 +591,11 @@ TimerSetupProc( if (tsdPtr == NULL) { tsdPtr = InitTimer(); }; - if (((flags & TCL_IDLE_EVENTS) && tsdPtr->idleList ) - || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerPending)) { + if ( ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerPending) + || ((flags & TCL_IDLE_EVENTS) && tsdPtr->idleList ) + ) { /* - * There is an idle handler or a pending timer event, so just poll. + * There is a pending timer event or an idle handler, so just poll. */ blockTime.sec = 0; @@ -630,15 +618,6 @@ TimerSetupProc( blockTime.sec = 0; blockTime.usec = 0; } - - /* - * If the first timer has expired, stick an event on the queue. - */ - - if (blockTime.sec == 0 && blockTime.usec == 0) { - tsdPtr->timerPending = 1; - QueueTimerHandlerEvent(); - } } else { return; @@ -647,15 +626,13 @@ TimerSetupProc( Tcl_SetMaxBlockTime(&blockTime); } -#if 0 /* *---------------------------------------------------------------------- * * TimerCheckProc -- * * This function is called by Tcl_DoOneEvent to check the timer event - * source for events. This routine checks both the idle and after timer - * lists. + * source for events. This routine checks the first timer in the list. * * Results: * None. @@ -668,66 +645,63 @@ TimerSetupProc( static void TimerCheckProc( - ClientData data, /* Not used. */ + ClientData data, /* Specific data. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */ { Tcl_Time blockTime, *firstTime; - ThreadSpecificData *tsdPtr = InitTimer(); - - if ((flags & TCL_TIMER_EVENTS) - && !tsdPtr->timerPending - ) { - /* - * Compute the timeout for the next timer on the list. - */ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data; - if (tsdPtr->promptList) { - goto queuetmr; - } + if (!(flags & TCL_TIMER_EVENTS)) { + return; + } - if (!tsdPtr->timerList) { - return; - } - Tcl_GetTime(&blockTime); - firstTime = &(TimerEntry2TimerHandler(tsdPtr->timerList)->time); - blockTime.sec = firstTime->sec - blockTime.sec; - blockTime.usec = firstTime->usec - blockTime.usec; - if (blockTime.usec < 0) { - blockTime.sec -= 1; - blockTime.usec += 1000000; - } - if (blockTime.sec < 0) { - blockTime.sec = 0; - blockTime.usec = 0; - } + if (tsdPtr == NULL) { tsdPtr = InitTimer(); }; - /* - * If the first timer has expired, stick an event on the queue. - */ + /* If already pending */ + if (!tsdPtr->timerList || tsdPtr->timerPending) { + return; + } - if (blockTime.sec == 0 && blockTime.usec == 0) { - queuetmr: - tsdPtr->timerPending = 1; - QueueTimerHandlerEvent(); - } + /* + * Verify the first timer on the queue. + */ + Tcl_GetTime(&blockTime); + firstTime = &(TimerEntry2TimerHandler(tsdPtr->timerList)->time); + blockTime.sec = firstTime->sec - blockTime.sec; + blockTime.usec = firstTime->usec - blockTime.usec; + if (blockTime.usec < 0) { + blockTime.sec -= 1; + blockTime.usec += 1000000; + } + if (blockTime.sec < 0) { + blockTime.sec = 0; + blockTime.usec = 0; + } + + /* + * If the first timer has expired, stick an event on the queue. + */ + if (blockTime.sec == 0 && blockTime.usec == 0) { + TclSetTimerEventMarker(); + tsdPtr->timerPending = 1; } } -#endif /* *---------------------------------------------------------------------- * - * TimerHandlerEventProc -- + * TclServiceTimerEvents -- * - * This function is called by Tcl_ServiceEvent when a timer event reaches - * the front of the event queue. This function handles the event by + * This function is called by Tcl_ServiceEvent when a timer events should + * be processed. This function handles the event by * invoking the callbacks for all timers that are ready. * * Results: * Returns 1 if the event was handled, meaning it should be removed from - * the queue. Returns 0 if the event was not handled, meaning it should - * stay on the queue. The only time the event isn't handled is if the - * TCL_TIMER_EVENTS flag bit isn't set. + * the queue. + * Returns 0 if the event was not handled (no timer events). + * Returns -1 if pending timer events available, meaning the marker should + * stay on the queue. * * Side effects: * Whatever the timer handler callback functions do. @@ -735,25 +709,17 @@ TimerCheckProc( *---------------------------------------------------------------------- */ -static int -TimerHandlerEventProc( - Tcl_Event *evPtr, /* Event to service. */ - int flags) /* Flags that indicate what events to handle, - * such as TCL_FILE_EVENTS. */ +int +TclServiceTimerEvents(void) { TimerEntry *entryPtr, *nextPtr; Tcl_Time time; size_t currentGeneration, currentEpoch; ThreadSpecificData *tsdPtr = InitTimer(); - /* - * Do nothing if timers aren't enabled. This leaves the event on the - * queue, so we will get to it as soon as ServiceEvents() is called with - * timers enabled. - */ - if (!(flags & TCL_TIMER_EVENTS)) { - return 0; + if (!tsdPtr->timerPending) { + return 0; /* no timer events */ } /* @@ -798,10 +764,10 @@ TimerHandlerEventProc( ckfree((char *) entryPtr); } - /* if pending prompt events (new generation) - repeat event cycle right now */ + /* if stil pending prompt events (new generation) - repeat event cycle as + * soon as possible */ if (tsdPtr->promptList) { - tsdPtr->timerPending = 1; - return 0; /* leave handler event in the queue */ + return -1; } /* Hereafter all timer events with time before now */ @@ -859,10 +825,10 @@ TimerHandlerEventProc( } done: - /* don't need to queue event again by pending timer events */ + /* pending timer events, so mark (queue) timer events */ if (tsdPtr->timerPending > 1) { tsdPtr->timerPending = 1; - return 0; /* leave handler event in the queue */ + return -1; } /* Reset generation if both timer queue are empty */ @@ -873,7 +839,7 @@ done: /* Compute the next timeout (later via TimerSetupProc using the first timer). */ tsdPtr->timerPending = 0; - return 1; /* processing done, again later via TimerSetupProc */ + return 1; /* processing done, again later via TimerCheckProc */ } /* @@ -925,11 +891,9 @@ TclCreateTimerEntryEx( /* attach to the prompt queue */ TclSpliceTailEx(entryPtr, tsdPtr->promptList, tsdPtr->lastPromptPtr); - /* execute immediately: queue handler event right now */ - if (!tsdPtr->timerPending) { - QueueTimerHandlerEvent(); - } - tsdPtr->timerPending++; /* queued and TimerSetupProc knows about */ + /* execute immediately: signal pending and set timer marker */ + tsdPtr->timerPending++; + TclSetTimerEventMarker(); } else { /* idle generation */ entryPtr->generation = tsdPtr->idleGeneration; @@ -1018,7 +982,7 @@ Tcl_CancelIdleCall( /* *---------------------------------------------------------------------- * - * TclServiceIdle -- + * TclServiceIdle -- , TclServiceIdleEx -- * * This function is invoked by the notifier when it becomes idle. It will * invoke all idle handlers that are present at the time the call is @@ -1035,14 +999,14 @@ Tcl_CancelIdleCall( */ int -TclServiceIdle(void) +TclServiceIdleEx( + int count) { TimerEntry *idlePtr; size_t currentGeneration; - Tcl_Time blockTime; ThreadSpecificData *tsdPtr = InitTimer(); - if (tsdPtr->idleList == NULL) { + if ((idlePtr = tsdPtr->idleList) == NULL) { return 0; } @@ -1065,9 +1029,7 @@ TclServiceIdle(void) * during the call. */ - while ((idlePtr = tsdPtr->idleList) != NULL - && idlePtr->generation <= currentGeneration - ) { + while (idlePtr->generation <= currentGeneration) { /* detach entry from the owner's list */ TclSpliceOutEx(idlePtr, tsdPtr->idleList, tsdPtr->lastIdlePtr); @@ -1079,18 +1041,28 @@ TclServiceIdle(void) (*idlePtr->deleteProc)(idlePtr->clientData); } ckfree((char *) idlePtr); + + /* stop processing idle if no more idle, count reached or other queued */ + if ( (idlePtr = tsdPtr->idleList) == NULL + || !--count + || TclPeekEventQueued(TCL_ALL_EVENTS) + ) { + break; + } } - if (tsdPtr->idleList) { - blockTime.sec = 0; - blockTime.usec = 0; - Tcl_SetMaxBlockTime(&blockTime); - } + /* Reset generation */ if (!tsdPtr->idleList) { tsdPtr->idleGeneration = 0; } return 1; } + +int +TclServiceIdle(void) +{ + return TclServiceIdleEx(INT_MAX); +} /* *---------------------------------------------------------------------- @@ -1152,21 +1124,17 @@ Tcl_AfterObjCmd( * First lets see if the command was passed a number as the first argument. */ - if (objv[1]->typePtr == &tclIntType -#ifndef NO_WIDE_TYPE - || objv[1]->typePtr == &tclWideIntType -#endif - || 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); - return TCL_ERROR; - } + index = -1; + if ( ( objv[1]->typePtr == &tclIndexType + || Tcl_GetWideIntFromObj(NULL, objv[1], &ms) != TCL_OK + ) + && Tcl_GetIndexFromObj(NULL, objv[1], afterSubCmds, "", 0, + &index) != TCL_OK + ) { + Tcl_AppendResult(interp, "bad argument \"", + Tcl_GetString(objv[1]), + "\": must be cancel, idle, info, or an integer", NULL); + return TCL_ERROR; } /* |