diff options
author | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:31:45 (GMT) |
---|---|---|
committer | sebres <sebres@users.sourceforge.net> | 2017-07-03 13:31:45 (GMT) |
commit | 06f9fcd88b1e4a25a0a4c87a3ed1e7ec2e0ebf31 (patch) | |
tree | d7452f7b0f058a76c4739074c702475b3a276a48 | |
parent | e7a4988c734f8a14028c271d4c6b147cbc6e635a (diff) | |
download | tcl-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.c | 131 | ||||
-rw-r--r-- | generic/tclIO.h | 2 | ||||
-rw-r--r-- | generic/tclInt.h | 3 | ||||
-rw-r--r-- | generic/tclNotify.c | 244 | ||||
-rw-r--r-- | generic/tclTimer.c | 29 | ||||
-rw-r--r-- | tests/io.test | 18 |
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(). |