From beb9601daa56f0fa2d98122884b79d9bc59e8d1e Mon Sep 17 00:00:00 2001 From: sebres Date: Mon, 3 Jul 2017 13:30:12 +0000 Subject: interim commit: code review, rewrite tclTimer, etc. --- generic/tclIO.c | 53 +++++++++++-- generic/tclIO.h | 2 +- generic/tclInt.h | 53 +++++++------ generic/tclInterp.c | 4 +- generic/tclTimer.c | 218 +++++++++++++++++++++++++++++++++------------------- 5 files changed, 219 insertions(+), 111 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 2e1569f..a705199 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -167,6 +167,7 @@ static void PreserveChannelBuffer(ChannelBuffer *bufPtr); static void ReleaseChannelBuffer(ChannelBuffer *bufPtr); static int IsShared(ChannelBuffer *bufPtr); static void ChannelFree(Channel *chanPtr); +static void FreeChannelTimerProc(ClientData clientData); static void ChannelTimerProc(ClientData clientData); static int ChanRead(Channel *chanPtr, char *dst, int dstSize); static int CheckChannelErrors(ChannelState *statePtr, @@ -2968,7 +2969,9 @@ CloseChannel( * Cancel any outstanding timer. */ - Tcl_DeleteTimerHandler(statePtr->timer); + if (statePtr->timer) { + TclpDeleteTimerEntry(statePtr->timer); + } /* * Mark the channel as deleted by clearing the type structure. @@ -3450,7 +3453,9 @@ Tcl_ClearChannelHandlers( * Cancel any outstanding timer. */ - Tcl_DeleteTimerHandler(statePtr->timer); + if (statePtr->timer) { + TclpDeleteTimerEntry(statePtr->timer); + } /* * Remove any references to channel handlers for this channel that may be @@ -8063,8 +8068,11 @@ UpdateInterest( mask &= ~TCL_EXCEPTION; if (!statePtr->timer) { - statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc, - chanPtr); + statePtr->timer = TclpCreateTimerEntryEx(ChannelTimerProc, + FreeChannelTimerProc, 0, TCL_PROMPT_EVENT); + if (statePtr->timer) { + statePtr->timer->clientData = chanPtr; + } } } } @@ -8074,6 +8082,35 @@ UpdateInterest( /* *---------------------------------------------------------------------- * + * FreeChannelTimerProc -- + * + * This function simply reset timer in channel state structure. + * It does *not* cancel the timer (called on execute/delete timer). + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +FreeChannelTimerProc( + ClientData clientData) /* Channel handle. */ +{ + Channel *chanPtr = clientData; + ChannelState *statePtr = chanPtr->state; + /* + * Because channel can operate with multiple timers (asynchronously), + * be sure another timer was not set in-between (e. g. recursive events) + */ + if (statePtr->timer && (statePtr->timer->flags & TCL_EVENTST_DELETE)) { + statePtr->timer = NULL; /* timer deleted */ + } +} + +/* + *---------------------------------------------------------------------- + * * ChannelTimerProc -- * * Timer handler scheduled by UpdateInterest to monitor the channel @@ -8101,11 +8138,11 @@ ChannelTimerProc( && (statePtr->inQueueHead != NULL) && IsBufferReady(statePtr->inQueueHead)) { /* - * Restart the timer in case a channel handler reenters the event loop + * Prolong the timer in case a channel handler reenters the event loop * before UpdateInterest gets called by Tcl_NotifyChannel. */ - statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,chanPtr); + TclpProlongTimerHandler(statePtr->timer, 0, 0); Tcl_Preserve(statePtr); Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); Tcl_Release(statePtr); @@ -8703,7 +8740,9 @@ TclCopyChannel( */ if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) { - Tcl_CreateTimerHandler(0, ZeroTransferTimerProc, csPtr); + TclTimerEntry *timer = TclpCreateTimerEntryEx(ZeroTransferTimerProc, + NULL, 0, TCL_PROMPT_EVENT); + timer->clientData = csPtr; return 0; } diff --git a/generic/tclIO.h b/generic/tclIO.h index d096798..cb2ca54 100644 --- a/generic/tclIO.h +++ b/generic/tclIO.h @@ -187,7 +187,7 @@ typedef struct ChannelState { /* Chain of all scripts registered for event * handlers ("fileevent") on this channel. */ int bufSize; /* What size buffers to allocate? */ - Tcl_TimerToken timer; /* Handle to wakeup timer for this channel. */ + TclTimerEntry *timer; /* Handle to wakeup timer for this channel. */ struct CopyState *csPtrR; /* State of background copy for which channel * is input, or NULL. */ struct CopyState *csPtrW; /* State of background copy for which channel diff --git a/generic/tclInt.h b/generic/tclInt.h index fcb4752..88d101b 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -143,10 +143,12 @@ typedef int ptrdiff_t; *---------------------------------------------------------------- */ -#define TCL_PROMPT_EVENT (1 << 0) /* Mark immediate event */ -#define TCL_ABSTMR_EVENT (1 << 1) /* Mark absolute timer event (the time - * of TimerHandler is absolute). */ -#define TCL_IDLE_EVENT (1 << 5) /* Mark idle event */ +#define TCL_PROMPT_EVENT (1 << 0) /* Mark immediate event */ +#define TCL_ABSTMR_EVENT (1 << 1) /* Mark absolute timer event (the time + * of TclTimerHandler is absolute). */ +#define TCL_IDLE_EVENT (1 << 3) /* Mark idle event */ +#define TCL_EVENTST_EXECUTE (1 << 5) /* Event executed now (reset to prolong). */ +#define TCL_EVENTST_DELETE (1 << 7) /* Event will be deleted. */ /* * This structure used for handling of timer events (with or without time to @@ -155,43 +157,44 @@ typedef int ptrdiff_t; * together into corresponding list. */ -typedef struct TimerEntry { +typedef struct TclTimerEntry { Tcl_TimerProc *proc; /* Function to call timer/idle event */ Tcl_TimerDeleteProc *deleteProc; /* Function to cleanup idle event */ ClientData clientData; /* Argument to pass to proc and deleteProc */ int flags; /* Flags (TCL_IDLE_EVENT, TCL_PROMPT_EVENT) */ size_t generation; /* Used to distinguish older handlers from * recently-created ones. */ - struct TimerEntry *nextPtr;/* Next and prev event in idle queue, */ - struct TimerEntry *prevPtr;/* or NULL for end/start of the queue. */ + struct TclTimerEntry *nextPtr;/* Next and prev event in idle queue, */ + struct TclTimerEntry *prevPtr;/* or NULL for end/start of the queue. */ /* ExtraData */ -} TimerEntry; +} TclTimerEntry; /* * For each timer callback that's pending there is one record of the following * type. The normal handlers (created by Tcl_CreateTimerHandler) are chained - * together in a list via TimerEntry sorted by time (earliest event first). + * together in a list via TclTimerEntry sorted by time (earliest event first). */ -typedef struct TimerHandler { +typedef struct TclTimerHandler { Tcl_WideInt time; /* When timer is to fire (absolute/relative). */ Tcl_TimerToken token; /* Identifies handler so it can be deleted. */ - struct TimerEntry entry; + struct TclTimerEntry entry; /* ExtraData */ -} TimerHandler; +} TclTimerHandler; /* - * Macros to wrap ExtraData and TimerHandler resp. TimerEntry (and vice versa) + * Macros to wrap ExtraData and TclTimerHandler resp. TclTimerEntry (and vice versa) */ -#define TimerEntry2ClientData(ptr) \ - ( (ClientData)(((TimerEntry *)(ptr))+1) ) -#define ClientData2TimerEntry(ptr) \ - ( ((TimerEntry *)(ptr))-1 ) +#define TclpTimerEntry2ClientData(ptr) \ + ( (ClientData)(((TclTimerEntry *)(ptr))+1) ) +#define TclpClientData2TimerEntry(ptr) \ + ( ((TclTimerEntry *)(ptr))-1 ) -#define TimerEntry2TimerHandler(ptr) \ - ( (TimerHandler *)(((char *)(ptr)) - TclOffset(TimerHandler,entry)) ) -#define TimerHandler2TimerEntry(ptr) \ +#define TclpTimerEntry2TimerHandler(ptr) \ + ( (TclTimerHandler *)(((char *)(ptr)) - \ + TclOffset(TclTimerHandler,entry)) ) +#define TclpTimerHandler2TimerEntry(ptr) \ ( &(ptr)->entry ) /* @@ -1864,7 +1867,7 @@ typedef struct Interp { * reached. */ int timeGranularity; /* Mod factor used to determine how often to * evaluate the limit check. */ - TimerEntry *timeEvent; /* Handle for a timer callback that will occur + TclTimerEntry *timeEvent;/* Handle for a timer callback that will occur * when the time-limit is exceeded. */ Tcl_HashTable callbacks;/* Mapping from (interp,type) pair to data @@ -3003,7 +3006,7 @@ MODULE_SCOPE int Tcl_ContinueObjCmd(ClientData clientData, MODULE_SCOPE void TclSetTimerEventMarker(int head); MODULE_SCOPE int TclServiceTimerEvents(void); MODULE_SCOPE int TclServiceIdleEx(int flags, int count); -MODULE_SCOPE TimerEntry* TclpCreateTimerHandlerEx( +MODULE_SCOPE TclTimerEntry* TclpCreateTimerHandlerEx( Tcl_WideInt usec, Tcl_TimerProc *proc, Tcl_TimerDeleteProc *deleteProc, size_t extraDataSize, int flags); @@ -3013,10 +3016,12 @@ MODULE_SCOPE Tcl_TimerToken TclCreateRelativeTimerHandler( MODULE_SCOPE Tcl_TimerToken TclCreateAbsoluteTimerHandler( Tcl_Time *timePtr, Tcl_TimerProc *proc, ClientData clientData); -MODULE_SCOPE TimerEntry* TclCreateTimerEntryEx( +MODULE_SCOPE TclTimerEntry* TclpCreateTimerEntryEx( Tcl_TimerProc *proc, Tcl_TimerDeleteProc *deleteProc, size_t extraDataSize, int flags); -MODULE_SCOPE void TclDeleteTimerEntry(TimerEntry *entryPtr); +MODULE_SCOPE void TclpDeleteTimerEntry(TclTimerEntry *entryPtr); +MODULE_SCOPE void TclpProlongTimerHandler(TclTimerEntry *entryPtr, + Tcl_WideInt usec, int flags); MODULE_SCOPE int TclPeekEventQueued(int flags); MODULE_SCOPE int TclDefaultBgErrorHandlerObjCmd( ClientData clientData, Tcl_Interp *interp, diff --git a/generic/tclInterp.c b/generic/tclInterp.c index 03a9fe2..0d7086c 100644 --- a/generic/tclInterp.c +++ b/generic/tclInterp.c @@ -3513,7 +3513,7 @@ TclLimitRemoveAllHandlers( */ if (iPtr->limit.timeEvent != NULL) { - TclDeleteTimerEntry(iPtr->limit.timeEvent); + TclpDeleteTimerEntry(iPtr->limit.timeEvent); iPtr->limit.timeEvent = NULL; } } @@ -3722,7 +3722,7 @@ Tcl_LimitSetTime( memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time)); if (iPtr->limit.timeEvent != NULL) { - TclDeleteTimerEntry(iPtr->limit.timeEvent); + TclpDeleteTimerEntry(iPtr->limit.timeEvent); } nextMoment = TCL_TIME_TO_USEC(*timeLimitPtr) + 10; iPtr->limit.timeEvent = TclpCreateTimerHandlerEx(nextMoment, diff --git a/generic/tclTimer.c b/generic/tclTimer.c index a245a18..c4183f4 100644 --- a/generic/tclTimer.c +++ b/generic/tclTimer.c @@ -65,17 +65,17 @@ typedef struct { 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 *timerList; /* First event in queue of timers. */ - TimerEntry *timerTail; /* Last event in queue of timers. */ - TimerEntry *promptList; /* First immediate event in queue. */ - TimerEntry *promptTail; /* Last immediate event in queue. */ + TclTimerEntry *timerList; /* First event in queue of timers. */ + TclTimerEntry *timerTail; /* Last event in queue of timers. */ + TclTimerEntry *promptList; /* First immediate event in queue. */ + TclTimerEntry *promptTail; /* Last immediate event in queue. */ size_t timerListEpoch; /* Used for safe process of event queue (stop * the cycle after modifying of event queue) */ int lastTimerId; /* Timer identifier of most recently created * timer event. */ int timerPending; /* 1 if a timer event is in the queue. */ - TimerEntry *idleList; /* First in list of all idle handlers. */ - TimerEntry *idleTail; /* Last in list (or NULL for empty list). */ + TclTimerEntry *idleList; /* First in list of all idle handlers. */ + TclTimerEntry *idleTail; /* Last in list (or NULL for empty list). */ size_t timerGeneration; /* Used to fill in the "generation" fields of */ size_t idleGeneration; /* timer or idle structures. Increments each * time we place a new handler to queue inside, @@ -91,10 +91,10 @@ static Tcl_ThreadDataKey dataKey; * Helper macros to wrap AfterInfo and handlers (and vice versa) */ -#define TimerEntry2AfterInfo(ptr) \ - ( (AfterInfo*)TimerEntry2ClientData(ptr) ) -#define AfterInfo2TimerEntry(ptr) \ - ClientData2TimerEntry(ptr) +#define TclpTimerEntry2AfterInfo(ptr) \ + ( (AfterInfo*)TclpTimerEntry2ClientData(ptr) ) +#define TclpAfterInfo2TimerEntry(ptr) \ + TclpClientData2TimerEntry(ptr) /* * Prototypes for functions referenced only in this file: @@ -253,6 +253,29 @@ InitTimer(void) return tsdPtr; } +static void +AttachTimerEntry( + ThreadSpecificData *tsdPtr, + TclTimerEntry *entryPtr) +{ + if (entryPtr->flags & TCL_PROMPT_EVENT) { + /* use timer generation, because usually no differences between + * call of "after 0" and "after 1" */ + entryPtr->generation = tsdPtr->timerGeneration; + /* attach to the prompt queue */ + TclSpliceTailEx(entryPtr, tsdPtr->promptList, tsdPtr->promptTail); + + /* execute immediately: signal pending and set timer marker */ + tsdPtr->timerPending++; + TclSetTimerEventMarker(0); + } else { + /* idle generation */ + entryPtr->generation = tsdPtr->idleGeneration; + /* attach to the idle queue */ + TclSpliceTailEx(entryPtr, tsdPtr->idleList, tsdPtr->idleTail); + } +} + /* *---------------------------------------------------------------------- * @@ -281,13 +304,13 @@ TimerExitProc( Tcl_DeleteEventSource(TimerSetupProc, TimerCheckProc, tsdPtr); while ((tsdPtr->promptTail) != NULL) { - TclDeleteTimerEntry(tsdPtr->promptTail); + TclpDeleteTimerEntry(tsdPtr->promptTail); } while ((tsdPtr->timerTail) != NULL) { - TclDeleteTimerEntry(tsdPtr->timerTail); + TclpDeleteTimerEntry(tsdPtr->timerTail); } while ((tsdPtr->idleTail) != NULL) { - TclDeleteTimerEntry(tsdPtr->idleTail); + TclpDeleteTimerEntry(tsdPtr->idleTail); } } } @@ -317,7 +340,7 @@ Tcl_CreateTimerHandler( Tcl_TimerProc *proc, /* Function to invoke. */ ClientData clientData) /* Arbitrary data to pass to proc. */ { - register TimerEntry *entryPtr; + register TclTimerEntry *entryPtr; Tcl_WideInt usec; /* @@ -336,7 +359,7 @@ Tcl_CreateTimerHandler( } entryPtr->clientData = clientData; - return TimerEntry2TimerHandler(entryPtr)->token; + return TclpTimerEntry2TimerHandler(entryPtr)->token; } /* @@ -358,7 +381,7 @@ Tcl_CreateTimerHandler( *-------------------------------------------------------------- */ -TimerEntry* +TclTimerEntry* TclpCreateTimerHandlerEx( Tcl_WideInt usec, /* Time to be invoked (absolute/relative) */ Tcl_TimerProc *proc, /* Function to invoke */ @@ -366,16 +389,17 @@ TclpCreateTimerHandlerEx( size_t extraDataSize, /* Size of extra data to allocate */ int flags) /* If TCL_ABSTMR_EVENT, time is absolute */ { - register TimerEntry *entryPtr, *entryPtrPos; - register TimerHandler *timerPtr; + register TclTimerEntry *entryPtr, *entryPtrPos; + register TclTimerHandler *timerPtr; ThreadSpecificData *tsdPtr; tsdPtr = InitTimer(); - timerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler) + extraDataSize); + timerPtr = (TclTimerHandler *)ckalloc( + sizeof(TclTimerHandler) + extraDataSize); if (timerPtr == NULL) { return NULL; } - entryPtr = TimerHandler2TimerEntry(timerPtr); + entryPtr = TclpTimerHandler2TimerEntry(timerPtr); /* * Fill in fields for the event. @@ -383,7 +407,7 @@ TclpCreateTimerHandlerEx( entryPtr->proc = proc; entryPtr->deleteProc = deleteProc; - entryPtr->clientData = TimerEntry2ClientData(entryPtr); + entryPtr->clientData = TclpTimerEntry2ClientData(entryPtr); entryPtr->flags = flags & TCL_ABSTMR_EVENT; entryPtr->generation = tsdPtr->timerGeneration; tsdPtr->timerListEpoch++; /* signal-timer list was changed */ @@ -428,7 +452,7 @@ TclpCreateTimerHandlerEx( /* if before current first (e. g. "after 1" before first "after 1000") */ if ( !(entryPtrPos = tsdPtr->timerList) - || usec < TimerEntry2TimerHandler(entryPtrPos)->time + || usec < TclpTimerEntry2TimerHandler(entryPtrPos)->time ) { /* splice to the head */ TclSpliceInEx(entryPtr, tsdPtr->timerList, tsdPtr->timerTail); @@ -436,7 +460,7 @@ TclpCreateTimerHandlerEx( /* search from end as long as one with time before not found */ for (entryPtrPos = tsdPtr->timerTail; entryPtrPos != NULL; entryPtrPos = entryPtrPos->prevPtr) { - if (usec >= TimerEntry2TimerHandler(entryPtrPos)->time) { + if (usec >= TclpTimerEntry2TimerHandler(entryPtrPos)->time) { break; } } @@ -485,7 +509,7 @@ TclCreateAbsoluteTimerHandler( Tcl_TimerProc *proc, ClientData clientData) { - register TimerEntry *entryPtr; + register TclTimerEntry *entryPtr; Tcl_WideInt usec; /* @@ -504,7 +528,7 @@ TclCreateAbsoluteTimerHandler( } entryPtr->clientData = clientData; - return TimerEntry2TimerHandler(entryPtr)->token; + return TclpTimerEntry2TimerHandler(entryPtr)->token; } /* @@ -531,7 +555,7 @@ TclCreateRelativeTimerHandler( Tcl_TimerProc *proc, ClientData clientData) { - register TimerEntry *entryPtr; + register TclTimerEntry *entryPtr; Tcl_WideInt usec; /* @@ -550,7 +574,7 @@ TclCreateRelativeTimerHandler( } entryPtr->clientData = clientData; - return TimerEntry2TimerHandler(entryPtr)->token; + return TclpTimerEntry2TimerHandler(entryPtr)->token; } /* @@ -576,7 +600,7 @@ Tcl_DeleteTimerHandler( Tcl_TimerToken token) /* Result previously returned by * Tcl_CreateTimerHandler. */ { - register TimerEntry *entryPtr; + register TclTimerEntry *entryPtr; ThreadSpecificData *tsdPtr = InitTimer(); if (token == NULL) { @@ -587,11 +611,11 @@ Tcl_DeleteTimerHandler( entryPtr != NULL; entryPtr = entryPtr->prevPtr ) { - if (TimerEntry2TimerHandler(entryPtr)->token != token) { + if (TclpTimerEntry2TimerHandler(entryPtr)->token != token) { continue; } - TclDeleteTimerEntry(entryPtr); + TclpDeleteTimerEntry(entryPtr); return; } } @@ -600,7 +624,7 @@ Tcl_DeleteTimerHandler( /* *-------------------------------------------------------------- * - * TclDeleteTimerEntry -- + * TclpDeleteTimerEntry -- * * Delete a previously-registered prompt, timer or idle handler. * @@ -616,18 +640,28 @@ Tcl_DeleteTimerHandler( */ void -TclDeleteTimerEntry( - TimerEntry *entryPtr) /* Result previously returned by */ - /* TclCreateTimerHandlerEx or TclCreateTimerEntryEx. */ +TclpDeleteTimerEntry( + TclTimerEntry *entryPtr) /* Result previously returned by */ + /* TclpCreateTimerHandlerEx or TclpCreateTimerEntryEx. */ { ThreadSpecificData *tsdPtr; if (entryPtr == NULL) { return; } + if (entryPtr->flags & (TCL_EVENTST_EXECUTE|TCL_EVENTST_DELETE)) { + /* do nothing - event will be automatically deleted hereafter */ + return; + } tsdPtr = InitTimer(); + /* + * Mark this entry will be deleted, so it can avoid double delete and + * caller can check in delete callback, the time entry handle is still + * the same (was not overriden in some recursive async-envent). + */ + entryPtr->flags |= TCL_EVENTST_DELETE; if (entryPtr->flags & TCL_PROMPT_EVENT) { /* prompt handler */ TclSpliceOutEx(entryPtr, tsdPtr->promptList, tsdPtr->promptTail); @@ -642,15 +676,38 @@ TclDeleteTimerEntry( /* free it via deleteProc or ckfree */ if (entryPtr->deleteProc) { + entryPtr->flags |= TCL_EVENTST_DELETE; (*entryPtr->deleteProc)(entryPtr->clientData); + /* if prolongation requested - reattached to tail */ + if (!(entryPtr->flags & TCL_EVENTST_DELETE)) { + return; + } } if (entryPtr->flags & (TCL_PROMPT_EVENT|TCL_IDLE_EVENT)) { ckfree((char *)entryPtr); } else { /* shift to the allocated pointer */ - ckfree((char *)TimerEntry2TimerHandler(entryPtr)); + ckfree((char *)TclpTimerEntry2TimerHandler(entryPtr)); + } +} + +void +TclpProlongTimerHandler( + TclTimerEntry *entryPtr, + Tcl_WideInt usec, + int flags) +{ + ThreadSpecificData *tsdPtr = InitTimer(); + + /* reset execution and deletion states */ + entryPtr->flags &= ~(TCL_EVENTST_EXECUTE|TCL_EVENTST_DELETE); + /* attach to the queue again (new generation) */ + if (usec != 0 || flags != 0) { + Tcl_Panic("NYI: ATM only prompt & idle"); + return; } + AttachTimerEntry(tsdPtr, entryPtr); } /* @@ -674,7 +731,7 @@ TclDeleteTimerEntry( static Tcl_WideInt TimerGetDueTime( ThreadSpecificData *tsdPtr, - TimerEntry *entryPtr, + TclTimerEntry *entryPtr, Tcl_WideInt now) { Tcl_WideInt firstTime; @@ -700,7 +757,7 @@ TimerGetDueTime( tsdPtr->knownTime = now; /* If absolute timer: end-time = absolute event-time */ - firstTime = TimerEntry2TimerHandler(entryPtr)->time; + firstTime = TclpTimerEntry2TimerHandler(entryPtr)->time; if ((entryPtr->flags & TCL_ABSTMR_EVENT)) { return firstTime; } @@ -871,7 +928,7 @@ TimerCheckProc( int TclServiceTimerEvents(void) { - TimerEntry *entryPtr, *nextPtr; + TclTimerEntry *entryPtr, *nextPtr; Tcl_WideInt now, entryTime; size_t currentGeneration, currentEpoch; int prevTmrPending; @@ -917,13 +974,22 @@ TclServiceTimerEvents(void) prevTmrPending = tsdPtr->timerPending; tsdPtr->timerPending = 0; /* execute event */ + entryPtr->flags |= TCL_EVENTST_EXECUTE; (*entryPtr->proc)(entryPtr->clientData); /* restore current timer pending */ tsdPtr->timerPending += prevTmrPending; - + /* if prolongation requested - reattached to tail */ + if (!(entryPtr->flags & TCL_EVENTST_EXECUTE)) { + continue; + } /* free it via deleteProc and ckfree */ if (entryPtr->deleteProc) { + entryPtr->flags |= TCL_EVENTST_DELETE; (*entryPtr->deleteProc)(entryPtr->clientData); + /* if prolongation requested - reattached to tail */ + if (!(entryPtr->flags & TCL_EVENTST_DELETE)) { + continue; + } } ckfree((char *) entryPtr); } @@ -978,16 +1044,18 @@ TclServiceTimerEvents(void) prevTmrPending = tsdPtr->timerPending; tsdPtr->timerPending = 0; /* invoke timer proc */ + entryPtr->flags |= TCL_EVENTST_EXECUTE; (*entryPtr->proc)(entryPtr->clientData); /* restore current timer pending */ tsdPtr->timerPending += prevTmrPending; /* free it via deleteProc or ckfree */ if (entryPtr->deleteProc) { + entryPtr->flags |= TCL_EVENTST_DELETE; (*entryPtr->deleteProc)(entryPtr->clientData); } - ckfree((char *) TimerEntry2TimerHandler(entryPtr)); + ckfree((char *) TclpTimerEntry2TimerHandler(entryPtr)); /* be sure that timer-list was not changed inside the proc call */ if (currentEpoch != tsdPtr->timerListEpoch) { @@ -1017,7 +1085,7 @@ TclServiceTimerEvents(void) /* *-------------------------------------------------------------- * - * TclCreateTimerEntryEx -- + * TclpCreateTimerEntryEx -- * * Arrange for proc to be invoked delayed (but prompt) as timer event, * without time ("after 0"). @@ -1038,40 +1106,26 @@ TclServiceTimerEvents(void) *-------------------------------------------------------------- */ -TimerEntry * -TclCreateTimerEntryEx( +TclTimerEntry * +TclpCreateTimerEntryEx( Tcl_TimerProc *proc, /* Function to invoke. */ Tcl_TimerDeleteProc *deleteProc, /* Function to cleanup */ size_t extraDataSize, int flags) { - register TimerEntry *entryPtr; + register TclTimerEntry *entryPtr; ThreadSpecificData *tsdPtr = InitTimer(); - entryPtr = (TimerEntry *) ckalloc(sizeof(TimerEntry) + extraDataSize); + entryPtr = (TclTimerEntry *) ckalloc(sizeof(TclTimerEntry) + extraDataSize); if (entryPtr == NULL) { return NULL; } entryPtr->proc = proc; entryPtr->deleteProc = deleteProc; - entryPtr->clientData = TimerEntry2ClientData(entryPtr); + entryPtr->clientData = TclpTimerEntry2ClientData(entryPtr); entryPtr->flags = flags; - if (flags & TCL_PROMPT_EVENT) { - /* use timer generation, because usually no differences between - * call of "after 0" and "after 1" */ - entryPtr->generation = tsdPtr->timerGeneration; - /* attach to the prompt queue */ - TclSpliceTailEx(entryPtr, tsdPtr->promptList, tsdPtr->promptTail); - - /* execute immediately: signal pending and set timer marker */ - tsdPtr->timerPending++; - TclSetTimerEventMarker(0); - } else { - /* idle generation */ - entryPtr->generation = tsdPtr->idleGeneration; - /* attach to the idle queue */ - TclSpliceTailEx(entryPtr, tsdPtr->idleList, tsdPtr->idleTail); - } + + AttachTimerEntry(tsdPtr, entryPtr); return entryPtr; } @@ -1099,7 +1153,7 @@ Tcl_DoWhenIdle( Tcl_IdleProc *proc, /* Function to invoke. */ ClientData clientData) /* Arbitrary value to pass to proc. */ { - TimerEntry *idlePtr = TclCreateTimerEntryEx(proc, NULL, 0, TCL_IDLE_EVENT); + TclTimerEntry *idlePtr = TclpCreateTimerEntryEx(proc, NULL, 0, TCL_IDLE_EVENT); if (idlePtr) { idlePtr->clientData = clientData; @@ -1129,7 +1183,7 @@ Tcl_CancelIdleCall( Tcl_IdleProc *proc, /* Function that was previously registered. */ ClientData clientData) /* Arbitrary value to pass to proc. */ { - register TimerEntry *idlePtr, *nextPtr; + register TclTimerEntry *idlePtr, *nextPtr; ThreadSpecificData *tsdPtr = InitTimer(); for (idlePtr = tsdPtr->idleList; @@ -1144,6 +1198,7 @@ Tcl_CancelIdleCall( /* free it via deleteProc and ckfree */ if (idlePtr->deleteProc) { + idlePtr->flags |= TCL_EVENTST_DELETE; (*idlePtr->deleteProc)(idlePtr->clientData); } ckfree((char *) idlePtr); @@ -1175,7 +1230,7 @@ TclServiceIdleEx( int flags, int count) { - TimerEntry *idlePtr; + TclTimerEntry *idlePtr; size_t currentGeneration; ThreadSpecificData *tsdPtr = InitTimer(); @@ -1207,19 +1262,28 @@ TclServiceIdleEx( TclSpliceOutEx(idlePtr, tsdPtr->idleList, tsdPtr->idleTail); /* execute event */ + idlePtr->flags |= TCL_EVENTST_EXECUTE; (*idlePtr->proc)(idlePtr->clientData); - + /* if prolongation requested - reattached to tail */ + if (!(idlePtr->flags & TCL_EVENTST_EXECUTE)) { + goto nextEvent; + } /* free it via deleteProc and ckfree */ if (idlePtr->deleteProc) { + idlePtr->flags |= TCL_EVENTST_DELETE; (*idlePtr->deleteProc)(idlePtr->clientData); + /* if prolongation requested - reattached to tail */ + if (!(idlePtr->flags & TCL_EVENTST_DELETE)) { + goto nextEvent; + } } ckfree((char *) idlePtr); + nextEvent: /* * Stop processing idle if idle queue empty, count reached or other * events queued (only if not idle events only to service). */ - if ( (idlePtr = tsdPtr->idleList) == NULL || !--count || ((flags & TCL_ALL_EVENTS) != TCL_IDLE_EVENTS @@ -1384,7 +1448,7 @@ Tcl_AfterObjCmd( return AfterDelay(interp, usec, 0); } case AFTER_AT: { - TimerEntry *entryPtr; + TclTimerEntry *entryPtr; int flags = 0; if (index == AFTER_AT) { if (objc < 3) { @@ -1408,14 +1472,14 @@ Tcl_AfterObjCmd( FreeAfterPtr, sizeof(AfterInfo), flags); } else { /* after 0 ... */ - entryPtr = TclCreateTimerEntryEx(AfterProc, + entryPtr = TclpCreateTimerEntryEx(AfterProc, FreeAfterPtr, sizeof(AfterInfo), TCL_PROMPT_EVENT); } if (entryPtr == NULL) { /* error handled in panic */ return TCL_ERROR; } - afterPtr = TimerEntry2AfterInfo(entryPtr); + afterPtr = TclpTimerEntry2AfterInfo(entryPtr); /* attach to the list */ afterPtr->assocPtr = assocPtr; @@ -1485,24 +1549,24 @@ Tcl_AfterObjCmd( } } if (afterPtr != NULL && afterPtr->assocPtr->interp == interp) { - TclDeleteTimerEntry(AfterInfo2TimerEntry(afterPtr)); + TclpDeleteTimerEntry(TclpAfterInfo2TimerEntry(afterPtr)); } break; } case AFTER_IDLE: { - TimerEntry *idlePtr; + TclTimerEntry *idlePtr; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "script script ..."); return TCL_ERROR; } - idlePtr = TclCreateTimerEntryEx(AfterProc, + idlePtr = TclpCreateTimerEntryEx(AfterProc, FreeAfterPtr, sizeof(AfterInfo), TCL_IDLE_EVENT); if (idlePtr == NULL) { /* error handled in panic */ return TCL_ERROR; } - afterPtr = TimerEntry2AfterInfo(idlePtr); + afterPtr = TclpTimerEntry2AfterInfo(idlePtr); /* attach to the list */ afterPtr->assocPtr = assocPtr; @@ -1557,7 +1621,7 @@ Tcl_AfterObjCmd( resultListPtr = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, resultListPtr, afterPtr->commandPtr); Tcl_ListObjAppendElement(interp, resultListPtr, Tcl_NewStringObj( - (AfterInfo2TimerEntry(afterPtr)->flags & TCL_IDLE_EVENT) ? + (TclpAfterInfo2TimerEntry(afterPtr)->flags & TCL_IDLE_EVENT) ? "idle" : "timer", -1)); Tcl_SetObjResult(interp, resultListPtr); break; @@ -1755,7 +1819,7 @@ AfterProc( */ /* remove delete proc from handler (we'll do cleanup here) */ - AfterInfo2TimerEntry(afterPtr)->deleteProc = NULL; + TclpAfterInfo2TimerEntry(afterPtr)->deleteProc = NULL; /* release object (mark it was triggered) */ if (afterPtr->selfPtr) { @@ -1858,7 +1922,7 @@ AfterCleanupProc( AfterAssocData *assocPtr = (AfterAssocData *) clientData; while ( assocPtr->lastAfterPtr ) { - TclDeleteTimerEntry(AfterInfo2TimerEntry(assocPtr->lastAfterPtr)); + TclpDeleteTimerEntry(TclpAfterInfo2TimerEntry(assocPtr->lastAfterPtr)); } } -- cgit v0.12