2007-05-10 Dick Porter <dick@ximian.com>
authorDick Porter <dick@acm.org>
Thu, 10 May 2007 14:46:35 +0000 (14:46 -0000)
committerDick Porter <dick@acm.org>
Thu, 10 May 2007 14:46:35 +0000 (14:46 -0000)
* sockets.c (_wapi_recvfrom): Refine the fix to bug 75705 so EINTR
isn't returned if the remote end shuts down cleanly.  Fixes zmd
and the second example in bug 75705.

svn path=/trunk/mono/; revision=77136

mono/io-layer/ChangeLog
mono/io-layer/socket-private.h
mono/io-layer/sockets.c

index 511bba1af58ffba0aa70e45edd4494f9abfc0a70..183e31cc193274155236da8af104e103dcf3d07f 100644 (file)
@@ -1,3 +1,9 @@
+2007-05-10  Dick Porter  <dick@ximian.com>
+
+       * sockets.c (_wapi_recvfrom): Refine the fix to bug 75705 so EINTR
+       isn't returned if the remote end shuts down cleanly.  Fixes zmd
+       and the second example in bug 75705.
+
 2007-04-26  Dick Porter  <dick@ximian.com>
 
        * shared.c (_wapi_shm_semaphores_init): Need to check
index b73731905419d0dfbb2deb68b5c77c906a3ad157..db210e2356827dcbe42d4ee0c6213fa0bbdafee2 100644 (file)
@@ -21,6 +21,7 @@ struct _WapiHandle_socket
        int type;
        int protocol;
        int saved_error;
+       int still_readable;
 };
 
 #endif /* _WAPI_SOCKET_PRIVATE_H_ */
index 70a4567d135d86f2f6fe6268039484ce34688e21..4ba505c7cf42a2af8eca11af0ad881ec4c8e8988 100644 (file)
@@ -189,6 +189,9 @@ guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
 {
        gpointer handle = GUINT_TO_POINTER (fd);
        gpointer new_handle;
+       struct _WapiHandle_socket *socket_handle;
+       struct _WapiHandle_socket new_socket_handle = {0};
+       gboolean ok;
        int new_fd;
        
        if (startup_count == 0) {
@@ -201,6 +204,15 @@ guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
                return(INVALID_SOCKET);
        }
        
+       ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+                                 (gpointer *)&socket_handle);
+       if (ok == FALSE) {
+               g_warning ("%s: error looking up socket handle %p",
+                          __func__, handle);
+               WSASetLastError (WSAENOTSOCK);
+               return(INVALID_SOCKET);
+       }
+       
        do {
                new_fd = accept (fd, addr, addrlen);
        } while (new_fd == -1 && errno == EINTR &&
@@ -230,7 +242,13 @@ guint32 _wapi_accept(guint32 fd, struct sockaddr *addr, socklen_t *addrlen)
                return(INVALID_SOCKET);
        }
 
-       new_handle = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, new_fd, NULL);
+       new_socket_handle.domain = socket_handle->domain;
+       new_socket_handle.type = socket_handle->type;
+       new_socket_handle.protocol = socket_handle->protocol;
+       new_socket_handle.still_readable = 1;
+
+       new_handle = _wapi_handle_new_fd (WAPI_HANDLE_SOCKET, new_fd,
+                                         &new_socket_handle);
        if(new_handle == _WAPI_HANDLE_INVALID) {
                g_warning ("%s: error creating socket handle", __func__);
                WSASetLastError (ERROR_GEN_FAILURE);
@@ -543,6 +561,8 @@ int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags,
                   struct sockaddr *from, socklen_t *fromlen)
 {
        gpointer handle = GUINT_TO_POINTER (fd);
+       struct _WapiHandle_socket *socket_handle;
+       gboolean ok;
        int ret;
        
        if (startup_count == 0) {
@@ -560,7 +580,7 @@ int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags,
        } while (ret == -1 && errno == EINTR &&
                 !_wapi_thread_cur_apc_pending ());
 
-       if (ret == 0) {
+       if (ret == 0 && len > 0) {
                /* According to the Linux man page, recvfrom only
                 * returns 0 when the socket has been shut down
                 * cleanly.  Turn this into an EINTR to simulate win32
@@ -569,8 +589,22 @@ int _wapi_recvfrom(guint32 fd, void *buf, size_t len, int recv_flags,
                 * shutdown() in socket_close() to trigger this.) See
                 * bug 75705.
                 */
-               ret = -1;
-               errno = EINTR;
+               /* Distinguish between the socket being shut down at
+                * the local or remote ends, and reads that request 0
+                * bytes to be read
+                */
+
+               /* If this returns FALSE, it means the socket has been
+                * closed locally.  If it returns TRUE, but
+                * still_readable != 1 then shutdown
+                * (SHUT_RD|SHUT_RDWR) has been called locally.
+                */
+               ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+                                         (gpointer *)&socket_handle);
+               if (ok == FALSE || socket_handle->still_readable != 1) {
+                       ret = -1;
+                       errno = EINTR;
+               }
        }
        
        if (ret == -1) {
@@ -713,6 +747,8 @@ int _wapi_setsockopt(guint32 fd, int level, int optname,
 
 int _wapi_shutdown(guint32 fd, int how)
 {
+       struct _WapiHandle_socket *socket_handle;
+       gboolean ok;
        gpointer handle = GUINT_TO_POINTER (fd);
        int ret;
        
@@ -725,6 +761,20 @@ int _wapi_shutdown(guint32 fd, int how)
                WSASetLastError (WSAENOTSOCK);
                return(SOCKET_ERROR);
        }
+
+       if (how == SHUT_RD ||
+           how == SHUT_RDWR) {
+               ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SOCKET,
+                                         (gpointer *)&socket_handle);
+               if (ok == FALSE) {
+                       g_warning ("%s: error looking up socket handle %p",
+                                  __func__, handle);
+                       WSASetLastError (WSAENOTSOCK);
+                       return(SOCKET_ERROR);
+               }
+               
+               socket_handle->still_readable = 0;
+       }
        
        ret = shutdown (fd, how);
        if (ret == -1) {
@@ -753,6 +803,7 @@ guint32 _wapi_socket(int domain, int type, int protocol, void *unused,
        socket_handle.domain = domain;
        socket_handle.type = type;
        socket_handle.protocol = protocol;
+       socket_handle.still_readable = 1;
        
        fd = socket (domain, type, protocol);
        if (fd == -1 && domain == AF_INET && type == SOCK_RAW &&