From 39e076480d96baa096f628753c88b68eb9d7f601 Mon Sep 17 00:00:00 2001
From: ferrieux <ferrieux@users.sourceforge.net>
Date: Sat, 28 Apr 2012 17:03:10 +0000
Subject: Compat flag, test, and doc update.

---
 doc/close.n     |  6 ++++--
 generic/tclIO.c | 32 +++++++++++++++++++++++++++++---
 tests/io.test   | 21 ++++++++++++++++++++-
 3 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/doc/close.n b/doc/close.n
index 4490f6a..2826d82 100644
--- a/doc/close.n
+++ b/doc/close.n
@@ -48,8 +48,10 @@ When the last interpreter in which the channel is registered invokes
 \fBinterp\fR command for a description of channel sharing.
 .PP
 Channels are automatically closed when an interpreter is destroyed and
-when the process exits.  Channels are switched to blocking mode, to ensure
-that all output is correctly flushed before the process exits.
+when the process exits.
+.VS 8.6
+From 8.6 on (TIP#398), nonblocking channels are no longer switched to blocking mode when exiting; this guarantees a timely exit even when the peer or a communication channel is stalled. To ensure proper flushing of stalled nonblocking channels on exit, one must now either (a) actively switch them back to blocking or (b) use the environment variable TCL_FLUSH_NONBLOCKING_ON_EXIT,  which when set and not equal to "0" restores the previous behavior.
+.VE 8.6
 .PP
 The command returns an empty string, and may generate an error if
 an error occurs while flushing output.  If a command in a command
diff --git a/generic/tclIO.c b/generic/tclIO.c
index e1e1193..527ae0c 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -396,6 +396,19 @@ TclFinalizeIOSubsystem(void)
     Channel *chanPtr = NULL;	/* Iterates over open channels. */
     ChannelState *statePtr;	/* State of channel stack */
     int active = 1;		/* Flag == 1 while there's still work to do */
+    int doflushnb;
+
+    /* Fetch the pre-TIP#398 compatibility flag */ 
+    {
+        const char *s;
+        Tcl_DString ds;
+        
+        s = TclGetEnv("TCL_FLUSH_NONBLOCKING_ON_EXIT", &ds);
+        doflushnb = ((s != NULL) && strcmp(s, "0"));
+        if (s != NULL) {
+            Tcl_DStringFree(&ds);
+        }
+    }
 
     /*
      * Walk all channel state structures known to this thread and close
@@ -414,8 +427,8 @@ TclFinalizeIOSubsystem(void)
 		statePtr != NULL;
 		statePtr = statePtr->nextCSPtr) {
 	    chanPtr = statePtr->topChanPtr;
-	    if (!GotFlag(statePtr, CHANNEL_INCLOSE | CHANNEL_CLOSED |
-		    CHANNEL_DEAD)) {
+	    if (!GotFlag(statePtr, CHANNEL_INCLOSE | CHANNEL_CLOSED | CHANNEL_DEAD)
+                || (doflushnb && GotFlag(statePtr, BG_FLUSH_SCHEDULED))) {
 		active = 1;
 		break;
 	    }
@@ -426,9 +439,21 @@ TclFinalizeIOSubsystem(void)
 	 */
 
 	if (active) {
+
 	    /*
-	     * TIP #398: we no longer set the channel back into blocking mode
+	     * TIP #398:  by default, we no  longer set the  channel back into
+             * blocking  mode.  To  restore  the old  blocking  behavior,  the
+             * environment variable  TCL_FLUSH_NONBLOCKING_ON_EXIT must be set
+             * and not be "0".
 	     */
+            if (doflushnb) {
+                    /* Set the channel back into blocking mode to ensure that we wait
+                     * for all data to flush out.
+                     */
+                
+                (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
+                                            "-blocking", "on");                    
+            }
 
 	    if ((chanPtr == (Channel *) tsdPtr->stdinChannel) ||
 		    (chanPtr == (Channel *) tsdPtr->stdoutChannel) ||
@@ -454,6 +479,7 @@ TclFinalizeIOSubsystem(void)
 		 * The refcount is greater than zero, so flush the channel.
 		 */
 
+                ResetFlag(statePtr, BG_FLUSH_SCHEDULED);
 		Tcl_Flush((Tcl_Channel) chanPtr);
 
 		/*
diff --git a/tests/io.test b/tests/io.test
index 53b85fa..74a246c 100644
--- a/tests/io.test
+++ b/tests/io.test
@@ -2736,6 +2736,25 @@ test io-29.33 {Tcl_Flush, implicit flush on exit} {exec} {
     close $f
     set r
 } "hello\nbye\nstrange\n"
+set path(script2) [makeFile {} script2]
+test io-29.33b {TIP#398, no implicit flush of nonblocking on exit} {exec} {
+    set f [open $path(script) w]
+    puts $f {
+		fconfigure stdout -blocking 0
+		puts -nonewline stdout [string repeat A 655360]
+		flush stdout
+	}
+    close $f
+    set f [open $path(script2) w]
+    puts $f {after 2000}
+    close $f
+	set t1 [clock seconds]
+	set ff [open "|[list [interpreter] $path(script2)]" w]
+	exec [interpreter] $path(script) >@ $ff
+	set t2 [clock seconds]
+	close $ff
+	expr {($t2-$t1)/2}
+} 0
 test io-29.34 {Tcl_Close, async flush on close, using sockets} {socket tempNotMac fileevent} {
     variable c 0
     variable x running
@@ -7761,7 +7780,7 @@ test io-73.2 {channel Tcl_Obj SetChannelFromAny, bug 2407783} -setup {
 # ### ### ### ######### ######### #########
 
 # cleanup
-foreach file [list fooBar longfile script output test1 pipe my_script \
+foreach file [list fooBar longfile script script2 output test1 pipe my_script \
 	test2 test3 cat stdout kyrillic.txt utf8-fcopy.txt utf8-rp.txt] {
     removeFile $file
 }
-- 
cgit v0.12

