[vlc-commits] net_Read: rewrite, fix corner cases (fix #8972)

Rémi Denis-Courmont git at videolan.org
Sun Jul 21 18:47:12 CEST 2013


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Sun Jul 21 19:37:06 2013 +0300| [3328b21b5b18721ff541cb326ff472a42d1bef4f] | committer: Rémi Denis-Courmont

net_Read: rewrite, fix corner cases (fix #8972)

This new implementation opportunistically tries to read data before
invoking poll(). This reduces latency when data is already waiting in
buffers, allows receiving pending data in non-blocking fashion even if
the object has been killed. This also gives GnuTLS a chance to dequeue
data from internal buffers.

Also the corner case of 0 read should now be handled correctly.

The corner case of an error after success read is no longer handled.
This was race-prone and useless.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=3328b21b5b18721ff541cb326ff472a42d1bef4f
---

 src/network/io.c |  144 +++++++++++++++++++-----------------------------------
 1 file changed, 50 insertions(+), 94 deletions(-)

diff --git a/src/network/io.c b/src/network/io.c
index f19f840..9b13b2a 100644
--- a/src/network/io.c
+++ b/src/network/io.c
@@ -256,135 +256,91 @@ ssize_t
 net_Read (vlc_object_t *restrict p_this, int fd, const v_socket_t *vs,
           void *restrict p_buf, size_t i_buflen, bool waitall)
 {
-    size_t i_total = 0;
-    struct pollfd ufd[2] = {
-        { .fd = fd,                           .events = POLLIN },
-        { .fd = vlc_object_waitpipe (p_this), .events = POLLIN },
-    };
+    struct pollfd ufd[2];
 
-    if (ufd[1].fd == -1)
-        return -1; /* vlc_object_waitpipe() sets errno */
+    ufd[0].fd = fd;
+    ufd[0].events = POLLIN;
+    ufd[1].fd = vlc_object_waitpipe (p_this);
+    ufd[1].events = POLLIN;
 
-    while (i_buflen > 0)
+    size_t i_total = 0;
+    do
     {
-        if (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), -1) < 0)
-        {
-            if (errno != EINTR)
-                goto error;
-            continue;
-        }
-
-        if (i_total > 0)
-        {
-            /* Errors (-1) and EOF (0) will be returned on next call,
-             * otherwise we'd "hide" the error from the caller, which is a
-             * bad idea™. */
-            if (ufd[0].revents & (POLLERR|POLLNVAL))
-                break;
-            if (ufd[1].revents)
-                break;
-        }
-        else
-        {
-            if (ufd[1].revents)
-            {
-                msg_Dbg (p_this, "socket %d polling interrupted", fd);
-#if defined(_WIN32)
-                WSASetLastError (WSAEINTR);
-#else
-                errno = EINTR;
-#endif
-                goto silent;
-            }
-        }
-
-        assert (ufd[0].revents);
-
         ssize_t n;
-#if defined(_WIN32)
-        int error;
-#endif
         if (vs != NULL)
         {
             int canc = vlc_savecancel ();
             n = vs->pf_recv (vs->p_sys, p_buf, i_buflen);
-#if defined(_WIN32)
-            /* We must read last error immediately, because vlc_restorecancel()
-             * access thread local storage, and TlsGetValue() will call
-             * SetLastError() to indicate that the function succeeded, thus
-             * overwriting the error code coming from pf_recv().
-             * WSAGetLastError is just an alias for GetLastError these days.
-             */
-            error = WSAGetLastError();
-#endif
             vlc_restorecancel (canc);
         }
         else
         {
 #ifdef _WIN32
             n = recv (fd, p_buf, i_buflen, 0);
-            error = WSAGetLastError();
 #else
             n = read (fd, p_buf, i_buflen);
 #endif
         }
 
-        if (n == -1)
+        if (n < 0)
         {
-#if defined(_WIN32)
-            switch (error)
-            {
-                case WSAEWOULDBLOCK:
-                case WSAEINTR:
-                /* only happens with vs != NULL (TLS) - not really an error */
-                    continue;
-
-                case WSAEMSGSIZE:
-                /* For UDP only */
-                /* On Win32, recv() fails if the datagram doesn't fit inside
-                 * the passed buffer, even though the buffer will be filled
-                 * with the first part of the datagram. */
-                    msg_Err (p_this, "Receive error: "
-                                     "Increase the mtu size (--mtu option)");
-                    n = i_buflen;
-                    break;
-            }
-#else
-            switch (errno)
+            switch (net_errno)
             {
                 case EAGAIN: /* spurious wakeup or no TLS data */
 #if (EAGAIN != EWOULDBLOCK)
                 case EWOULDBLOCK:
 #endif
                 case EINTR:  /* asynchronous signal */
-                    continue;
-            }
+                    break;
+#ifdef _WIN32
+                case WSAEMSGSIZE: /* datagram too big */
+                    n = i_buflen;
+                    break;
 #endif
-            goto error;
+                default:
+                    goto error;
+            }
+        }
+        else
+        if (n > 0)
+        {
+            i_total += n;
+            p_buf = (char *)p_buf + n;
+            i_buflen -= n;
+
+            if (!waitall)
+                break;
         }
+        else
+            break;/* end of stream or empty packet */
 
-        if (n == 0)
-            /* For streams, this means end of file, and there will not be any
-             * further data ever on the stream. For datagram sockets, this
-             * means empty datagram, and there could be more data coming.
-             * However, it makes no sense to set <waitall> with datagrams in the
-             * first place.
-             */
-            break; // EOF
+        /* Wait for more data */
+        if (ufd[1].fd == -1)
+        {
+            errno = EINTR;
+            return -1;
+        }
+        while (poll (ufd, sizeof (ufd) / sizeof (ufd[0]), -1) < 0)
+            if (errno != EINTR)
+                goto error;
 
-        i_total += n;
-        p_buf = (char *)p_buf + n;
-        i_buflen -= n;
+        if (ufd[1].revents)
+        {
+            msg_Dbg (p_this, "socket %d polling interrupted", fd);
+#if defined(_WIN32)
+            WSASetLastError (WSAEINTR);
+#endif
+            errno = EINTR;
+            return -1;
+        }
 
-        if (!waitall)
-            break;
+        assert (ufd[0].revents);
     }
+    while (i_buflen > 0);
 
     return i_total;
-
 error:
-    msg_Err (p_this, "Read error: %m");
-silent:
+    msg_Err (p_this, "read error: %m");
     return -1;
 }
 



More information about the vlc-commits mailing list