From f22c720323ae972ba91834691c7c0ad1b800d2df Mon Sep 17 00:00:00 2001
From: andreas_kupries <akupries@shaw.ca>
Date: Thu, 3 Apr 2008 18:06:23 +0000
Subject: 	* generic/tclIO.c (CopyData): Applied patch [Bug 1932639] to 
 * tests/io.test: prevent fcopy from calling -command synchronously 	*
 tests/chanio.test: the first time. Thanks to Alexandre Ferrieux 	 
 <ferrieux@users.sourceforge.net> for report and patch.

---
 ChangeLog         |  7 +++++++
 generic/tclIO.c   | 38 ++++++++++++++++++++++++--------------
 tests/chanio.test | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
 tests/io.test     | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 123 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f78f29c..5d06fc1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2008-04-03  Andreas Kupries  <andreask@activestate.com>
+
+	* generic/tclIO.c (CopyData): Applied patch [Bug 1932639] to
+	* tests/io.test: prevent fcopy from calling -command synchronously
+	* tests/chanio.test: the first time. Thanks to Alexandre Ferrieux
+	  <ferrieux@users.sourceforge.net> for report and patch.
+
 2008-04-02  Andreas Kupries  <andreask@activestate.com>
 
 	* generic/tclIO.c (CopyData): Applied patch for the fcopy problem
diff --git a/generic/tclIO.c b/generic/tclIO.c
index 8222681..eadd56b 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.137.2.1 2008/04/02 20:27:08 andreas_kupries Exp $
+ * RCS: @(#) $Id: tclIO.c,v 1.137.2.2 2008/04/03 18:06:24 andreas_kupries Exp $
  */
 
 #include "tclInt.h"
@@ -8578,23 +8578,33 @@ CopyData(
 	    goto writeError;
 	}
 
-	/*
-	 * Read up to bufSize bytes.
-	 */
+	if (cmdPtr && (mask == 0)) {
+	    /*
+	     * In async mode, we skip reading synchronously and fake an
+	     * underflow instead to prime the readable fileevent.
+	     */
 
-	if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) {
-	    sizeb = csPtr->bufSize;
+	    size      = 0;
+	    underflow = 1;
 	} else {
-	    sizeb = csPtr->toRead;
-	}
+	    /*
+	     * Read up to bufSize bytes.
+	     */
 
-	if (inBinary || sameEncoding) {
-	    size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb);
-	} else {
-	    size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb,
-		    0 /* No append */);
+	    if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) {
+		sizeb = csPtr->bufSize;
+	    } else {
+		sizeb = csPtr->toRead;
+	    }
+
+	    if (inBinary || sameEncoding) {
+		size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb);
+	    } else {
+		size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb,
+				   0 /* No append */);
+	    }
+	    underflow = (size >= 0) && (size < sizeb);	/* Input underflow */
 	}
-	underflow = (size >= 0) && (size < sizeb);	/* Input underflow */
 
 	if (size < 0) {
 	readError:
diff --git a/tests/chanio.test b/tests/chanio.test
index 3c56758..c2a6940 100644
--- a/tests/chanio.test
+++ b/tests/chanio.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: chanio.test,v 1.3 2007/12/13 15:26:04 dgp Exp $
+# RCS: @(#) $Id: chanio.test,v 1.3.2.1 2008/04/03 18:06:26 andreas_kupries Exp $
 
 if {[catch {package require tcltest 2}]} {
     chan puts stderr "Skipping tests in [info script].  tcltest 2 required."
@@ -6887,6 +6887,51 @@ test chan-io-53.7 {CopyData: Flooding chan copy from pipe} {stdio openpipe fcopy
     # -1=error 0=script error N=number of bytes
     expr ($fcopyTestDone == 0) ? $fcopyTestCount : -1
 } {3450}
+test chan-io-53.8 {CopyData: async callback and error handling, Bug 1932639} -setup {
+    # copy progress callback. errors out intentionally
+    proc ::cmd args {
+	lappend ::RES "CMD $args"
+	error !STOP
+    }
+    # capture callback error here
+    proc ::bgerror args {
+	lappend ::RES "bgerror/OK $args"
+	set ::forever has-been-reached
+	return
+    }
+    # Files we use for our channels
+    set foo [makeFile ashgdfashdgfasdhgfasdhgf foo]
+    set bar [makeFile {} bar]
+    # Channels to copy between
+    set f [open $foo r] ; fconfigure $f -translation binary
+    set g [open $bar w] ; fconfigure $g -translation binary -buffering none
+} -constraints {stdio openpipe fcopy} -body {
+    # Record input size, so that result is always defined
+    lappend ::RES [file size $bar]
+    # Run the copy. Should not invoke -command now.
+    chan copy $f $g -size 2 -command ::cmd
+    # Check that -command was not called synchronously
+    set sbs [file size bar]
+    lappend ::RES [expr {($sbs > 0) ? "sync/FAIL" : "sync/OK"}] $sbs
+    # Now let the async part happen. Should capture the error in cmd
+    # via bgerror. If not break the event loop via timer.
+    after 1000 {
+	lappend ::RES {bgerror/FAIL timeout}
+	set ::forever has-been-reached
+    }
+    vwait ::forever
+    # Report
+    set ::RES
+} -cleanup {
+    chan close $f
+    chan close $g
+    catch {unset ::RES}
+    catch {unset ::forever}
+    rename ::cmd {}
+    rename ::bgerror {}
+    removeFile foo
+    removeFile bar
+} -result {0 sync/OK 0 {CMD 2} {bgerror/OK !STOP}}
 
 test chan-io-54.1 {Recursive channel events} {socket fileevent} {
     # This test checks to see if file events are delivered during recursive
diff --git a/tests/io.test b/tests/io.test
index 3ca2239..86ccddc 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.80 2007/12/13 15:26:06 dgp Exp $
+# RCS: @(#) $Id: io.test,v 1.80.2.1 2008/04/03 18:06:25 andreas_kupries Exp $
 
 if {[catch {package require tcltest 2}]} {
     puts stderr "Skipping tests in [info script].  tcltest 2 required."
@@ -6887,6 +6887,51 @@ test io-53.7 {CopyData: Flooding fcopy from pipe} {stdio openpipe fcopy} {
     # -1=error 0=script error N=number of bytes
     expr ($fcopyTestDone == 0) ? $fcopyTestCount : -1
 } {3450}
+test io-53.8 {CopyData: async callback and error handling, Bug 1932639} -setup {
+    # copy progress callback. errors out intentionally
+    proc ::cmd args {
+	lappend ::RES "CMD $args"
+	error !STOP
+    }
+    # capture callback error here
+    proc ::bgerror args {
+	lappend ::RES "bgerror/OK $args"
+	set ::forever has-been-reached
+	return
+    }
+    # Files we use for our channels
+    set foo [makeFile ashgdfashdgfasdhgfasdhgf foo]
+    set bar [makeFile {} bar]
+    # Channels to copy between
+    set f [open $foo r] ; fconfigure $f -translation binary
+    set g [open $bar w] ; fconfigure $g -translation binary -buffering none
+} -constraints {stdio openpipe fcopy} -body {
+    # Record input size, so that result is always defined
+    lappend ::RES [file size $bar]
+    # Run the copy. Should not invoke -command now.
+    fcopy $f $g -size 2 -command ::cmd
+    # Check that -command was not called synchronously
+    set sbs [file size bar]
+    lappend ::RES [expr {($sbs > 0) ? "sync/FAIL" : "sync/OK"}] $sbs
+    # Now let the async part happen. Should capture the error in cmd
+    # via bgerror. If not break the event loop via timer.
+    after 1000 {
+	lappend ::RES {bgerror/FAIL timeout}
+	set ::forever has-been-reached
+    }
+    vwait ::forever
+    # Report
+    set ::RES
+} -cleanup {
+    close $f
+    close $g
+    catch {unset ::RES}
+    catch {unset ::forever}
+    rename ::cmd {}
+    rename ::bgerror {}
+    removeFile foo
+    removeFile bar
+} -result {0 sync/OK 0 {CMD 2} {bgerror/OK !STOP}}
 
 test io-54.1 {Recursive channel events} {socket fileevent} {
     # This test checks to see if file events are delivered during recursive
-- 
cgit v0.12

