apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jim Carlson <jcarl...@jnous.com>
Subject [PATCH] apr_socket_sendv() and IOV_MAX
Date Fri, 25 Apr 2003 02:15:11 GMT
Here's the patch for apr_socket_sendv().  The function now sends the iovec[] in 
IOV_MAX sized chunks, until the whole thing is sent, or there is an error.  If 
EAGAIN is returned on a timed socket, it will wait and try again, just like 
before, but it will only do this once.  Note that it is very possible for bytes 
to be sent and some value other than APR_SUCCESS to be returned.  (The 
apr_network_io.h header already indicates that this is a possibility for this 
function, but I believe it wouldn't have happened in the original code.)

I've tested this a bit on Solaris and Linux, with both blocking and nonblocking 
sockets, with the expected results.  I did notice one odd thing: when I set a 
socket to nonblocking, and then return it to blocking (using 
apr_socket_timeout_set(-1)), I don't seem to get blocking behaviour.  Instead I 
get underwrites from writev().  However, when I leave a socket with its default 
of blocking, or explicitly set it once to blocking, I don't get an underwrite 
(all else is constant).  This doesn't have anything to do with my patch -- it 
happens with both versions of apr_socket_sendv() -- but if anyone has an 
explanation, I'd like to hear it.

Another thing: the effect of this patch on Linux appears to be suboptimal. 
Based on my observations, Linux defines IOV_MAX (I've seen 16 and 1024), but 
ignores it in writev.  I can send > IOV_MAX iovecs to writev() w/o an error 
return.  Accordingly, the work done by this patch is unnecessary on Linux. 
However, beyond checking #ifdef IOV_MAX, I don't know of a good way to isolate 
that case.  And this patch does solve a real problem on Solaris.

Lastly, I followed the convention in sendrecv.c of calling 
apr_wait_for_io_or_timeout() on an EAGAIN only if (socket->timeout != 0). 
Similarly, APR_INCOMPLETE_WRITE is only set if socket->timeout is non-zero. 
However, shouldn't the condition in both cases be (socket->timeout > 0)?

Thanks,
Jim


--- network_io/unix/sendrecv.c.orig	2003-04-24 16:01:09.760030000 -0700
+++ network_io/unix/sendrecv.c	2003-04-24 18:32:08.890003000 -0700
@@ -65,6 +65,11 @@
  #include <sys/sysctl.h>
  #endif

+#ifndef min
+#define min(x,y) ((x) <= (y) ? (x) : (y))
+#endif
+
+
  apr_status_t apr_socket_send(apr_socket_t *sock, const char *buf,
                               apr_size_t *len)
  {
@@ -222,45 +227,58 @@
                                apr_int32_t nvec, apr_size_t *len)
  {
      apr_ssize_t rv;
-    apr_size_t requested_len = 0;
-    apr_int32_t i;
+    apr_int32_t i, vecstosend, have_waited = 0;

-    for (i = 0; i < nvec; i++) {
-        requested_len += vec[i].iov_len;
-    }
+    *len = 0;

      if (sock->netmask & APR_INCOMPLETE_WRITE) {
          sock->netmask &= ~APR_INCOMPLETE_WRITE;
          goto do_select;
      }

-    do {
-        rv = writev(sock->socketdes, vec, nvec);
-    } while (rv == -1 && errno == EINTR);
-
-    if (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) &&
-      sock->timeout != 0) {
-        apr_status_t arv;
-do_select:
-        arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
-        if (arv != APR_SUCCESS) {
-            *len = 0;
-            return arv;
-        }
-        else {
-            do {
-                rv = writev(sock->socketdes, vec, nvec);
-            } while (rv == -1 && errno == EINTR);
+    /* loop repeatedly, sending vec in IOV_MAX sized chunks, until
+     * everything is sent, or we see an error, or we get EAGAIN more
+     * than once on a socket with timeout != 0 */
+    while(nvec > 0) {
+#ifdef IOV_MAX
+        vecstosend = min(nvec, IOV_MAX);
+#else
+        vecstosend = nvec;
+#endif
+        do {
+            rv = writev(sock->socketdes, vec, vecstosend);
+        } while (rv == -1 && errno == EINTR);
+
+        if (rv != -1) {
+            *len += rv;
+            /* did we send them all? */
+            for(i=0; i < vecstosend; i++)
+                rv -= vec[i].iov_len;
+            if (rv < 0) {
+                /* no */
+                break;
+            }
+            vec += vecstosend;
+            nvec -= vecstosend;
+        } else if ((errno == EAGAIN || errno == EWOULDBLOCK)
+                && sock->timeout != 0) {
+            if (have_waited) {
+                sock->netmask |= APR_INCOMPLETE_WRITE;
+                return errno;
+            }
+    do_select:
+            rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+            have_waited = 1;
+        } else {
+            return errno;
          }
      }
-    if (rv == -1) {
-        *len = 0;
-        return errno;
-    }
-    if (sock->timeout && rv < requested_len) {
-    sock->netmask |= APR_INCOMPLETE_WRITE;
-    }
-    (*len) = rv;
+    if (nvec && sock->timeout)
+        sock->netmask |= APR_INCOMPLETE_WRITE;
+
      return APR_SUCCESS;
  }
  #endif


Mime
View raw message