summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-07-03 13:31:45 (GMT)
committersebres <sebres@users.sourceforge.net>2017-07-03 13:31:45 (GMT)
commit06f9fcd88b1e4a25a0a4c87a3ed1e7ec2e0ebf31 (patch)
treed7452f7b0f058a76c4739074c702475b3a276a48
parente7a4988c734f8a14028c271d4c6b147cbc6e635a (diff)
downloadtcl-06f9fcd88b1e4a25a0a4c87a3ed1e7ec2e0ebf31.zip
tcl-06f9fcd88b1e4a25a0a4c87a3ed1e7ec2e0ebf31.tar.gz
tcl-06f9fcd88b1e4a25a0a4c87a3ed1e7ec2e0ebf31.tar.bz2
fixed timer-marker handling: timer should be always executed after queued event (of the same generation), it was marked (be sure it marked to immediate execution in corresponding checkProc only).
tclIO: scheduled event rewritten using Tcl_Event instead of timer event (IO is not timer, e. g. executed also by usage of `vwait -notimer ...`, etc).
-rw-r--r--generic/tclIO.c131
-rw-r--r--generic/tclIO.h2
-rw-r--r--generic/tclInt.h3
-rw-r--r--generic/tclNotify.c244
-rw-r--r--generic/tclTimer.c29
-rw-r--r--tests/io.test18
6 files changed, 230 insertions, 197 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c
index aa22698..8d8f211 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -167,8 +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 ChannelScheduledProc(Tcl_Event *evPtr, int flags);
static int ChanRead(Channel *chanPtr, char *dst, int dstSize);
static int CheckChannelErrors(ChannelState *statePtr,
int direction);
@@ -1587,7 +1586,7 @@ Tcl_CreateChannel(
statePtr->interestMask = 0;
statePtr->scriptRecordPtr = NULL;
statePtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
- statePtr->timer = NULL;
+ statePtr->schedEvent = NULL;
statePtr->csPtrR = NULL;
statePtr->csPtrW = NULL;
statePtr->outputStage = NULL;
@@ -2966,12 +2965,12 @@ CloseChannel(
}
/*
- * Cancel any outstanding timer.
+ * Cancel any outstanding scheduled event.
*/
- if (statePtr->timer) {
- TclpDeleteTimerEvent(statePtr->timer);
- statePtr->timer = NULL;
+ if (statePtr->schedEvent) {
+ TclpCancelEvent(statePtr->schedEvent);
+ statePtr->schedEvent = NULL;
}
/*
@@ -3451,12 +3450,12 @@ Tcl_ClearChannelHandlers(
chanPtr = statePtr->topChanPtr;
/*
- * Cancel any outstanding timer.
+ * Cancel any outstanding scheduled event.
*/
- if (statePtr->timer) {
- TclpDeleteTimerEvent(statePtr->timer);
- statePtr->timer = NULL;
+ if (statePtr->schedEvent) {
+ TclpCancelEvent(statePtr->schedEvent);
+ statePtr->schedEvent = NULL;
}
/*
@@ -4391,7 +4390,7 @@ Tcl_GetsObj(
/*
* We didn't get a complete line so we need to indicate to UpdateInterest
* that the gets blocked. It will wait for more data instead of firing a
- * timer, avoiding a busy wait. This is where we are assuming that the
+ * event, avoiding a busy wait. This is where we are assuming that the
* next operation is a gets. No more file events will be delivered on this
* channel until new data arrives or some operation is performed on the
* channel (e.g. gets, read, fconfigure) that changes the blocking state.
@@ -4672,7 +4671,7 @@ TclGetsObjBinary(
/*
* We didn't get a complete line so we need to indicate to UpdateInterest
* that the gets blocked. It will wait for more data instead of firing a
- * timer, avoiding a busy wait. This is where we are assuming that the
+ * event, avoiding a busy wait. This is where we are assuming that the
* next operation is a gets. No more file events will be delivered on this
* channel until new data arrives or some operation is performed on the
* channel (e.g. gets, read, fconfigure) that changes the blocking state.
@@ -7989,7 +7988,7 @@ Tcl_NotifyChannel(
* None.
*
* Side effects:
- * May schedule a timer or driver handler.
+ * May schedule a event or driver handler.
*
*----------------------------------------------------------------------
*/
@@ -8018,7 +8017,7 @@ UpdateInterest(
/*
* If there is data in the input queue, and we aren't waiting for more
- * data, then we need to schedule a timer so we don't block in the
+ * data, then we need to schedule an event so we don't block in the
* notifier. Also, cancel the read interest so we don't get duplicate
* events.
*/
@@ -8047,7 +8046,7 @@ UpdateInterest(
*
* - Tcl drops READABLE here, because it has data in its own
* buffers waiting to be read by the extension.
- * - A READABLE event is syntesized via timer.
+ * - A READABLE event is syntesized via tcl-event (on queue tail).
* - The OS still reports the EXCEPTION condition on the file.
* - And the extension gets the EXCPTION event first, and handles
* this as EOF.
@@ -8069,12 +8068,13 @@ UpdateInterest(
mask &= ~TCL_EXCEPTION;
- if (!statePtr->timer) {
- statePtr->timer = TclpCreatePromptTimerEvent(ChannelTimerProc,
- FreeChannelTimerProc, 0, TCL_TMREV_PROMPT);
- if (statePtr->timer) {
- statePtr->timer->clientData = chanPtr;
- }
+ if (!statePtr->schedEvent) {
+ Tcl_Event *evPtr = (Tcl_Event *)ckalloc(
+ sizeof(Tcl_Event) + sizeof(Channel*));
+ *(Channel**)(evPtr+1) = chanPtr;
+ evPtr->proc = ChannelScheduledProc;
+ statePtr->schedEvent = evPtr;
+ Tcl_QueueEvent(evPtr, TCL_QUEUE_TAIL);
}
}
}
@@ -8084,32 +8084,9 @@ UpdateInterest(
/*
*----------------------------------------------------------------------
*
- * FreeChannelTimerProc --
- *
- * This function simply reset timer in channel state structure.
- * It does *not* cancel the timer (called on execute/delete timer).
- *
- * Results:
- * None.
+ * ChannelScheduledProc --
*
- *----------------------------------------------------------------------
- */
-
-static void
-FreeChannelTimerProc(
- ClientData clientData) /* Channel handle. */
-{
- Channel *chanPtr = clientData;
- ChannelState *statePtr = chanPtr->state;
- statePtr->timer = NULL; /* timer deleted */
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * ChannelTimerProc --
- *
- * Timer handler scheduled by UpdateInterest to monitor the channel
+ * Event handler scheduled by UpdateInterest to monitor the channel
* buffers until they are empty.
*
* Results:
@@ -8121,38 +8098,41 @@ FreeChannelTimerProc(
*----------------------------------------------------------------------
*/
-static void
-ChannelTimerProc(
- ClientData clientData)
+static int
+ChannelScheduledProc(
+ Tcl_Event *evPtr, int flags)
{
- Channel *chanPtr = clientData;
- ChannelState *statePtr = chanPtr->state;
- /* State info for channel */
+ Channel *chanPtr = *(Channel**)(evPtr+1);
+ ChannelState *statePtr; /* State info for channel */
+
+ if (!chanPtr) {
+ return 1;
+ }
+
+ statePtr = chanPtr->state;
if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA)
&& (statePtr->interestMask & TCL_READABLE)
&& (statePtr->inQueueHead != NULL)
&& IsBufferReady(statePtr->inQueueHead)) {
+
/*
- * Prolong the timer in case a channel handler reenters the event loop
+ * Prolong the event in case a channel handler reenters the event loop
* before UpdateInterest gets called by Tcl_NotifyChannel.
*/
- statePtr->timer = TclpProlongTimerEvent(statePtr->timer, 0, TCL_TMREV_PROMPT);
- if (!statePtr->timer) {
- statePtr->timer = TclpCreatePromptTimerEvent(ChannelTimerProc,
- FreeChannelTimerProc, 0, TCL_TMREV_PROMPT);
- if (statePtr->timer) {
- statePtr->timer->clientData = chanPtr;
- }
- }
+ statePtr->schedEvent->proc = ChannelScheduledProc; /* reattach to tail */
+
Tcl_Preserve(statePtr);
Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);
Tcl_Release(statePtr);
- } else {
- statePtr->timer = NULL;
- UpdateInterest(chanPtr);
+
+ return 1;
}
+
+ statePtr->schedEvent = NULL; /* event done. */
+ UpdateInterest(chanPtr);
+ return 1;
}
/*
@@ -8605,9 +8585,9 @@ Tcl_FileEventObjCmd(
/*
*----------------------------------------------------------------------
*
- * ZeroTransferTimerProc --
+ * ZeroTransferEventProc --
*
- * Timer handler scheduled by TclCopyChannel so that -command is
+ * Event handler scheduled by TclCopyChannel so that -command is
* called asynchronously even when -size is 0.
*
* Results:
@@ -8619,14 +8599,17 @@ Tcl_FileEventObjCmd(
*----------------------------------------------------------------------
*/
-static void
-ZeroTransferTimerProc(
- ClientData clientData)
+static int
+ZeroTransferEventProc(
+ Tcl_Event *evPtr, int flags)
{
/* calling CopyData with mask==0 still implies immediate invocation of the
* -command callback, and completion of the fcopy.
*/
+ ClientData clientData = *(ClientData*)(evPtr+1);
CopyData(clientData, 0);
+
+ return 1;
}
/*
@@ -8743,9 +8726,11 @@ TclCopyChannel(
*/
if ((nonBlocking == CHANNEL_NONBLOCKING) && (toRead == 0)) {
- TclTimerEvent *timer = TclpCreatePromptTimerEvent(ZeroTransferTimerProc,
- NULL, 0, TCL_TMREV_PROMPT);
- timer->clientData = csPtr;
+ Tcl_Event *evPtr = (Tcl_Event *)ckalloc(
+ sizeof(Tcl_Event) + sizeof(ClientData*));
+ *(ClientData*)(evPtr+1) = csPtr;
+ evPtr->proc = ZeroTransferEventProc;
+ Tcl_QueueEvent(evPtr, TCL_QUEUE_TAIL);
return 0;
}
diff --git a/generic/tclIO.h b/generic/tclIO.h
index 0b41f54..e779ffc 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? */
- TclTimerEvent *timer; /* Handle to wakeup timer for this channel. */
+ Tcl_Event *schedEvent; /* Scheduler event to wakeup 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 78c56e1..20d89ef 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2995,9 +2995,10 @@ MODULE_SCOPE int Tcl_ConcatObjCmd(ClientData clientData,
MODULE_SCOPE int Tcl_ContinueObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
-MODULE_SCOPE void TclSetTimerEventMarker(int head);
+MODULE_SCOPE void TclSetTimerEventMarker(int flags);
MODULE_SCOPE int TclServiceTimerEvents(void);
MODULE_SCOPE int TclServiceIdleEx(int flags, int count);
+MODULE_SCOPE void TclpCancelEvent(Tcl_Event *evPtr);
MODULE_SCOPE TclTimerEvent* TclpCreateTimerEvent(Tcl_WideInt usec,
Tcl_TimerProc *proc, Tcl_TimerDeleteProc *delProc,
size_t extraDataSize, int flags);
diff --git a/generic/tclNotify.c b/generic/tclNotify.c
index c6978cb..e6480b2 100644
--- a/generic/tclNotify.c
+++ b/generic/tclNotify.c
@@ -496,6 +496,12 @@ QueueEvent(
tsdPtr->lastEventPtr = evPtr;
}
tsdPtr->firstEventPtr = evPtr;
+
+ /* move timer event hereafter */
+ if (tsdPtr->timerMarkerPtr == INT2PTR(-1)) {
+ tsdPtr->timerMarkerPtr = evPtr;
+ }
+
} else if (position == TCL_QUEUE_MARK) {
/*
* Insert the event after the current marker event and advance the
@@ -513,10 +519,45 @@ QueueEvent(
if (evPtr->nextPtr == NULL) {
tsdPtr->lastEventPtr = evPtr;
}
+
+ /* move timer event hereafter */
+ if (tsdPtr->timerMarkerPtr == INT2PTR(-1)) {
+ tsdPtr->timerMarkerPtr = evPtr;
+ }
}
Tcl_MutexUnlock(&(tsdPtr->queueMutex));
}
+static void
+UnlinkEvent(
+ ThreadSpecificData *tsdPtr,
+ Tcl_Event *evPtr,
+ Tcl_Event *prevPtr) {
+ /*
+ * Unlink it.
+ */
+
+ if (prevPtr == NULL) {
+ tsdPtr->firstEventPtr = evPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = evPtr->nextPtr;
+ }
+
+ /*
+ * Update 'last' and 'marker' events if either has been deleted.
+ */
+
+ if (evPtr->nextPtr == NULL) {
+ tsdPtr->lastEventPtr = prevPtr;
+ }
+ if (tsdPtr->markerEventPtr == evPtr) {
+ tsdPtr->markerEventPtr = prevPtr;
+ }
+ if (tsdPtr->timerMarkerPtr == evPtr) {
+ tsdPtr->timerMarkerPtr = prevPtr ? prevPtr : INT2PTR(-1);
+ }
+}
+
/*
*----------------------------------------------------------------------
*
@@ -545,7 +586,6 @@ Tcl_DeleteEvents(
Tcl_Event *prevPtr; /* Pointer to evPtr's predecessor, or NULL if
* evPtr designates the first event in the
* queue for the thread. */
- Tcl_Event* hold;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -559,50 +599,73 @@ Tcl_DeleteEvents(
prevPtr = NULL;
evPtr = tsdPtr->firstEventPtr;
while (evPtr != NULL) {
+ Tcl_Event *nextPtr = evPtr->nextPtr;
if ((*proc)(evPtr, clientData) == 1) {
- /*
- * This event should be deleted. Unlink it.
- */
-
- if (prevPtr == NULL) {
- tsdPtr->firstEventPtr = evPtr->nextPtr;
- } else {
- prevPtr->nextPtr = evPtr->nextPtr;
- }
/*
- * Update 'last' and 'marker' events if either has been deleted.
+ * This event should be deleted. Unlink it.
*/
- if (evPtr->nextPtr == NULL) {
- tsdPtr->lastEventPtr = prevPtr;
- }
- if (tsdPtr->markerEventPtr == evPtr) {
- tsdPtr->markerEventPtr = prevPtr;
- }
- if (tsdPtr->timerMarkerPtr == evPtr) {
- tsdPtr->timerMarkerPtr = prevPtr;
- }
+ UnlinkEvent(tsdPtr, evPtr, prevPtr);
/*
* Delete the event data structure.
*/
- hold = evPtr;
- evPtr = evPtr->nextPtr;
- ckfree((char *) hold);
+ ckfree((char *) evPtr);
} else {
/*
* Event is to be retained.
*/
prevPtr = evPtr;
- evPtr = evPtr->nextPtr;
}
+ evPtr = nextPtr;
}
Tcl_MutexUnlock(&(tsdPtr->queueMutex));
}
+void
+TclpCancelEvent(
+ Tcl_Event *evPtr) /* Event to remove from queue. */
+{
+ Tcl_Event *prevPtr = NULL;
+
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ Tcl_MutexLock(&(tsdPtr->queueMutex));
+
+ /*
+ * Search event to unlink from queue.
+ */
+
+ if (evPtr != tsdPtr->firstEventPtr) {
+ for (prevPtr = tsdPtr->firstEventPtr;
+ prevPtr && prevPtr->nextPtr != evPtr;
+ prevPtr = prevPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ if (!prevPtr) {
+ evPtr = NULL; /* not in queue (already removed) */
+ }
+ }
+
+ if (evPtr) {
+ /*
+ * Unlink it.
+ */
+
+ UnlinkEvent(tsdPtr, evPtr, prevPtr);
+
+ /*
+ * Delete the event data structure.
+ */
+ ckfree((char *) evPtr);
+ }
+
+ Tcl_MutexUnlock(&(tsdPtr->queueMutex));
+}
+
/*
*----------------------------------------------------------------------
*
@@ -632,7 +695,7 @@ Tcl_ServiceEvent(
* matching this will be skipped for
* processing later. */
{
- Tcl_Event *evPtr, *prevPtr;
+ Tcl_Event *evPtr, *prevPtr = NULL;
Tcl_EventProc *proc;
int result;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
@@ -666,16 +729,13 @@ Tcl_ServiceEvent(
* If timer marker reached, process timer events now.
*/
if (flags & TCL_TIMER_EVENTS) { /* timer allowed */
- if (tsdPtr->timerMarkerPtr == INT2PTR(-1)) { /* timer-event reached */
- goto processTimer;
- }
-#if 0
- if ( !tsdPtr->firstEventPtr /* no another events at all */
- || ((flags & TCL_ALL_EVENTS) == TCL_TIMER_EVENTS) /* timers only */
+ if ( tsdPtr->timerMarkerPtr == INT2PTR(-1) /* timer-event reached */
+ || ( tsdPtr->timerMarkerPtr == INT2PTR(-2) /* next cycle, but ... */
+ && ((flags & TCL_ALL_EVENTS) == TCL_TIMER_EVENTS) /* timers only */
+ )
) {
- goto timer;
+ goto processTimer;
}
-#endif
}
/*
@@ -684,9 +744,15 @@ Tcl_ServiceEvent(
*/
Tcl_MutexLock(&(tsdPtr->queueMutex));
- for (evPtr = tsdPtr->firstEventPtr; evPtr != NULL;
- evPtr = evPtr->nextPtr) {
+ for (evPtr = tsdPtr->firstEventPtr;
+ evPtr != NULL && tsdPtr->timerMarkerPtr != INT2PTR(-1);
+ evPtr = evPtr->nextPtr
+ ) {
+ if (tsdPtr->timerMarkerPtr == evPtr) {
+ tsdPtr->timerMarkerPtr = INT2PTR(-1); /* timer marker reached */
+ }
+
/*
* Call the handler for the event. If it actually handles the event
* then free the storage for the event. There are two tricky things
@@ -705,6 +771,7 @@ Tcl_ServiceEvent(
proc = evPtr->proc;
if (proc == NULL) {
+ prevPtr = evPtr;
continue;
}
evPtr->proc = NULL;
@@ -721,41 +788,48 @@ Tcl_ServiceEvent(
Tcl_MutexLock(&(tsdPtr->queueMutex));
if (result) {
+
/*
* The event was processed, so remove it from the queue.
*/
- if (tsdPtr->timerMarkerPtr == evPtr) {
- tsdPtr->timerMarkerPtr = INT2PTR(-1); /* timer marker reached */
- }
- if (tsdPtr->firstEventPtr == evPtr) {
- tsdPtr->firstEventPtr = evPtr->nextPtr;
- if (evPtr->nextPtr == NULL) {
- tsdPtr->lastEventPtr = NULL;
- }
- if (tsdPtr->markerEventPtr == evPtr) {
- tsdPtr->markerEventPtr = NULL;
- }
- } else {
+ prevPtr = NULL;
+ if (evPtr != tsdPtr->firstEventPtr) {
for (prevPtr = tsdPtr->firstEventPtr;
prevPtr && prevPtr->nextPtr != evPtr;
prevPtr = prevPtr->nextPtr) {
/* Empty loop body. */
}
- if (prevPtr) {
- prevPtr->nextPtr = evPtr->nextPtr;
- if (evPtr->nextPtr == NULL) {
- tsdPtr->lastEventPtr = prevPtr;
- }
- if (tsdPtr->markerEventPtr == evPtr) {
- tsdPtr->markerEventPtr = prevPtr;
- }
- } else {
+ if (!prevPtr) {
evPtr = NULL;
}
}
if (evPtr) {
- ckfree((char *) evPtr);
+ /* Detach event from queue */
+ UnlinkEvent(tsdPtr, evPtr, prevPtr);
+
+ /* If wanted to prolong (repeat) */
+ if (evPtr->proc) {
+ /*
+ * Event was restored (prolonged) - sign to reattach to tail
+ */
+ if (evPtr != tsdPtr->lastEventPtr) {
+ /* detach event from queue */
+ UnlinkEvent(tsdPtr, evPtr, prevPtr);
+ /* attach to tail */
+ evPtr->nextPtr = NULL;
+ if (tsdPtr->firstEventPtr == NULL) {
+ tsdPtr->firstEventPtr = evPtr;
+ } else {
+ tsdPtr->lastEventPtr->nextPtr = evPtr;
+ }
+ tsdPtr->lastEventPtr = evPtr;
+ }
+ } else {
+ /* Free event */
+ UnlinkEvent(tsdPtr, evPtr, prevPtr);
+ ckfree((char *) evPtr);
+ }
}
Tcl_MutexUnlock(&(tsdPtr->queueMutex));
return 1;
@@ -775,50 +849,19 @@ Tcl_ServiceEvent(
*/
if (flags & TCL_TIMER_EVENTS) {
-timer:
-#if 1
- /* If available pending timer-events of new generation */
- if (tsdPtr->timerMarkerPtr == INT2PTR(-2)) {
- /* no other events - process timer-events (next cycle) */
- if (!tsdPtr->lastEventPtr) { /* no other events */
- goto processTimer;
- } else {
- tsdPtr->timerMarkerPtr = tsdPtr->lastEventPtr;
- }
- return 0;
- }
-#else
-#if 1
+
/* If available pending timer-events of new generation */
- if ( tsdPtr->timerMarkerPtr == INT2PTR(-2)
- || !tsdPtr->firstEventPtr /* no other events */
- ) {
+ if (tsdPtr->timerMarkerPtr == INT2PTR(-2)) { /* pending */
/* no other events - process timer-events (next cycle) */
- if (tsdPtr->timerMarkerPtr == INT2PTR(-2)) {
+ if (!(tsdPtr->timerMarkerPtr = tsdPtr->lastEventPtr)) { /* no other events */
tsdPtr->timerMarkerPtr = INT2PTR(-1);
- } else {
- tsdPtr->timerMarkerPtr = INT2PTR(-2);
}
return 0;
}
-#else
- /* If available pending timer-events of new generation */
- if ( tsdPtr->timerMarkerPtr == INT2PTR(-2)
- || !tsdPtr->lastEventPtr
- ) {
- /* if other events available */
- if ((tsdPtr->timerMarkerPtr = tsdPtr->lastEventPtr)) {
- /* process timer-events after it (next cycle) */
- return 0;
- }
- /* no other events - process timer-events now */
- goto processTimer;
- }
-#endif
-#endif
+
if (tsdPtr->timerMarkerPtr == INT2PTR(-1)) {
- processTimer:
+ processTimer:
/* reset marker */
tsdPtr->timerMarkerPtr = NULL;
@@ -833,7 +876,7 @@ timer:
/* marker to last event in the queue */
if (!(tsdPtr->timerMarkerPtr = tsdPtr->lastEventPtr)) {
/*
- * Marker as "now" - queue is empty, so timers events are first,
+ * Marker as "pending" - queue is empty, so timers events are first,
* if setup-proc resp. check-proc will not generate new events.
*/
tsdPtr->timerMarkerPtr = INT2PTR(-2);
@@ -882,7 +925,7 @@ TclPeekEventQueued(
*/
if ( Tcl_AsyncReady()
|| (tsdPtr->firstEventPtr)
- || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerMarkerPtr == INT2PTR(-1))
+ || ((flags & TCL_TIMER_EVENTS) && tsdPtr->timerMarkerPtr)
) {
return 1;
}
@@ -943,18 +986,21 @@ TclPeekEventQueued(
void
TclSetTimerEventMarker(
- int head)
+ int flags)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
if (tsdPtr->timerMarkerPtr == NULL || tsdPtr->timerMarkerPtr == INT2PTR(-2)) {
/* marker to last event in the queue */
- if (head || !(tsdPtr->timerMarkerPtr = tsdPtr->lastEventPtr)) {
+ if ( !(tsdPtr->timerMarkerPtr = tsdPtr->lastEventPtr) /* no other events */
+ || ((flags & TCL_ALL_EVENTS) == TCL_TIMER_EVENTS) /* timers only */
+ ) {
/*
- * Marker as "now" - queue is empty, so timers events are first,
+ * Marker as "pending" - queue is empty, so timers events are first,
* if setup-proc resp. check-proc will not generate new events.
+ * Force timer execution if flags specified (from checkProc).
*/
- tsdPtr->timerMarkerPtr = INT2PTR(-2);
+ tsdPtr->timerMarkerPtr = flags ? INT2PTR(-1) : INT2PTR(-2);
};
}
}
diff --git a/generic/tclTimer.c b/generic/tclTimer.c
index bb13c39..9c80332 100644
--- a/generic/tclTimer.c
+++ b/generic/tclTimer.c
@@ -918,7 +918,6 @@ TimerSetupProc(
{
Tcl_Time blockTime;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data;
- Tcl_WideInt entryTime = 0;
if (tsdPtr == NULL) { tsdPtr = InitTimer(); };
@@ -941,7 +940,7 @@ TimerSetupProc(
*/
Tcl_WideInt now = TclpGetMicroseconds();
- entryTime = 0x7FFFFFFFFFFFFFFFL;
+ Tcl_WideInt entryTime = 0x7FFFFFFFFFFFFFFFL;
if (tsdPtr->relTimerList) {
entryTime = TimerGetDueTime(tsdPtr,
@@ -973,14 +972,6 @@ TimerSetupProc(
return;
}
- /*
- * If the first timer has expired, stick an event on the queue right now.
- */
- if (!tsdPtr->timerPending && entryTime <= 0) {
- TclSetTimerEventMarker(0);
- tsdPtr->timerPending = 1;
- }
-
Tcl_SetMaxBlockTime(&blockTime);
}
@@ -1015,20 +1006,18 @@ TimerCheckProc(
if (tsdPtr == NULL) { tsdPtr = InitTimer(); };
- /* If already pending (or no timer-events) */
- if (tsdPtr->timerPending) {
- return;
- }
- if (tsdPtr->promptList) {
+ /* If already pending (or prompt-events) */
+ if (tsdPtr->timerPending || tsdPtr->promptList) {
goto mark;
}
- if (!tsdPtr->relTimerList && !tsdPtr->absTimerList) {
- return;
- }
/*
* Verify the first timer on the queue.
*/
+
+ if (!tsdPtr->relTimerList && !tsdPtr->absTimerList) {
+ return;
+ }
entryTime = 0x7FFFFFFFFFFFFFFFL;
now = TclpGetMicroseconds();
if (tsdPtr->relTimerList) {
@@ -1051,9 +1040,9 @@ TimerCheckProc(
/*
* If the first timer has expired, stick an event on the queue.
*/
- if (!tsdPtr->timerPending && entryTime <= 0) {
+ if (entryTime <= 0) {
mark:
- TclSetTimerEventMarker(0);
+ TclSetTimerEventMarker(flags); /* force timer execution */
tsdPtr->timerPending = 1;
}
}
diff --git a/tests/io.test b/tests/io.test
index 7275b42..c560010 100644
--- a/tests/io.test
+++ b/tests/io.test
@@ -2881,7 +2881,8 @@ test io-29.33 {Tcl_Flush, implicit flush on exit} {exec} {
close $f
set r
} "hello\nbye\nstrange\n"
-test io-29.34 {Tcl_Close, async flush on close, using sockets} {socket tempNotMac fileevent} {
+
+proc io-29.34 {wait} {
variable c 0
variable x running
set l abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
@@ -2910,14 +2911,25 @@ test io-29.34 {Tcl_Close, async flush on close, using sockets} {socket tempNotMa
}
set ss [socket -server [namespace code accept] -myaddr 127.0.0.1 0]
set cs [socket 127.0.0.1 [lindex [fconfigure $ss -sockname] 2]]
- vwait [namespace which -variable x]
+ {*}$wait [namespace which -variable x]
+ if {$x ne "accepted"} {error "timeout, state: $x"}
fconfigure $cs -blocking off
writelots $cs $l
close $cs
close $ss
- vwait [namespace which -variable x]
+ {*}$wait [namespace which -variable x]
+ if {$x ne "done"} {error "timeout, state: $x"}
set c
+}
+test io-29.34.1 {Tcl_Close, async flush on close, using sockets} {socket tempNotMac fileevent} {
+ io-29.34 {vwait 5000}
} 9000
+
+test io-29.34.2 {same as io-29.34 / IO without timer events} {socket tempNotMac fileevent} {
+ # this test will fail if IO uses timer event for scheduled execution.
+ io-29.34 {vwait -notimer 5000}
+} 9000
+
test io-29.35 {Tcl_Close vs fileevent vs multiple interpreters} {socket tempNotMac fileevent} {
# On Mac, this test screws up sockets such that subsequent tests using port 2828
# either cause errors or panic().