summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-07-03 13:30:12 (GMT)
committersebres <sebres@users.sourceforge.net>2017-07-03 13:30:12 (GMT)
commitbeb9601daa56f0fa2d98122884b79d9bc59e8d1e (patch)
tree5c885e1d207316f3eec8127fb5113fc63d15ae78
parent6b524c87faf668d9cd21daccf2cdc8aa08957bfa (diff)
downloadtcl-beb9601daa56f0fa2d98122884b79d9bc59e8d1e.zip
tcl-beb9601daa56f0fa2d98122884b79d9bc59e8d1e.tar.gz
tcl-beb9601daa56f0fa2d98122884b79d9bc59e8d1e.tar.bz2
interim commit: code review, rewrite tclTimer, etc.
-rw-r--r--generic/tclIO.c53
-rw-r--r--generic/tclIO.h2
-rw-r--r--generic/tclInt.h53
-rw-r--r--generic/tclInterp.c4
-rw-r--r--generic/tclTimer.c218
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 <command> ... */
- 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));
}
}