diff options
-rw-r--r-- | tests/socket.test | 17 | ||||
-rw-r--r-- | win/tclWinSock.c | 73 |
2 files changed, 57 insertions, 33 deletions
diff --git a/tests/socket.test b/tests/socket.test index 218cce4..a3d4b4b 100644 --- a/tests/socket.test +++ b/tests/socket.test @@ -67,6 +67,10 @@ namespace import -force ::tcltest::* 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. # @@ -926,6 +930,19 @@ test socket-8.2 {testing writable event when quick failure} {socket win} { 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 diff --git a/win/tclWinSock.c b/win/tclWinSock.c index e7b086a..aa298f5 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -719,44 +719,52 @@ SocketEventProc( 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. + * Throw the readable event if an async connect failed. */ - 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 (infoPtr->lastError) { - 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) { + fd_set readFds; + struct timeval timeout; + /* - * Connect errors should also fire the readable handler. + * 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. */ - mask |= TCL_READABLE; + 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); } @@ -2409,19 +2417,18 @@ SocketProc( */ 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; } - } - if (infoPtr->flags & SOCKET_ASYNC_CONNECT) { - infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT); - if (error != ERROR_SUCCESS) { - TclWinConvertWSAError((DWORD) error); - infoPtr->lastError = Tcl_GetErrno(); - } + /* fire writable event on connect */ infoPtr->readyEvents |= FD_WRITE; + } + infoPtr->readyEvents |= event; /* |