Index: tests/socket.test ================================================================== --- tests/socket.test +++ tests/socket.test @@ -64,10 +64,14 @@ namespace import -force ::tcltest::* # Some tests require the testthread and exec commands testConstraint testthread [llength [info commands testthread]] testConstraint exec [llength [info commands exec]] + +# Produce a random port number in the Dynamic/Private range +# from 49152 through 65535. +proc randport {} { expr {int(rand()*16383+49152)} } # If remoteServerIP or remoteServerPort are not set, check in the # environment variables for externally set values. # @@ -923,10 +927,23 @@ vwait x catch {close $s} after cancel $a1 set x } writable + +test socket-8.3 {testing fileevent readable on failed async socket connect} {socket} { + # Test for bug 581937ab1e + + set a1 [after 5000 {set x timeout}] + # This connect should fail + set s [socket -async localhost [randport]] + fileevent $s readable {set x readable} + vwait x + catch {close $s} + after cancel $a1 + set x +} readable test socket-9.1 {testing spurious events} {socket} { set len 0 set spurious 0 set done 0 Index: win/tclWinSock.c ================================================================== --- win/tclWinSock.c +++ win/tclWinSock.c @@ -717,46 +717,54 @@ Tcl_Time blockTime = { 0, 0 }; Tcl_SetMaxBlockTime(&blockTime); mask |= TCL_READABLE|TCL_WRITABLE; } else if (events & FD_READ) { - fd_set readFds; - struct timeval timeout; - - /* - * We must check to see if data is really available, since someone - * could have consumed the data in the meantime. Turn off async - * notification so select will work correctly. If the socket is still - * readable, notify the channel driver, otherwise reset the async - * select handler and keep waiting. - */ - - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) UNSELECT, (LPARAM) infoPtr); - - FD_ZERO(&readFds); - FD_SET(infoPtr->socket, &readFds); - timeout.tv_usec = 0; - timeout.tv_sec = 0; - - if (select(0, &readFds, NULL, NULL, &timeout) != 0) { - mask |= TCL_READABLE; - } else { - infoPtr->readyEvents &= ~(FD_READ); - SendMessage(tsdPtr->hwnd, SOCKET_SELECT, - (WPARAM) SELECT, (LPARAM) infoPtr); - } - } - if (events & (FD_WRITE | FD_CONNECT)) { - mask |= TCL_WRITABLE; - if (events & FD_CONNECT && infoPtr->lastError != NO_ERROR) { - /* - * Connect errors should also fire the readable handler. - */ - - mask |= TCL_READABLE; - } + /* + * Throw the readable event if an async connect failed. + */ + + if (infoPtr->lastError) { + + mask |= TCL_READABLE; + + } else { + fd_set readFds; + struct timeval timeout; + + /* + * We must check to see if data is really available, since someone + * could have consumed the data in the meantime. Turn off async + * notification so select will work correctly. If the socket is still + * readable, notify the channel driver, otherwise reset the async + * select handler and keep waiting. + */ + + SendMessage(tsdPtr->hwnd, SOCKET_SELECT, + (WPARAM) UNSELECT, (LPARAM) infoPtr); + + FD_ZERO(&readFds); + FD_SET(infoPtr->socket, &readFds); + timeout.tv_usec = 0; + timeout.tv_sec = 0; + + if (select(0, &readFds, NULL, NULL, &timeout) != 0) { + mask |= TCL_READABLE; + } else { + infoPtr->readyEvents &= ~(FD_READ); + SendMessage(tsdPtr->hwnd, SOCKET_SELECT, + (WPARAM) SELECT, (LPARAM) infoPtr); + } + } + } + + /* + * writable event + */ + + if (events & FD_WRITE) { + mask |= TCL_WRITABLE; } if (mask) { Tcl_NotifyChannel(infoPtr->channel, mask); } @@ -2407,23 +2415,22 @@ * Remember any error that occurred so we can report * connection failures. */ if (error != ERROR_SUCCESS) { + /* Async Connect error */ TclWinConvertWSAError((DWORD) error); infoPtr->lastError = Tcl_GetErrno(); + /* Fire also readable event on connect failure */ + infoPtr->readyEvents |= FD_READ; } + + /* fire writable event on connect */ + infoPtr->readyEvents |= FD_WRITE; + } - if (infoPtr->flags & SOCKET_ASYNC_CONNECT) { - infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT); - if (error != ERROR_SUCCESS) { - TclWinConvertWSAError((DWORD) error); - infoPtr->lastError = Tcl_GetErrno(); - } - infoPtr->readyEvents |= FD_WRITE; - } infoPtr->readyEvents |= event; /* * Wake up the Main Thread. */