Subject: * Rework WaitForConnect() to fix synchronous completion of asynchronous connections. * Let TcpInputProc() and TcpOutputProc() fail before calling any I/O syscalls when an asynchronous connection has failed. * Adjust the tests accordingly.

From: max <[email-masked]>
Date: Fri, 4 Apr 2014 10:02:47 +0000

---
 tests/socket.test  | 13 ++++++++-----
 unix/tclUnixSock.c | 54 ++++++++++++++++++++++++------------------------------
 2 files changed, 32 insertions(+), 35 deletions(-)

diff --git a/tests/socket.test b/tests/socket.test
index 61660cd..4fba2c3 100644
--- a/tests/socket.test
+++ b/tests/socket.test
@@ -1993,7 +1993,7 @@ test socket-14.7.2 {pending [socket -async] and blocking [gets], no listener} \
         list $x [fconfigure $sock -error]
     } -cleanup {
         close $sock
-    } -match glob -result {{error reading "sock*": socket is not connected} {connection refused}}
+    } -match glob -result {{error reading "sock*": connection refused} {}}
 test socket-14.8.0 {pending [socket -async] and nonblocking [gets], server is IPv4} \
     -constraints {socket supported_inet supported_inet6} \
     -setup {
@@ -2055,10 +2055,10 @@ test socket-14.8.2 {pending [socket -async] and nonblocking [gets], no listener}
             if {[catch {gets $sock} x] || $x ne "" || ![fblocked $sock]} break
             after 200
         }
-        fconfigure $sock -error
+        list $x [fconfigure $sock -error]
     } -cleanup {
         close $sock
-    } -match glob -result {connection refused}
+    } -match glob -result {{error reading "sock*": connection refused} {}}
 test socket-14.9.0 {pending [socket -async] and blocking [puts], server is IPv4} \
     -constraints {socket supported_inet supported_inet6} \
     -setup {
@@ -2171,7 +2171,9 @@ test socket-14.11.0 {pending [socket -async] and blocking [puts], no listener, n
         vwait x
         close $sock
     } -cleanup {
-    } -result {broken pipe} -returnCodes 1
+        catch {close $sock}
+        unset x
+    } -result {connection refused} -returnCodes 1
 test socket-14.11.1 {pending [socket -async] and blocking [puts], no listener, flush} \
     -constraints {socket supported_inet supported_inet6} \
     -body {
@@ -2183,8 +2185,9 @@ test socket-14.11.1 {pending [socket -async] and blocking [puts], no listener, f
         vwait x
         close $sock
     } -cleanup {
+        catch {close $sock}
         unset x
-    } -result {broken pipe} -returnCodes 1
+    } -result {connection refused} -returnCodes 1
 test socket-14.12 {[socket -async] background progress triggered by [fconfigure -error]} \
     -constraints {socket supported_inet supported_inet6} \
     -body {
diff --git a/unix/tclUnixSock.c b/unix/tclUnixSock.c
index 466b231..d4b7b62 100644
--- a/unix/tclUnixSock.c
+++ b/unix/tclUnixSock.c
@@ -128,8 +128,7 @@ static int		TcpInputProc(ClientData instanceData, char *buf,
 static int		TcpOutputProc(ClientData instanceData,
 			    const char *buf, int toWrite, int *errorCode);
 static void		TcpWatchProc(ClientData instanceData, int mask);
-static int		WaitForConnect(TcpState *statePtr, int *errorCodePtr,
-			    int noblock);
+static int		WaitForConnect(TcpState *statePtr, int noblock);
 
 /*
  * This structure describes the channel type structure for TCP socket
@@ -399,41 +398,33 @@ TcpBlockModeProc(
 static int
 WaitForConnect(
     TcpState *statePtr,		/* State of the socket. */
-    int *errorCodePtr,		/* Where to store errors? */
     int noblock)		/* Don't wait, even for sockets in blocking mode */
 {
-    int timeOut;		/* How long to wait. */
-    int state;			/* Of calling TclWaitForFile. */
-
     /*
      * If an asynchronous connect is in progress, attempt to wait for it to
      * complete before reading.
      */
 
     if (statePtr->flags & TCP_ASYNC_CONNECT) {
-	if (noblock || statePtr->flags & TCP_ASYNC_SOCKET) {
-	    timeOut = 0;
+	if (noblock || (statePtr->flags & TCP_ASYNC_SOCKET)) {
+            if (TclUnixWaitForFile(statePtr->fds.fd,
+                                    TCL_WRITABLE | TCL_EXCEPTION, 0) != 0) {
+                CreateClientSocket(NULL, statePtr);
+            }
 	} else {
-	    timeOut = -1;
-	    CLEAR_BITS(statePtr->flags, TCP_ASYNC_CONNECT);
-	}
-	errno = 0;
-	state = TclUnixWaitForFile(statePtr->fds.fd,
-		TCL_WRITABLE | TCL_EXCEPTION, timeOut);
-        if (state != 0) {
-            CreateClientSocket(NULL, statePtr);
-        }
-        if (statePtr->flags & TCP_ASYNC_CONNECT) {
-            /* We are still in progress, so ignore the result of the last
-             * attempt */
-	    *errorCodePtr = errno = EWOULDBLOCK;
-	    return -1;
+            while (statePtr->flags & TCP_ASYNC_CONNECT) {
+                if (TclUnixWaitForFile(statePtr->fds.fd,
+                                        TCL_WRITABLE | TCL_EXCEPTION, -1) != 0) {
+                    CreateClientSocket(NULL, statePtr);
+                }
+            }
         }
-	if (state & TCL_EXCEPTION) {
-	    return -1;
-	}
     }
-    return 0;
+    if (statePtr->error != 0) {
+        return -1;
+    } else {
+        return 0;
+    }
 }
 
 /*
@@ -471,7 +462,9 @@ TcpInputProc(
     int bytesRead;
 
     *errorCodePtr = 0;
-    if (WaitForConnect(statePtr, errorCodePtr, 0) != 0) {
+    if (WaitForConnect(statePtr, 0) != 0) {
+        *errorCodePtr = statePtr->error;
+        statePtr->error = 0;
 	return -1;
     }
     bytesRead = recv(statePtr->fds.fd, buf, (size_t) bufSize, 0);
@@ -521,7 +514,9 @@ TcpOutputProc(
     int written;
 
     *errorCodePtr = 0;
-    if (WaitForConnect(statePtr, errorCodePtr, 0) != 0) {
+    if (WaitForConnect(statePtr, 0) != 0) {
+        *errorCodePtr = statePtr->error;
+        statePtr->error = 0;
 	return -1;
     }
     written = send(statePtr->fds.fd, buf, (size_t) toWrite, 0);
@@ -748,9 +743,8 @@ TcpGetOptionProc(
 {
     TcpState *statePtr = instanceData;
     size_t len = 0;
-    int errorCode;
 
-    WaitForConnect(statePtr, &errorCode, 1);
+    WaitForConnect(statePtr, 1);
 
     if (optionName != NULL) {
 	len = strlen(optionName);
-- 
cgit v0.12