summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--generic/tclIO.c66
-rw-r--r--generic/tclIO.h5
-rw-r--r--tests/io.test72
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 <andreask@activestate.com>
+ * 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