Mailing-List: contact cygwin-developers-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-developers-owner AT cygwin DOT com Delivered-To: mailing list cygwin-developers AT cygwin DOT com X-Authentication-Warning: atacama.four-d.de: mail set sender to using -f Date: Wed, 26 Feb 2003 15:04:36 +0100 (=?ISO-8859-1?Q?Westeurop=E4ische_Normalzeit?=) From: Thomas Pfaff To: cygwin-developers AT cygwin DOT com Subject: Re: Interruptable connect In-Reply-To: <20030225180438.GT8853@cygbert.vinschen.de> Message-ID: X-X-Sender: pfaff AT antarctica DOT intern DOT net MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII On Tue, 25 Feb 2003, Corinna Vinschen wrote: > On Tue, Feb 25, 2003 at 03:32:08PM +0100, Corinna Vinschen wrote: > > Btw. Thomas, do you have the Stevens book "UNIX Network Programming"? > The end of chapter 15.5 contains the sub chapter "Interrupted connect" > which states > > What happens if our call to connect on a normal blocking socket is > interrupted, say, by a caught signal, before TCP's three-way handshake > completes? Assuming the connect is not automatically restarted, it > returns EINTR. But we cannot call connect again to wait for the > connect to complete. Doing so will return EADDRINUSE. > > What we must do in this scenario is call select, just as we have done > in this section for a nonblocking connect. Then select returns when > the connection completes successfully (making the socket writable) or > when the connection fails (making the socket readable and writable). > > So, IIUC, connect isn't supposed to stop the TCP layer further trying > to connect! Which means, I hope, appropriately screwing your accept() > code into connect() in fhandler_socket.cc should be completely sufficient > to solve our problem. > This is in accordance with http://www.opengroup.org/onlinepubs/007904975/functions/connect.html : If connect() is interrupted by a signal that is caught while blocked waiting to establish a connection, connect() shall fail and set errno to [EINTR], but the connection request shall not be aborted, and the connection shall be established asynchronously. [...] When the connection has been established asynchronously, select() and poll() shall indicate that the file descriptor for the socket is ready for writing. The connect can be changed to look like this : int fhandler_socket::connect (const struct sockaddr *name, int namelen) { WSAEVENT ev[2] = { WSA_INVALID_EVENT, signal_arrived }; int res = -1; BOOL secret_check_failed = FALSE; BOOL in_progress = FALSE; sockaddr_in sin; int secret [4]; if (!get_inet_addr (name, namelen, &sin, &namelen, secret)) { return -1; if (!is_nonblocking () && !is_connect_pending ()) { ev[0] = WSACreateEvent (); WSAEventSelect (sock, ev[0], FD_CONNECT); } res = ::connect (get_socket (), (sockaddr *) &sin, namelen); if (res && !is_nonblocking () && !is_connect_pending () && WSAGetLastError () == WSAEWOULDBLOCK) { wait_result = WSAWaitForMultipleEvents (2, ev, FALSE, 0, FALSE); if (wait_result == WSA_WAIT_EVENT_0) WSAEnumNetworkEvents (sock, ev[0], &sock_event); /* Unset events for connecting socket and switch back to blocking mode */ WSAEventSelect (get_socket (), ev[0], 0); unsigned long nonblocking = 0; ioctlsocket ( get_socket (), FIONBIO, &nonblocking); switch (wait_result) { case WSA_WAIT_EVENT_0: if (sock_event.lNetworkEvents & FD_CONNECT) { if (sock_event.iErrorCode[FD_CONNECT_BIT]) { WSASetLastError (sock_event.iErrorCode[FD_CONNECT_BIT]); set_winsock_errno (); } else res = 0; } /* else; : Should never happen since FD_CONNECT is the only event that has been selected */ break; case WSA_WAIT_EVENT_0 + 1: debug_printf ("signal received during connect"); WSASetLastError (WSAEINPROGESS); set_errno (EINTR); break; case WSA_WAIT_FAILED: default: /* Should never happen */ WSASetLastError (WSAEFAULT); set_winsock_errno (); break; } } if (res) { /* Special handling for connect to return the correct error code when called on a non-blocking socket. */ if (is_nonblocking ()) [...] if (ev[0] != WSA_INVALID_EVENT) WSACloseEvent (ev[0]); return res; } A second attempt to connect will return WSAEALREADY. To avoid a second call to WSAEventSelect WSAEINPROGESS is set when a signal arrived, this will set the connect state to CONNECT_PENDING. I have not tested this code yet. I will generate a patch if you agree. BTW, i see a really strange behaviour with select and getsockopt, it seems that SO_ERROR is set some time after the select is signaled. Here is a WIN32 test case : #define WIN32_LEAN_AND_MEAN #include #include #include int main(void) { fd_set wrfds; fd_set excfds; WSADATA wsaData; SOCKET sock; int res; int sock_error; int sock_error_len = sizeof(sock_error); struct sockaddr_in s_server; unsigned long nonblocking; WSAStartup( 0x202, &wsaData ); sock = socket(AF_INET,SOCK_STREAM,0); s_server.sin_family = AF_INET; s_server.sin_addr.s_addr = htonl (INADDR_LOOPBACK); s_server.sin_port = htons( 5678 ); nonblocking = 1; res = ioctlsocket (sock, FIONBIO, &nonblocking); if (connect (sock, (struct sockaddr*) &s_server, sizeof (s_server)) != 0) { if (WSAGetLastError () != WSAEWOULDBLOCK) { printf( "%d\n", WSAGetLastError()); return 0; } } nonblocking = 0; res = ioctlsocket (sock, FIONBIO, &nonblocking); FD_ZERO(&wrfds); FD_ZERO(&excfds); FD_SET(sock, &wrfds); FD_SET(sock, &excfds); select (FD_SETSIZE, NULL, &wrfds, &excfds, NULL); if (FD_ISSET(sock, &wrfds)) { res = getsockopt (sock, SOL_SOCKET, SO_ERROR, &sock_error, &sock_error_len); printf( "wr: %d %d\n", res, sock_error); } else if (FD_ISSET(sock, &excfds)) { do { res = getsockopt (sock, SOL_SOCKET, SO_ERROR, &sock_error, &sock_error_len); printf( "ex: %d %d\n", res, sock_error); Sleep (0); } while (sock_error == 0); } closesocket(sock); return 0; } The first call to getsockopt returns 0 in sock_error, the second will return 10061 as expected. This is on NT4 German, SP6 Can you duplicate this ? Thomas