Index: generic/tclIO.c ================================================================== --- generic/tclIO.c +++ generic/tclIO.c @@ -1705,11 +1705,10 @@ statePtr->inQueueTail = NULL; statePtr->chPtr = NULL; statePtr->interestMask = 0; statePtr->scriptRecordPtr = NULL; statePtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE; - statePtr->timer = NULL; statePtr->timerChanPtr = NULL; statePtr->csPtrR = NULL; statePtr->csPtrW = NULL; statePtr->outputStage = NULL; @@ -8698,28 +8697,25 @@ * testsuite on all of them. */ mask &= ~TCL_EXCEPTION; - if (!statePtr->timer) { + if (!statePtr->timerChanPtr) { TclChannelPreserve((Tcl_Channel)chanPtr); statePtr->timerChanPtr = chanPtr; - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc, chanPtr); + Tcl_DoWhenIdle(ChannelTimerProc, chanPtr); } } } - if (!statePtr->timer + if (!statePtr->timerChanPtr && mask & TCL_WRITABLE && GotFlag(statePtr, CHANNEL_NONBLOCKING)) { TclChannelPreserve((Tcl_Channel)chanPtr); statePtr->timerChanPtr = chanPtr; - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc,chanPtr); + Tcl_DoWhenIdle(ChannelTimerProc,chanPtr); } - ChanWatch(chanPtr, mask); } /* @@ -8755,20 +8751,18 @@ if (chanPtr->typePtr == NULL) { CleanupTimerHandler(statePtr); } else { Tcl_Preserve(statePtr); - statePtr->timer = NULL; if (statePtr->interestMask & TCL_WRITABLE && GotFlag(statePtr, CHANNEL_NONBLOCKING) && !GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { /* * Restart the timer in case a channel handler reenters the event loop * before UpdateInterest gets called by Tcl_NotifyChannel. */ - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc,chanPtr); + Tcl_DoWhenIdle(ChannelTimerProc,chanPtr); Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_WRITABLE); } else { /* The channel may have just been closed from within Tcl_NotifyChannel */ if (!GotFlag(statePtr, CHANNEL_INCLOSE)) { if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA) @@ -8778,12 +8772,11 @@ /* * Restart the timer in case a channel handler reenters the event loop * before UpdateInterest gets called by Tcl_NotifyChannel. */ - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc,chanPtr); + Tcl_DoWhenIdle(ChannelTimerProc,chanPtr); Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); } else { CleanupTimerHandler(statePtr); UpdateInterest(chanPtr); } @@ -8798,21 +8791,20 @@ static void DeleteTimerHandler( ChannelState *statePtr ) { - if (statePtr->timer != NULL) { - Tcl_DeleteTimerHandler(statePtr->timer); + if (statePtr->timerChanPtr != NULL) { + Tcl_CancelIdleCall(ChannelTimerProc,statePtr->timerChanPtr); CleanupTimerHandler(statePtr); } } static void CleanupTimerHandler( ChannelState *statePtr ){ TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr); - statePtr->timer = NULL; statePtr->timerChanPtr = NULL; } /* *---------------------------------------------------------------------- Index: generic/tclIO.h ================================================================== --- generic/tclIO.h +++ generic/tclIO.h @@ -187,11 +187,10 @@ * handlers for. */ EventScriptRecord *scriptRecordPtr; /* Chain of all scripts registered for event * handlers ("fileevent") on this channel. */ Tcl_Size bufSize; /* What size buffers to allocate? */ - Tcl_TimerToken timer; /* Handle to wakeup timer for this channel. */ Channel *timerChanPtr; /* Needed in order to decrement the refCount of the right channel when the timer is deleted. */ struct CopyState *csPtrR; /* State of background copy for which channel * is input, or NULL. */ Index: tests/io.test ================================================================== --- tests/io.test +++ tests/io.test @@ -6235,11 +6235,11 @@ } -result {initial foo eof} close $f test io-44.6 {FileEventProc procedure: write-only non-blocking channel} -setup { -} -constraints {stdio fileevent openpipe} -body { +} -constraints {stdio fileevent} -body { namespace eval refchan { namespace ensemble create namespace export * @@ -6286,10 +6286,87 @@ if {$count > 262144} { chan event $f writable {} set x done } }] + set token [after 10000 [namespace code { + set x timeout + }]] + vwait [namespace which -variable x] + return $x +} -cleanup { + after cancel $token + catch {chan close $f} +} -result done + + +test io-44.7 { + FileEventProc procedure: write-only non-blocking channel postevent idle +} -setup { +} -constraints {stdio fileevent} -body { + + namespace eval refchan2 { + namespace ensemble create + namespace export * + + proc finalize {chan args} { + namespace delete c_$chan + } + + proc initialize {chan args} { + namespace eval c_$chan {} + namespace upvar c_$chan watching watching + set watching {} + list finalize initialize seek watch write + } + + proc watch {chan args} { + namespace upvar c_$chan watching watching + foreach arg $args { + switch $arg { + write { + if {$arg ni $watching} { + lappend watching $arg + } + after idle after 0 chan postevent $chan $arg + } + } + } + } + + proc write {chan args} { + incr ::counter + after idle after 0 chan postevent $chan write + return 1 + } + } + + set f [chan create w [namespace which refchan2]] + chan configure $f -blocking 0 + chan event $f writable [namespace code { + puts $f X + }] + + variable x {} + + apply [list {} { + set script { + set lambda [list ::apply [list script { + variable count + variable x + if {[incr count] < 1000} { + try $script + } else { + set x done + } + } [namespace current]] $script] + after idle [list after 0 $lambda] + } + try $script + } [namespace current]] + + set token [after 10000 [namespace code { set x timeout }]] vwait [namespace which -variable x] return $x