From 56497ff7346fa776215fc25fd154ee60d5422e2b Mon Sep 17 00:00:00 2001 From: andreas_kupries Date: Mon, 7 Apr 2008 22:17:36 +0000 Subject: * tests/io.test (io-53.10): Testcase for bi-directionaly fcopy. * generic/tclIO.c: Additional changes to data structures for fcopy * generic/tclIO.h: and channels to perform proper cleanup in case of a channel having two background copy operations running as is now possible. --- ChangeLog | 6 +++++ generic/tclIO.c | 66 ++++++++++++++++++++++++++++++---------------------- generic/tclIO.h | 5 ++-- tests/io.test | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6c5be07..2a6f378 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2008-04-07 Andreas Kupries + * tests/io.test (io-53.10): Testcase for bi-directionaly fcopy. + * generic/tclIO.c: Additional changes to data structures for fcopy + * generic/tclIO.h: and channels to perform proper cleanup in case + of a channel having two background copy operations running as is + now possible. + * generic/tclIO.c (BUSY_STATE, CheckChannelErrors, TclCopyChannel): New macro, and the places using it. This change allows for bi-directional fcopy on channels. [Bug 1350564]. diff --git a/generic/tclIO.c b/generic/tclIO.c index 739b644..3f79021 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIO.c,v 1.61.2.26 2008/04/07 19:40:58 andreas_kupries Exp $ + * RCS: @(#) $Id: tclIO.c,v 1.61.2.27 2008/04/07 22:17:37 andreas_kupries Exp $ */ #include "tclInt.h" @@ -157,9 +157,8 @@ static int WriteChars _ANSI_ARGS_((Channel *chanPtr, CONST char *src, int srcLen)); #define BUSY_STATE(st,fl) \ - ((st)->csPtr && \ - ( (((fl)&TCL_READABLE)&&((st)->csPtr->readPtr ==(st)->topChanPtr)) || \ - (((fl)&TCL_WRITABLE)&&((st)->csPtr->writePtr==(st)->topChanPtr)))) + ((((st)->csPtrR) && ((fl) & TCL_READABLE)) || \ + (((st)->csPtrW) && ((fl) & TCL_WRITABLE))) /* *--------------------------------------------------------------------------- @@ -1218,7 +1217,8 @@ Tcl_CreateChannel(typePtr, chanName, instanceData, mask) statePtr->scriptRecordPtr = (EventScriptRecord *) NULL; statePtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE; statePtr->timer = NULL; - statePtr->csPtr = NULL; + statePtr->csPtrR = NULL; + statePtr->csPtrW = NULL; statePtr->outputStage = NULL; if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) { @@ -1374,13 +1374,18 @@ Tcl_StackChannel(interp, typePtr, instanceData, mask, prevChan) */ if ((mask & TCL_WRITABLE) != 0) { - CopyState *csPtr; + CopyState *csPtrR; + CopyState *csPtrW; - csPtr = statePtr->csPtr; - statePtr->csPtr = (CopyState*) NULL; + csPtrR = statePtr->csPtrR; + statePtr->csPtrR = (CopyState*) NULL; + + csPtrW = statePtr->csPtrW; + statePtr->csPtrW = (CopyState*) NULL; if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) { - statePtr->csPtr = csPtr; + statePtr->csPtrR = csPtrR; + statePtr->csPtrW = csPtrW; if (interp) { Tcl_AppendResult(interp, "could not flush channel \"", Tcl_GetChannelName(prevChan), "\"", (char *) NULL); @@ -1388,7 +1393,8 @@ Tcl_StackChannel(interp, typePtr, instanceData, mask, prevChan) return (Tcl_Channel) NULL; } - statePtr->csPtr = csPtr; + statePtr->csPtrR = csPtrR; + statePtr->csPtrW = csPtrW; } /* * Discard any input in the buffers. They are not yet read by the @@ -1503,13 +1509,18 @@ Tcl_UnstackChannel (interp, chan) */ if (statePtr->flags & TCL_WRITABLE) { - CopyState* csPtr; + CopyState *csPtrR; + CopyState *csPtrW; + + csPtrR = statePtr->csPtrR; + statePtr->csPtrR = (CopyState*) NULL; - csPtr = statePtr->csPtr; - statePtr->csPtr = (CopyState*) NULL; + csPtrW = statePtr->csPtrW; + statePtr->csPtrW = (CopyState*) NULL; if (Tcl_Flush((Tcl_Channel) chanPtr) != TCL_OK) { - statePtr->csPtr = csPtr; + statePtr->csPtrR = csPtrR; + statePtr->csPtrW = csPtrW; if (interp) { Tcl_AppendResult(interp, "could not flush channel \"", Tcl_GetChannelName((Tcl_Channel) chanPtr), "\"", @@ -1518,7 +1529,8 @@ Tcl_UnstackChannel (interp, chan) return TCL_ERROR; } - statePtr->csPtr = csPtr; + statePtr->csPtrR = csPtrR; + statePtr->csPtrW = csPtrW; } /* @@ -2739,7 +2751,8 @@ Tcl_ClearChannelHandlers (channel) * Cancel any pending copy operation. */ - StopCopy(statePtr->csPtr); + StopCopy(statePtr->csPtrR); + StopCopy(statePtr->csPtrW); /* * Must set the interest mask now to 0, otherwise infinite loops @@ -6319,12 +6332,10 @@ Tcl_GetChannelOption(interp, chan, optionName, dsPtr) * If we are in the middle of a background copy, use the saved flags. */ - if (statePtr->csPtr) { - if (chanPtr == statePtr->csPtr->readPtr) { - flags = statePtr->csPtr->readFlags; - } else { - flags = statePtr->csPtr->writeFlags; - } + if (statePtr->csPtrR) { + flags = statePtr->csPtrR->readFlags; + } else if (statePtr->csPtrW) { + flags = statePtr->csPtrW->writeFlags; } else { flags = statePtr->flags; } @@ -6534,7 +6545,7 @@ Tcl_SetChannelOption(interp, chan, optionName, newValue) * If the channel is in the middle of a background copy, fail. */ - if (statePtr->csPtr) { + if (statePtr->csPtrR || statePtr->csPtrW) { if (interp) { Tcl_AppendResult(interp, "unable to set channel options: background copy in progress", @@ -7757,8 +7768,9 @@ TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr) Tcl_IncrRefCount(cmdPtr); } csPtr->cmdPtr = cmdPtr; - inStatePtr->csPtr = csPtr; - outStatePtr->csPtr = csPtr; + + inStatePtr->csPtrR = csPtr; + outStatePtr->csPtrW = csPtr; /* * Start copying data between the channels. @@ -8698,8 +8710,8 @@ StopCopy(csPtr) } Tcl_DecrRefCount(csPtr->cmdPtr); } - inStatePtr->csPtr = NULL; - outStatePtr->csPtr = NULL; + inStatePtr->csPtrR = NULL; + outStatePtr->csPtrW = NULL; ckfree((char*) csPtr); } diff --git a/generic/tclIO.h b/generic/tclIO.h index ea3c6e4..4564f32 100644 --- a/generic/tclIO.h +++ b/generic/tclIO.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIO.h,v 1.5.4.2 2004/07/15 20:46:19 andreas_kupries Exp $ + * RCS: @(#) $Id: tclIO.h,v 1.5.4.3 2008/04/07 22:17:37 andreas_kupries Exp $ */ /* @@ -225,7 +225,8 @@ typedef struct ChannelState { int bufSize; /* What size buffers to allocate? */ Tcl_TimerToken timer; /* Handle to wakeup timer for this channel. */ - CopyState *csPtr; /* State of background copy, or NULL. */ + CopyState *csPtrR; /* State of background copy for which channel is input, or NULL. */ + CopyState *csPtrW; /* State of background copy for which channel is output, or NULL. */ Channel *topChanPtr; /* Refers to topmost channel in a stack. * Never NULL. */ Channel *bottomChanPtr; /* Refers to bottommost channel in a stack. diff --git a/tests/io.test b/tests/io.test index 696366b..f59226d 100644 --- a/tests/io.test +++ b/tests/io.test @@ -13,7 +13,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: io.test,v 1.40.2.15 2008/04/07 19:41:00 andreas_kupries Exp $ +# RCS: @(#) $Id: io.test,v 1.40.2.16 2008/04/07 22:17:37 andreas_kupries Exp $ if {[catch {package require tcltest 2}]} { puts stderr "Skipping tests in [info script]. tcltest 2 required." @@ -7013,6 +7013,76 @@ test io-53.9 {CopyData: -size and event interaction, Bug 780533} -setup { removeFile err catch {unset ::forever} } -result OK +test io-53.10 {Bug 1350564, multi-directional fcopy} -setup { + set err [makeFile {} err] + set pipe [open "|[info nameofexecutable] 2> $err" r+] + fconfigure $pipe -translation binary -buffering line + puts $pipe { + fconfigure stderr -buffering line + # Kill server when pipe closed by invoker. + proc bye args { + if {![eof stdin]} { gets stdin ; return } + puts stderr BYE + exit + } + # Server code. Bi-directional copy between 2 sockets. + proc geof {sok} { + puts stderr DONE/$sok + close $sok + } + proc new {sok args} { + puts stderr NEW/$sok + global l srv + fconfigure $sok -translation binary -buffering none + lappend l $sok + if {[llength $l]==2} { + close $srv + foreach {a b} $l break + fcopy $a $b -command [list geof $a] + fcopy $b $a -command [list geof $b] + puts stderr 2COPY + } + puts stderr ... + } + puts stderr SRV + set l {} + set srv [socket -server new 9999] + puts stderr WAITING + fileevent stdin readable bye + puts OK + vwait forever + } + # wait for OK from server. + gets $pipe + # Now the two clients. + proc ::done {sock} { + if {[eof $sock]} { close $sock ; return } + lappend ::forever [gets $sock] + return + } + set a [socket 127.0.0.1 9999] + set b [socket 127.0.0.1 9999] + fconfigure $a -translation binary -buffering none + fconfigure $b -translation binary -buffering none + fileevent $a readable [list ::done $a] + fileevent $b readable [list ::done $b] +} -constraints {stdio openpipe fcopy} -body { + # Now pass data through the server in both directions. + set ::forever {} + puts $a AB + vwait ::forever + puts $b BA + vwait ::forever + set ::forever +} -cleanup { + catch {close $a} + catch {close $b} + close $pipe + rename ::done {} + after 1000 ;# Give Windows time to kill the process + removeFile err + catch {unset ::forever} +} -result {AB BA} test io-54.1 {Recursive channel events} {socket fileevent} { # This test checks to see if file events are delivered during recursive -- cgit v0.12