summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-07-03 13:21:34 (GMT)
committersebres <sebres@users.sourceforge.net>2017-07-03 13:21:34 (GMT)
commit04a1210ce8351b888d853d55e0eb0cade2dc0deb (patch)
tree3bb555111fb329b6bc4707b587746793e86c4106
parent5c5c34d0f377a01a93633a879290b3ecda260b9a (diff)
downloadtcl-04a1210ce8351b888d853d55e0eb0cade2dc0deb.zip
tcl-04a1210ce8351b888d853d55e0eb0cade2dc0deb.tar.gz
tcl-04a1210ce8351b888d853d55e0eb0cade2dc0deb.tar.bz2
timer resp. idle events optimized: better handling using doubly linked lists, prevents allocating memory twice for the "after" events (use memory inside timer/idle event for the "after" structure), etc.
-rw-r--r--generic/tcl.h2
-rw-r--r--generic/tclInt.h67
-rw-r--r--generic/tclTimer.c539
3 files changed, 418 insertions, 190 deletions
diff --git a/generic/tcl.h b/generic/tcl.h
index 64c4683..e36f4d8 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -696,6 +696,7 @@ typedef void (Tcl_FileFreeProc) _ANSI_ARGS_((ClientData clientData));
typedef void (Tcl_FreeInternalRepProc) _ANSI_ARGS_((struct Tcl_Obj *objPtr));
typedef void (Tcl_FreeProc) _ANSI_ARGS_((char *blockPtr));
typedef void (Tcl_IdleProc) _ANSI_ARGS_((ClientData clientData));
+typedef void (Tcl_IdleDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef void (Tcl_InterpDeleteProc) _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp));
typedef int (Tcl_MathProc) _ANSI_ARGS_((ClientData clientData,
@@ -710,6 +711,7 @@ typedef void (Tcl_PanicProc) _ANSI_ARGS_((CONST char *format, ...));
typedef void (Tcl_TcpAcceptProc) _ANSI_ARGS_((ClientData callbackData,
Tcl_Channel chan, char *address, int port));
typedef void (Tcl_TimerProc) _ANSI_ARGS_((ClientData clientData));
+typedef void (Tcl_TimerDeleteProc) _ANSI_ARGS_((ClientData clientData));
typedef int (Tcl_SetFromAnyProc) _ANSI_ARGS_((Tcl_Interp *interp,
struct Tcl_Obj *objPtr));
typedef void (Tcl_UpdateStringProc) _ANSI_ARGS_((struct Tcl_Obj *objPtr));
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 7e70627..99f9346 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2499,6 +2499,65 @@ MODULE_SCOPE char tclEmptyString;
#define TCL_DD_SHORTEST0 0x0
/* 'Shortest possible' after masking */
+
+
+/*
+ *----------------------------------------------------------------
+ * Data structures related to timer / idle events.
+ *----------------------------------------------------------------
+ */
+
+/*
+ * 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 sorted by time (earliest event first).
+ */
+
+
+typedef struct TimerHandler {
+ Tcl_TimerProc *proc; /* Function to call timer event */
+ Tcl_TimerDeleteProc *deleteProc; /* Function to cleanup timer event */
+ ClientData clientData; /* Argument to pass to proc and deleteProc */
+ size_t generation; /* Used to distinguish older handlers from
+ * recently-created ones. */
+ Tcl_TimerToken token; /* Identifies handler so it can be deleted. */
+ struct TimerHandler *nextPtr; /* Next and prev event in timer/idle queue, */
+ struct TimerHandler *prevPtr; /* or NULL for end/start of the queue. */
+ Tcl_Time time; /* When timer is to fire (if timer event). */
+/* ExtraData */
+} TimerHandler;
+
+/*
+ * There is one of the following structures for each of the handlers declared
+ * in a call to Tcl_DoWhenIdle. All of the currently-active handlers are
+ * linked together into a list.
+ */
+
+typedef struct IdleHandler {
+ Tcl_IdleProc *proc; /* Function to call idle event */
+ Tcl_IdleDeleteProc *deleteProc; /* Function to cleanup idle event */
+ ClientData clientData; /* Argument to pass to proc and deleteProc */
+ size_t generation; /* Used to distinguish older handlers from
+ * recently-created ones. */
+ struct IdleHandler *nextPtr;/* Next and prev event in idle queue, */
+ struct IdleHandler *prevPtr;/* or NULL for end/start of the queue. */
+/* ExtraData */
+} IdleHandler;
+
+
+/*
+ * Macros to wrap ExtraData and TimerHandler resp. IdleHandler (and vice versa)
+ */
+#define TimerHandler2ClientData(ptr) \
+ ( (ClientData)(((TimerHandler *)(ptr))+1) )
+#define ClientData2TimerHandler(ptr) \
+ ( ((TimerHandler *)(ptr))-1 )
+
+#define IdleHandler2ClientData(ptr) \
+ ( (ClientData)(((IdleHandler *)(ptr))+1) )
+#define ClientData2IdleHandler(ptr) \
+ ( ((IdleHandler *)(ptr))-1 )
+
/*
*----------------------------------------------------------------
* Procedures shared among Tcl modules but not used by the outside world:
@@ -2867,6 +2926,14 @@ MODULE_SCOPE int Tcl_ContinueObjCmd(ClientData clientData,
MODULE_SCOPE Tcl_TimerToken TclCreateAbsoluteTimerHandler(
Tcl_Time *timePtr, Tcl_TimerProc *proc,
ClientData clientData);
+MODULE_SCOPE TimerHandler* TclCreateAbsoluteTimerHandlerEx(
+ Tcl_Time *timePtr, Tcl_TimerProc *proc,
+ Tcl_TimerDeleteProc *deleteProc, size_t extraSize);
+MODULE_SCOPE void TclDeleteTimerHandler(TimerHandler *timerHandlerPtr);
+MODULE_SCOPE IdleHandler* TclCreateIdleHandlerEx(
+ Tcl_IdleProc *proc, Tcl_IdleDeleteProc *deleteProc,
+ size_t extraDataSize);
+MODULE_SCOPE void TclDeleteIdleHandler(IdleHandler *idlePtr);
MODULE_SCOPE int TclDefaultBgErrorHandlerObjCmd(
ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
diff --git a/generic/tclTimer.c b/generic/tclTimer.c
index d05320d..40316ca 100644
--- a/generic/tclTimer.c
+++ b/generic/tclTimer.c
@@ -13,40 +13,21 @@
#include "tclInt.h"
/*
- * 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 sorted by time (earliest event first).
- */
-
-typedef struct TimerHandler {
- Tcl_Time time; /* When timer is to fire. */
- Tcl_TimerProc *proc; /* Function to call. */
- ClientData clientData; /* Argument to pass to proc. */
- Tcl_TimerToken token; /* Identifies handler so it can be deleted. */
- struct TimerHandler *nextPtr;
- /* Next event in queue, or NULL for end of
- * queue. */
-} TimerHandler;
-
-/*
* The data structure below is used by the "after" command to remember the
* command to be executed later. All of the pending "after" commands for an
* interpreter are linked together in a list.
*/
+#define IDLE_EVENT (1 << 1) /* Mark idle event */
+
typedef struct AfterInfo {
struct AfterAssocData *assocPtr;
/* Pointer to the "tclAfter" assocData for the
* interp in which command will be
* executed. */
Tcl_Obj *commandPtr; /* Command to execute. */
- int id; /* Integer identifier for command; used to
- * cancel it. */
- Tcl_TimerToken token; /* Used to cancel the "after" command. NULL
- * means that the command is run as an idle
- * handler rather than as a timer handler.
- * NULL means this is an "after idle" handler
- * rather than a timer handler. */
+ size_t id; /* Integer identifier for command */
+ int flags; /* Flags (IDLE_EVENT) */
struct AfterInfo *nextPtr; /* Next in list of all "after" commands for
* this interpreter. */
struct AfterInfo *prevPtr; /* Prev in list of all "after" commands for
@@ -69,20 +50,6 @@ typedef struct AfterAssocData {
} AfterAssocData;
/*
- * There is one of the following structures for each of the handlers declared
- * in a call to Tcl_DoWhenIdle. All of the currently-active handlers are
- * linked together into a list.
- */
-
-typedef struct IdleHandler {
- Tcl_IdleProc (*proc); /* Function to call. */
- ClientData clientData; /* Value to pass to proc. */
- int generation; /* Used to distinguish older handlers from
- * recently-created ones. */
- struct IdleHandler *nextPtr;/* Next in list of active handlers. */
-} IdleHandler;
-
-/*
* The timer and idle queues are per-thread because they are associated with
* the notifier, which is also per-thread.
*
@@ -94,25 +61,42 @@ typedef struct IdleHandler {
* The structure defined below is used in this file only.
*/
-typedef struct ThreadSpecificData {
- TimerHandler *firstTimerHandlerPtr; /* First event in queue. */
+typedef struct {
+ TimerHandler *timerList; /* First event in queue. */
+ TimerHandler *lastTimerPtr; /* Last 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. */
int timerPending; /* 1 if a timer event is in the queue. */
IdleHandler *idleList; /* First in list of all idle handlers. */
IdleHandler *lastIdlePtr; /* Last in list (or NULL for empty list). */
- int idleGeneration; /* Used to fill in the "generation" fields of
- * IdleHandler structures. Increments each
- * time Tcl_DoOneEvent starts calling idle
- * handlers, so that all old handlers can be
+ 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,
+ * a new loop, so that all old handlers can be
* called without calling any of the new ones
* created by old ones. */
- int afterId; /* For unique identifiers of after events. */
+ size_t afterId; /* For unique identifiers of after events. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
/*
+ * Helper macros to wrap AfterInfo and handlers (and vice versa)
+ */
+
+#define TimerHandler2AfterInfo(ptr) \
+ ( (AfterInfo*)TimerHandler2ClientData(ptr) )
+#define AfterInfo2TimerHandler(ptr) \
+ ClientData2TimerHandler(ptr)
+
+#define IdleHandler2AfterInfo(ptr) \
+ ( (AfterInfo*)IdleHandler2ClientData(ptr) )
+#define AfterInfo2IdleHandler(ptr) \
+ ClientData2IdleHandler(ptr)
+
+/*
* Helper macros for working with times. TCL_TIME_BEFORE encodes how to write
* the ordering relation on (normalized) times, and TCL_TIME_DIFF_MS computes
* the number of milliseconds difference between two times. Both macros use
@@ -138,7 +122,7 @@ static void AfterCleanupProc(ClientData clientData,
Tcl_Interp *interp);
static int AfterDelay(Tcl_Interp *interp, Tcl_WideInt ms);
static void AfterProc(ClientData clientData);
-static void FreeAfterPtr(AfterInfo *afterPtr);
+static void FreeAfterPtr(ClientData clientData);
static AfterInfo * GetAfterEvent(AfterAssocData *assocPtr,
Tcl_Obj *commandPtr);
static ThreadSpecificData *InitTimer(void);
@@ -146,8 +130,8 @@ static void TimerExitProc(ClientData clientData);
static int TimerHandlerEventProc(Tcl_Event *evPtr, int flags);
static void TimerCheckProc(ClientData clientData, int flags);
static void TimerSetupProc(ClientData clientData, int flags);
-
-/*
+
+ /*
*----------------------------------------------------------------------
*
* InitTimer --
@@ -205,12 +189,11 @@ TimerExitProc(
if (tsdPtr != NULL) {
register TimerHandler *timerHandlerPtr;
- timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
- while (timerHandlerPtr != NULL) {
- tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
+ while ((timerHandlerPtr = tsdPtr->timerList) != NULL) {
+ tsdPtr->timerList = timerHandlerPtr->nextPtr;
ckfree((char *) timerHandlerPtr);
- timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
}
+ tsdPtr->lastTimerPtr = NULL;
}
}
@@ -239,6 +222,7 @@ Tcl_CreateTimerHandler(
Tcl_TimerProc *proc, /* Function to invoke. */
ClientData clientData) /* Arbitrary data to pass to proc. */
{
+ register TimerHandler *timerHandlerPtr;
Tcl_Time time;
/*
@@ -252,20 +236,27 @@ Tcl_CreateTimerHandler(
time.usec -= 1000000;
time.sec += 1;
}
- return TclCreateAbsoluteTimerHandler(&time, proc, clientData);
+
+ timerHandlerPtr = TclCreateAbsoluteTimerHandlerEx(&time, proc, NULL, 0);
+ if (timerHandlerPtr == NULL) {
+ return NULL;
+ }
+ timerHandlerPtr->clientData = clientData;
+
+ return timerHandlerPtr->token;
}
/*
*--------------------------------------------------------------
*
- * TclCreateAbsoluteTimerHandler --
+ * TclCreateAbsoluteTimerHandlerEx -- , TclCreateAbsoluteTimerHandler --
*
* Arrange for a given function to be invoked at a particular time in the
* future.
*
* Results:
- * The return value is a token for the timer event, which may be used to
- * delete the event before it fires.
+ * The return value is a handler or token of the timer event, which may
+ * be used to delete the event before it fires.
*
* Side effects:
* When the time in timePtr has been reached, proc will be invoked
@@ -274,17 +265,21 @@ Tcl_CreateTimerHandler(
*--------------------------------------------------------------
*/
-Tcl_TimerToken
-TclCreateAbsoluteTimerHandler(
- Tcl_Time *timePtr,
- Tcl_TimerProc *proc,
- ClientData clientData)
+TimerHandler*
+TclCreateAbsoluteTimerHandlerEx(
+ Tcl_Time *timePtr, /* Time to be invoked */
+ Tcl_TimerProc *proc, /* Function to invoke */
+ Tcl_TimerDeleteProc *deleteProc, /* Function to cleanup */
+ size_t extraDataSize)
{
- register TimerHandler *timerHandlerPtr, *tPtr2, *prevPtr;
+ register TimerHandler *timerHandlerPtr, *thPtrPos;
ThreadSpecificData *tsdPtr;
tsdPtr = InitTimer();
- timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler));
+ timerHandlerPtr = (TimerHandler *) ckalloc(sizeof(TimerHandler) + extraDataSize);
+ if (timerHandlerPtr == NULL) {
+ return NULL;
+ }
/*
* Fill in fields for the event.
@@ -292,7 +287,10 @@ TclCreateAbsoluteTimerHandler(
memcpy((void *)&timerHandlerPtr->time, (void *)timePtr, sizeof(Tcl_Time));
timerHandlerPtr->proc = proc;
- timerHandlerPtr->clientData = clientData;
+ timerHandlerPtr->deleteProc = deleteProc;
+ timerHandlerPtr->clientData = TimerHandler2ClientData(timerHandlerPtr);
+ timerHandlerPtr->generation = tsdPtr->timerGeneration;
+ tsdPtr->timerListEpoch++; /* signal-timer list was changed */
tsdPtr->lastTimerId++;
timerHandlerPtr->token = (Tcl_TimerToken) INT2PTR(tsdPtr->lastTimerId);
@@ -301,21 +299,57 @@ TclCreateAbsoluteTimerHandler(
* (ordered by event firing time).
*/
- for (tPtr2 = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL; tPtr2 != NULL;
- prevPtr = tPtr2, tPtr2 = tPtr2->nextPtr) {
- if (TCL_TIME_BEFORE(timerHandlerPtr->time, tPtr2->time)) {
- break;
- }
- }
- timerHandlerPtr->nextPtr = tPtr2;
- if (prevPtr == NULL) {
- tsdPtr->firstTimerHandlerPtr = timerHandlerPtr;
+ /* if before current first (e. g. "after 0" before first "after 1000") */
+ if ( !(thPtrPos = tsdPtr->timerList)
+ || TCL_TIME_BEFORE(timerHandlerPtr->time, thPtrPos->time)
+ ) {
+ /* splice to the head */
+ TclSpliceInEx(timerHandlerPtr,
+ tsdPtr->timerList, tsdPtr->lastTimerPtr);
} else {
- prevPtr->nextPtr = timerHandlerPtr;
+ /* search from end as long as one with time before not found */
+ for (thPtrPos = tsdPtr->lastTimerPtr; thPtrPos != NULL;
+ thPtrPos = thPtrPos->prevPtr) {
+ if (TCL_TIME_BEFORE(thPtrPos->time, timerHandlerPtr->time)) {
+ break;
+ }
+ }
+ /* normally it should be always true, because checked above, but ... */
+ if (thPtrPos != NULL) {
+ /* insert after found element (with time before new) */
+ timerHandlerPtr->prevPtr = thPtrPos;
+ if ((timerHandlerPtr->nextPtr = thPtrPos->nextPtr)) {
+ thPtrPos->nextPtr->prevPtr = timerHandlerPtr;
+ } else {
+ tsdPtr->lastTimerPtr = timerHandlerPtr;
+ }
+ thPtrPos->nextPtr = timerHandlerPtr;
+ } else {
+ /* unexpected case, but ... splice to the head */
+ TclSpliceInEx(timerHandlerPtr,
+ tsdPtr->timerList, tsdPtr->lastTimerPtr);
+ }
}
TimerSetupProc(NULL, TCL_ALL_EVENTS);
+ return timerHandlerPtr;
+}
+
+Tcl_TimerToken
+TclCreateAbsoluteTimerHandler(
+ Tcl_Time *timePtr,
+ Tcl_TimerProc *proc,
+ ClientData clientData)
+{
+ register TimerHandler *timerHandlerPtr;
+
+ timerHandlerPtr = TclCreateAbsoluteTimerHandlerEx(timePtr, proc, NULL, 0);
+ if (timerHandlerPtr == NULL) {
+ return NULL;
+ }
+ timerHandlerPtr->clientData = clientData;
+
return timerHandlerPtr->token;
}
@@ -340,30 +374,51 @@ TclCreateAbsoluteTimerHandler(
void
Tcl_DeleteTimerHandler(
Tcl_TimerToken token) /* Result previously returned by
- * Tcl_DeleteTimerHandler. */
+ * Tcl_CreateTimerHandler. */
{
- register TimerHandler *timerHandlerPtr, *prevPtr;
+ register TimerHandler *timerHandlerPtr;
ThreadSpecificData *tsdPtr = InitTimer();
if (token == NULL) {
return;
}
- for (timerHandlerPtr = tsdPtr->firstTimerHandlerPtr, prevPtr = NULL;
- timerHandlerPtr != NULL; prevPtr = timerHandlerPtr,
- timerHandlerPtr = timerHandlerPtr->nextPtr) {
+ for (timerHandlerPtr = tsdPtr->lastTimerPtr;
+ timerHandlerPtr != NULL;
+ timerHandlerPtr = timerHandlerPtr->prevPtr
+ ) {
if (timerHandlerPtr->token != token) {
continue;
}
- if (prevPtr == NULL) {
- tsdPtr->firstTimerHandlerPtr = timerHandlerPtr->nextPtr;
- } else {
- prevPtr->nextPtr = timerHandlerPtr->nextPtr;
- }
- ckfree((char *) timerHandlerPtr);
+
+ TclDeleteTimerHandler(timerHandlerPtr);
return;
}
}
+
+void
+TclDeleteTimerHandler(
+ TimerHandler *timerHandlerPtr) /* Result previously returned by */
+ /* TclCreateAbsoluteTimerHandlerEx. */
+{
+ ThreadSpecificData *tsdPtr;
+
+ if (timerHandlerPtr == NULL) {
+ return;
+ }
+
+ tsdPtr = InitTimer();
+
+ tsdPtr->timerListEpoch++; /* signal-timer list was changed */
+ TclSpliceOutEx(timerHandlerPtr, tsdPtr->timerList, tsdPtr->lastTimerPtr);
+
+ /* free it via deleteProc or ckfree */
+ if (timerHandlerPtr->deleteProc) {
+ (*timerHandlerPtr->deleteProc)(timerHandlerPtr->clientData);
+ }
+
+ ckfree((char *)timerHandlerPtr);
+}
/*
*----------------------------------------------------------------------
@@ -400,14 +455,14 @@ TimerSetupProc(
blockTime.sec = 0;
blockTime.usec = 0;
- } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
+ } else if ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerList) {
/*
* Compute the timeout for the next timer on the list.
*/
Tcl_GetTime(&blockTime);
- blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
- blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
+ blockTime.sec = tsdPtr->timerList->time.sec - blockTime.sec;
+ blockTime.usec = tsdPtr->timerList->time.usec -
blockTime.usec;
if (blockTime.usec < 0) {
blockTime.sec -= 1;
@@ -451,14 +506,14 @@ TimerCheckProc(
Tcl_Time blockTime;
ThreadSpecificData *tsdPtr = InitTimer();
- if ((flags & TCL_TIMER_EVENTS) && tsdPtr->firstTimerHandlerPtr) {
+ if ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerList) {
/*
* Compute the timeout for the next timer on the list.
*/
Tcl_GetTime(&blockTime);
- blockTime.sec = tsdPtr->firstTimerHandlerPtr->time.sec - blockTime.sec;
- blockTime.usec = tsdPtr->firstTimerHandlerPtr->time.usec -
+ blockTime.sec = tsdPtr->timerList->time.sec - blockTime.sec;
+ blockTime.usec = tsdPtr->timerList->time.usec -
blockTime.usec;
if (blockTime.usec < 0) {
blockTime.sec -= 1;
@@ -510,9 +565,9 @@ TimerHandlerEventProc(
int flags) /* Flags that indicate what events to handle,
* such as TCL_FILE_EVENTS. */
{
- TimerHandler *timerHandlerPtr, **nextPtrPtr;
+ TimerHandler *timerHandlerPtr, *nextPtr;
Tcl_Time time;
- int currentTimerId;
+ size_t currentGeneration, currentEpoch;
ThreadSpecificData *tsdPtr = InitTimer();
/*
@@ -531,9 +586,7 @@ TimerHandlerEventProc(
* 1. New handlers can get added to the list while the current one is
* being processed. If new ones get added, we don't want to process
* them during this pass through the list to avoid starving other event
- * sources. This is implemented using the token number in the handler:
- * new handlers will have a newer token than any of the ones currently
- * on the list.
+ * sources. This is implemented using check of the generation epoch.
* 2. The handler can call Tcl_DoOneEvent, so we have to remove the
* handler from the list before calling it. Otherwise an infinite loop
* could result.
@@ -551,36 +604,60 @@ TimerHandlerEventProc(
*/
tsdPtr->timerPending = 0;
- currentTimerId = tsdPtr->lastTimerId;
+ currentGeneration = tsdPtr->timerGeneration++;
Tcl_GetTime(&time);
- while (1) {
- nextPtrPtr = &tsdPtr->firstTimerHandlerPtr;
- timerHandlerPtr = tsdPtr->firstTimerHandlerPtr;
- if (timerHandlerPtr == NULL) {
- break;
- }
+ for (timerHandlerPtr = tsdPtr->timerList;
+ timerHandlerPtr != NULL;
+ timerHandlerPtr = nextPtr
+ ) {
+ nextPtr = timerHandlerPtr->nextPtr;
if (TCL_TIME_BEFORE(time, timerHandlerPtr->time)) {
break;
}
/*
- * Bail out if the next timer is of a newer generation.
+ * Bypass timers of newer generation.
*/
- if ((currentTimerId - PTR2INT(timerHandlerPtr->token)) < 0) {
- break;
+ if (timerHandlerPtr->generation > currentGeneration) {
+ continue;
}
+ tsdPtr->timerListEpoch++; /* signal-timer list was changed */
+
/*
* Remove the handler from the queue before invoking it, to avoid
* potential reentrancy problems.
*/
- (*nextPtrPtr) = timerHandlerPtr->nextPtr;
+ TclSpliceOutEx(timerHandlerPtr,
+ tsdPtr->timerList, tsdPtr->lastTimerPtr);
+
+ currentEpoch = tsdPtr->timerListEpoch;
+
+ /* invoke timer proc */
(*timerHandlerPtr->proc)(timerHandlerPtr->clientData);
+ /* free it via deleteProc or ckfree */
+ if (timerHandlerPtr->deleteProc) {
+ (*timerHandlerPtr->deleteProc)(timerHandlerPtr->clientData);
+ }
+
ckfree((char *) timerHandlerPtr);
+
+ /* be sure that timer-list was not changed inside the proc call */
+ if (currentEpoch != tsdPtr->timerListEpoch) {
+ /* timer-list was changed - stop processing */
+ break;
+ }
}
+
+ /* Reset generation */
+ if (!tsdPtr->timerList) {
+ tsdPtr->timerGeneration = 0;
+ }
+
+ /* Compute the next timeout (first timer on the list). */
TimerSetupProc(NULL, TCL_TIMER_EVENTS);
return 1;
}
@@ -588,7 +665,7 @@ TimerHandlerEventProc(
/*
*--------------------------------------------------------------
*
- * Tcl_DoWhenIdle --
+ * TclCreateIdleHandlerEx --, Tcl_DoWhenIdle --
*
* Arrange for proc to be invoked the next time the system is idle (i.e.,
* just before the next time that Tcl_DoOneEvent would have to wait for
@@ -604,30 +681,85 @@ TimerHandlerEventProc(
*--------------------------------------------------------------
*/
-void
-Tcl_DoWhenIdle(
- Tcl_IdleProc *proc, /* Function to invoke. */
- ClientData clientData) /* Arbitrary value to pass to proc. */
+IdleHandler *
+TclCreateIdleHandlerEx(
+ Tcl_IdleProc *proc, /* Function to invoke. */
+ Tcl_IdleDeleteProc *deleteProc, /* Function to cleanup */
+ size_t extraDataSize)
{
register IdleHandler *idlePtr;
Tcl_Time blockTime;
ThreadSpecificData *tsdPtr = InitTimer();
- idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler));
+ idlePtr = (IdleHandler *) ckalloc(sizeof(IdleHandler) + extraDataSize);
+ if (idlePtr == NULL) {
+ return NULL;
+ }
idlePtr->proc = proc;
- idlePtr->clientData = clientData;
+ idlePtr->deleteProc = deleteProc;
+ idlePtr->clientData = IdleHandler2ClientData(idlePtr);
idlePtr->generation = tsdPtr->idleGeneration;
- idlePtr->nextPtr = NULL;
- if (tsdPtr->lastIdlePtr == NULL) {
- tsdPtr->idleList = idlePtr;
- } else {
- tsdPtr->lastIdlePtr->nextPtr = idlePtr;
- }
- tsdPtr->lastIdlePtr = idlePtr;
+ /* attach to the idle queue */
+ TclSpliceTailEx(idlePtr, tsdPtr->idleList, tsdPtr->lastIdlePtr);
+
+ /* reset next block time */
blockTime.sec = 0;
blockTime.usec = 0;
Tcl_SetMaxBlockTime(&blockTime);
+
+ return idlePtr;
+}
+
+void
+Tcl_DoWhenIdle(
+ Tcl_IdleProc *proc, /* Function to invoke. */
+ ClientData clientData) /* Arbitrary value to pass to proc. */
+{
+ register IdleHandler *idlePtr = TclCreateIdleHandlerEx(proc, NULL, 0);
+
+ if (idlePtr) {
+ idlePtr->clientData = clientData;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TclDeleteIdleHandler --
+ *
+ * Delete a previously-registered idle handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TclDeleteIdleHandler(
+ IdleHandler *idlePtr) /* Result previously returned by */
+ /* TclCreateIdleHandlerEx */
+{
+ ThreadSpecificData *tsdPtr;
+
+ if (idlePtr == NULL) {
+ return;
+ }
+
+ tsdPtr = InitTimer();
+
+ /* detach entry from the owner list */
+ TclSpliceOutEx(idlePtr, tsdPtr->idleList, tsdPtr->lastIdlePtr);
+
+ /* free it via deleteProc and ckfree */
+ if (idlePtr->deleteProc) {
+ (*idlePtr->deleteProc)(idlePtr->clientData);
+ }
+ ckfree((char *)idlePtr);
}
/*
@@ -653,26 +785,24 @@ Tcl_CancelIdleCall(
Tcl_IdleProc *proc, /* Function that was previously registered. */
ClientData clientData) /* Arbitrary value to pass to proc. */
{
- register IdleHandler *idlePtr, *prevPtr;
- IdleHandler *nextPtr;
+ register IdleHandler *idlePtr, *nextPtr;
ThreadSpecificData *tsdPtr = InitTimer();
- for (prevPtr = NULL, idlePtr = tsdPtr->idleList; idlePtr != NULL;
- prevPtr = idlePtr, idlePtr = idlePtr->nextPtr) {
- while ((idlePtr->proc == proc)
+ for (idlePtr = tsdPtr->idleList;
+ idlePtr != NULL;
+ idlePtr = nextPtr
+ ) {
+ nextPtr = idlePtr->nextPtr;
+ if ((idlePtr->proc == proc)
&& (idlePtr->clientData == clientData)) {
- nextPtr = idlePtr->nextPtr;
- ckfree((char *) idlePtr);
- idlePtr = nextPtr;
- if (prevPtr == NULL) {
- tsdPtr->idleList = idlePtr;
- } else {
- prevPtr->nextPtr = idlePtr;
- }
- if (idlePtr == NULL) {
- tsdPtr->lastIdlePtr = prevPtr;
- return;
+ /* detach entry from the owner list */
+ TclSpliceOutEx(idlePtr, tsdPtr->idleList, tsdPtr->lastIdlePtr);
+
+ /* free it via deleteProc and ckfree */
+ if (idlePtr->deleteProc) {
+ (*idlePtr->deleteProc)(idlePtr->clientData);
}
+ ckfree((char *) idlePtr);
}
}
}
@@ -700,7 +830,7 @@ int
TclServiceIdle(void)
{
IdleHandler *idlePtr;
- int oldGeneration;
+ size_t currentGeneration;
Tcl_Time blockTime;
ThreadSpecificData *tsdPtr = InitTimer();
@@ -708,8 +838,7 @@ TclServiceIdle(void)
return 0;
}
- oldGeneration = tsdPtr->idleGeneration;
- tsdPtr->idleGeneration++;
+ currentGeneration = tsdPtr->idleGeneration++;
/*
* The code below is trickier than it may look, for the following reasons:
@@ -728,15 +857,19 @@ TclServiceIdle(void)
* during the call.
*/
- for (idlePtr = tsdPtr->idleList;
- ((idlePtr != NULL)
- && ((oldGeneration - idlePtr->generation) >= 0));
- idlePtr = tsdPtr->idleList) {
- tsdPtr->idleList = idlePtr->nextPtr;
- if (tsdPtr->idleList == NULL) {
- tsdPtr->lastIdlePtr = NULL;
- }
+ while ((idlePtr = tsdPtr->idleList) != NULL
+ && idlePtr->generation <= currentGeneration
+ ) {
+ /* detach entry from the owner's list */
+ TclSpliceOutEx(idlePtr, tsdPtr->idleList, tsdPtr->lastIdlePtr);
+
+ /* execute event */
(*idlePtr->proc)(idlePtr->clientData);
+
+ /* free it via deleteProc and ckfree */
+ if (idlePtr->deleteProc) {
+ (*idlePtr->deleteProc)(idlePtr->clientData);
+ }
ckfree((char *) idlePtr);
}
if (tsdPtr->idleList) {
@@ -744,6 +877,10 @@ TclServiceIdle(void)
blockTime.usec = 0;
Tcl_SetMaxBlockTime(&blockTime);
}
+ /* Reset generation */
+ if (!tsdPtr->idleList) {
+ tsdPtr->idleGeneration = 0;
+ }
return 1;
}
@@ -833,14 +970,35 @@ Tcl_AfterObjCmd(
switch (index) {
case -1: {
+ TimerHandler *timerPtr;
if (ms < 0) {
ms = 0;
}
if (objc == 2) {
return AfterDelay(interp, ms);
}
- afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
+
+ Tcl_GetTime(&wakeup);
+ wakeup.sec += (long)(ms / 1000);
+ wakeup.usec += ((long)(ms % 1000)) * 1000;
+ if (wakeup.usec > 1000000) {
+ wakeup.sec++;
+ wakeup.usec -= 1000000;
+ }
+
+ timerPtr = TclCreateAbsoluteTimerHandlerEx(&wakeup, AfterProc,
+ FreeAfterPtr, sizeof(AfterInfo));
+ if (timerPtr == NULL) { /* error handled in panic */
+ return TCL_ERROR;
+ }
+ afterPtr = TimerHandler2AfterInfo(timerPtr);
+
+ /* attach to the list */
afterPtr->assocPtr = assocPtr;
+ TclSpliceTailEx(afterPtr,
+ assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
+ afterPtr->flags = 0;
+
if (objc == 3) {
afterPtr->commandPtr = objv[2];
} else {
@@ -858,19 +1016,7 @@ Tcl_AfterObjCmd(
* around when wrap-around occurs.
*/
- afterPtr->id = tsdPtr->afterId;
- tsdPtr->afterId += 1;
- Tcl_GetTime(&wakeup);
- wakeup.sec += (long)(ms / 1000);
- wakeup.usec += ((long)(ms % 1000)) * 1000;
- if (wakeup.usec > 1000000) {
- wakeup.sec++;
- wakeup.usec -= 1000000;
- }
- afterPtr->token = TclCreateAbsoluteTimerHandler(&wakeup, AfterProc,
- (ClientData) afterPtr);
- /* attach to the list */
- TclSpliceTailEx(afterPtr, assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
+ afterPtr->id = tsdPtr->afterId++;
Tcl_SetObjResult(interp, Tcl_ObjPrintf("after#%d", afterPtr->id));
return TCL_OK;
@@ -907,37 +1053,48 @@ Tcl_AfterObjCmd(
Tcl_DecrRefCount(commandPtr);
}
if (afterPtr != NULL) {
- if (afterPtr->token != NULL) {
- Tcl_DeleteTimerHandler(afterPtr->token);
+ if (!(afterPtr->flags & IDLE_EVENT)) {
+ TclDeleteTimerHandler(AfterInfo2TimerHandler(afterPtr));
} else {
- Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
+ TclDeleteIdleHandler(AfterInfo2IdleHandler(afterPtr));
}
- FreeAfterPtr(afterPtr);
}
break;
}
- case AFTER_IDLE:
+ case AFTER_IDLE: {
+ IdleHandler *idlePtr;
+
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "script script ...");
return TCL_ERROR;
}
- afterPtr = (AfterInfo *) ckalloc((unsigned) (sizeof(AfterInfo)));
+
+ idlePtr = TclCreateIdleHandlerEx(AfterProc,
+ FreeAfterPtr, sizeof(AfterInfo));
+ if (idlePtr == NULL) { /* error handled in panic */
+ return TCL_ERROR;
+ }
+ afterPtr = IdleHandler2AfterInfo(idlePtr);
+
+ /* attach to the list */
afterPtr->assocPtr = assocPtr;
+ TclSpliceTailEx(afterPtr,
+ assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
+ afterPtr->flags = IDLE_EVENT;
+
if (objc == 3) {
afterPtr->commandPtr = objv[2];
} else {
afterPtr->commandPtr = Tcl_ConcatObj(objc-2, objv+2);
}
Tcl_IncrRefCount(afterPtr->commandPtr);
- afterPtr->id = tsdPtr->afterId;
- tsdPtr->afterId += 1;
- afterPtr->token = NULL;
- /* attach to the list */
- TclSpliceTailEx(afterPtr, assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
-
- Tcl_DoWhenIdle(AfterProc, (ClientData) afterPtr);
+
+ afterPtr->id = tsdPtr->afterId++;
+
Tcl_SetObjResult(interp, Tcl_ObjPrintf("after#%d", afterPtr->id));
- break;
+
+ return TCL_OK;
+ };
case AFTER_INFO: {
Tcl_Obj *resultListPtr;
@@ -964,7 +1121,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->flags & IDLE_EVENT) ? "idle" : "timer", -1));
Tcl_SetObjResult(interp, resultListPtr);
break;
}
@@ -1131,6 +1288,14 @@ AfterProc(
* a core dump.
*/
+
+ /* remove delete proc from handler (we'll do cleanup here) */
+ if (!(afterPtr->flags & IDLE_EVENT)) {
+ AfterInfo2TimerHandler(afterPtr)->deleteProc = NULL;
+ } else {
+ AfterInfo2IdleHandler(afterPtr)->deleteProc = NULL;
+ }
+
/* detach entry from the owner's list */
TclSpliceOutEx(afterPtr, assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
@@ -1152,7 +1317,7 @@ AfterProc(
*/
Tcl_DecrRefCount(afterPtr->commandPtr);
- ckfree((char *) afterPtr);
+
}
/*
@@ -1168,23 +1333,23 @@ AfterProc(
* None.
*
* Side effects:
- * The memory associated with afterPtr is released.
+ * The memory associated with afterPtr is not released (owned by handler).
*
*----------------------------------------------------------------------
*/
static void
FreeAfterPtr(
- AfterInfo *afterPtr) /* Command to be deleted. */
+ ClientData clientData) /* Command to be deleted. */
{
+ AfterInfo *afterPtr = (AfterInfo *) clientData;
AfterAssocData *assocPtr = afterPtr->assocPtr;
/* detach entry from the owner's list */
TclSpliceOutEx(afterPtr, assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
- /* free command and entry */
+ /* free command of entry */
Tcl_DecrRefCount(afterPtr->commandPtr);
- ckfree((char *) afterPtr);
}
/*
@@ -1215,18 +1380,12 @@ AfterCleanupProc(
AfterInfo *afterPtr;
while ( (afterPtr = assocPtr->lastAfterPtr) ) {
- /* detach entry from the owner's list */
- TclSpliceOutEx(afterPtr, assocPtr->firstAfterPtr, assocPtr->lastAfterPtr);
-
- if (afterPtr->token != NULL) {
- Tcl_DeleteTimerHandler(afterPtr->token);
+ if (!(afterPtr->flags & IDLE_EVENT)) {
+ TclDeleteTimerHandler(AfterInfo2TimerHandler(afterPtr));
} else {
- Tcl_CancelIdleCall(AfterProc, (ClientData) afterPtr);
+ TclDeleteIdleHandler(AfterInfo2IdleHandler(afterPtr));
}
- Tcl_DecrRefCount(afterPtr->commandPtr);
- ckfree((char *) afterPtr);
}
- ckfree((char *) assocPtr);
}
/*