? network_io/unix/.sockopt.c.swp
Index: include/arch/unix/apr_arch_networkio.h
===================================================================
RCS file: /home/cvspublic/apr/include/arch/unix/apr_arch_networkio.h,v
retrieving revision 1.8
diff -u -r1.8 apr_arch_networkio.h
--- include/arch/unix/apr_arch_networkio.h	13 Feb 2004 09:38:30 -0000	1.8
+++ include/arch/unix/apr_arch_networkio.h	24 Feb 2004 03:47:33 -0000
@@ -137,5 +137,16 @@
             (skt)->options &= ~(option);        \
     } while (0)
 
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+/**
+ * Set if we discover that SO_xxxTIMEO doesn't work.
+ * This can vary based on kernel version (Linux 2.2 to Linux 2.4), so we need
+ * to do this check at runtime. It's harmless to do so without a mutex or
+ * once, as the worst thing that can happen is that we try it more often than
+ * necessary. (It should never become (un)broken during execution.)
+ */
+extern int apr_sockopt_timeo_known_broken;
+#endif
+
 #endif  /* ! NETWORK_IO_H */
 
Index: network_io/unix/sendrecv.c
===================================================================
RCS file: /home/cvspublic/apr/network_io/unix/sendrecv.c,v
retrieving revision 1.103
diff -u -r1.103 sendrecv.c
--- network_io/unix/sendrecv.c	13 Feb 2004 09:38:33 -0000	1.103
+++ network_io/unix/sendrecv.c	24 Feb 2004 03:47:33 -0000
@@ -26,6 +26,12 @@
 #include <sys/sysctl.h>
 #endif
 
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+#define EMULATED_TIMEOUT (sock->timeout > 0 && apr_sockopt_timeo_known_broken)
+#else
+#define EMULATED_TIMEOUT (sock->timeout > 0)
+#endif
+
 apr_status_t apr_socket_send(apr_socket_t *sock, const char *buf, 
                              apr_size_t *len)
 {
@@ -41,7 +47,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) 
-                 && (sock->timeout > 0)) {
+                 && EMULATED_TIMEOUT) {
         apr_status_t arv;
 do_select:
         arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
@@ -59,7 +65,7 @@
         *len = 0;
         return errno;
     }
-    if ((sock->timeout > 0) && (rv < *len)) {
+    if (EMULATED_TIMEOUT && (rv < *len)) {
         sock->options |= APR_INCOMPLETE_WRITE;
     }
     (*len) = rv;
@@ -81,7 +87,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
 do_select:
         arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
         if (arv != APR_SUCCESS) {
@@ -98,7 +104,7 @@
         (*len) = 0;
         return errno;
     }
-    if ((sock->timeout > 0) && (rv < *len)) {
+    if (EMULATED_TIMEOUT && (rv < *len)) {
         sock->options |= APR_INCOMPLETE_READ;
     }
     (*len) = rv;
@@ -121,7 +127,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
         apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
         if (arv != APR_SUCCESS) {
             *len = 0;
@@ -154,7 +160,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
         apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 1);
         if (arv != APR_SUCCESS) {
             *len = 0;
@@ -201,7 +207,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
         apr_status_t arv;
 do_select:
         arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
@@ -219,7 +225,7 @@
         *len = 0;
         return errno;
     }
-    if ((sock->timeout > 0) && (rv < requested_len)) {
+    if (EMULATED_TIMEOUT && (rv < requested_len)) {
         sock->options |= APR_INCOMPLETE_WRITE;
     }
     (*len) = rv;
@@ -299,7 +305,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
 do_select:
         arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
         if (arv != APR_SUCCESS) {
@@ -334,7 +340,7 @@
              * partial byte count;  this is a non-blocking socket.
              */
 
-            if (sock->timeout > 0) {
+            if (EMULATED_TIMEOUT) {
                 sock->options |= APR_INCOMPLETE_WRITE;
             }
             return arv;
@@ -482,7 +488,7 @@
 
             if (rv == -1) {
                 if (errno == EAGAIN) {
-                    if (sock->timeout > 0) {
+                    if (EMULATED_TIMEOUT) {
                         sock->options |= APR_INCOMPLETE_WRITE;
                     }
                     /* FreeBSD's sendfile can return -1/EAGAIN even if it
@@ -521,7 +527,7 @@
             }
         }
         if ((rv == -1) && (errno == EAGAIN) 
-                       && (sock->timeout > 0)) {
+                       && EMULATED_TIMEOUT) {
             apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
             if (arv != APR_SUCCESS) {
                 *len = 0;
@@ -638,7 +644,7 @@
     } while (rc == -1 && errno == EINTR);
 
     if ((rc == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
         apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
 
         if (arv != APR_SUCCESS) {
@@ -785,7 +791,7 @@
     } while (rv == -1 && errno == EINTR);
 
     if ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 
-                   && (sock->timeout > 0)) {
+                   && EMULATED_TIMEOUT) {
 do_select:
         arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
         if (arv != APR_SUCCESS) {
@@ -813,7 +819,7 @@
         return errno;
     }
 
-    if ((sock->timeout > 0)
+    if (EMULATED_TIMEOUT
           && (parms.bytes_sent 
                 < (parms.file_bytes + parms.header_length + parms.trailer_length))) {
         sock->options |= APR_INCOMPLETE_WRITE;
@@ -928,7 +934,7 @@
             if (nbytes) {
                 rv = 0;
             }
-            else if (!arv && (sock->timeout > 0)) {
+            else if (!arv && EMULATED_TIMEOUT) {
                 apr_status_t t = apr_wait_for_io_or_timeout(NULL, sock, 0);
 
                 if (t != APR_SUCCESS) {
@@ -949,7 +955,7 @@
 
     /* Update how much we sent */
     *len = nbytes;
-    if ((sock->timeout > 0) && (*len < requested_len)) {
+    if (EMULATED_TIMEOUT && (*len < requested_len)) {
         sock->options |= APR_INCOMPLETE_WRITE;
     }
     return APR_SUCCESS;
Index: network_io/unix/sockopt.c
===================================================================
RCS file: /home/cvspublic/apr/network_io/unix/sockopt.c,v
retrieving revision 1.76
diff -u -r1.76 sockopt.c
--- network_io/unix/sockopt.c	13 Feb 2004 09:38:33 -0000	1.76
+++ network_io/unix/sockopt.c	24 Feb 2004 03:47:33 -0000
@@ -13,9 +13,13 @@
  * limitations under the License.
  */
 
+#include <assert.h>
 #include "apr_arch_networkio.h"
 #include "apr_strings.h"
 
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+int apr_sockopt_timeo_known_broken;
+#endif
 
 static apr_status_t soblock(int sd)
 {
@@ -76,29 +80,60 @@
 apr_status_t apr_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
 {
     apr_status_t stat;
+    int want_nonblocking, currently_nonblocking;
 
-    /* If our timeout is positive or zero and our last timeout was
-     * negative, then we need to ensure that we are non-blocking.
-     * Conversely, if our timeout is negative and we had a positive
-     * or zero timeout, we must make sure our socket is blocking.
-     * We want to avoid calling fcntl more than necessary on the socket,
-     */
-    if (t >= 0 && sock->timeout < 0) {
-        if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 1) {
-            if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) {
-                return stat;
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+    if (!apr_sockopt_timeo_known_broken && sock->timeout != t && t != -1) {
+        struct timeval tv;
+        tv.tv_sec  = t / APR_USEC_PER_SEC;
+        tv.tv_usec = t % APR_USEC_PER_SEC;
+        if (setsockopt(sock->socketdes, SOL_SOCKET, SO_RCVTIMEO, &tv,
+                       sizeof(struct timeval)) < 0) {
+            if (errno == ENOPROTOOPT) {
+                apr_sockopt_timeo_known_broken = 1;
+            } else {
+                return errno;
             }
-            apr_set_option(sock, APR_SO_NONBLOCK, 1);
+        } else if (setsockopt(sock->socketdes, SOL_SOCKET, SO_SNDTIMEO, &tv,
+                              sizeof(struct timeval)) < 0) {
+            /* No one would implement one and not the other, right? */
+            return errno;
         }
-    } 
-    else if (t < 0 && sock->timeout >= 0) {
-        if (apr_is_option_set(sock, APR_SO_NONBLOCK) != 0) { 
-            if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) { 
-                return stat; 
-            }
-            apr_set_option(sock, APR_SO_NONBLOCK, 0);
-        } 
     }
+#endif
+
+    /*
+     * working setsockopt   && timeout >  0 => blocking
+     * no/broken setsockopt && timeout >  0 => non-blocking
+     *                         timeout == 0 => non-blocking
+     *                         timeout <= 0 => blocking
+     */
+#if defined(SO_RCVTIMEO) && defined(SO_SNDTIMEO)
+    want_nonblocking =      (t             == 0)
+        || (t > 0             && apr_sockopt_timeo_known_broken);
+    currently_nonblocking = (sock->timeout == 0)
+        || (sock->timeout > 0 && apr_sockopt_timeo_known_broken);
+#else
+    want_nonblocking =      (t             >= 0);
+    currently_nonblocking = (sock->timeout >= 0);
+#endif
+
+    assert(   !!apr_is_option_set(sock, APR_SO_NONBLOCK)
+           == !!currently_nonblocking);
+    if (want_nonblocking && !currently_nonblocking) {
+        /* Set non-blocking */
+        if ((stat = sononblock(sock->socketdes)) != APR_SUCCESS) {
+            return stat;
+        }
+        apr_set_option(sock, APR_SO_NONBLOCK, 1);
+    } else if (!want_nonblocking && currently_nonblocking) {
+        /* Set blocking */
+        if ((stat = soblock(sock->socketdes)) != APR_SUCCESS) {
+            return stat;
+        }
+        apr_set_option(sock, APR_SO_NONBLOCK, 0);
+    }
+
     /* must disable the incomplete read support if we disable
      * a timeout
      */
