summaryrefslogtreecommitdiffstats
path: root/generic/tclTimer.c
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 /generic/tclTimer.c
parent6b524c87faf668d9cd21daccf2cdc8aa08957bfa (diff)
downloadtcl-beb9601daa56f0fa2d98122884b79d9bc59e8d1e.zip
tcl-beb9601daa56f0fa2d98122884b79d9bc59e8d1e.tar.gz
tcl-beb9601daa56f0fa2d98122884b79d9bc59e8d1e.tar.bz2
interim commit: code review, rewrite tclTimer, etc.
Diffstat (limited to 'generic/tclTimer.c')
-rw-r--r--generic/tclTimer.c218
1 files changed, 141 insertions, 77 deletions
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));
}
}