poke-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] mi: Make sure fd is non-blocking before select+read


From: Tim Rühsen
Subject: [PATCH] mi: Make sure fd is non-blocking before select+read
Date: Tue, 12 May 2020 18:58:02 +0200

On Linux, select() may report a socket file descriptor as
"ready for reading", while nevertheless a subsequent read blocks.

2020-05-12  Tim Rühsen  <address@hidden>

        * poke/pk-mi.c (pk_mi_fd_set_nonblocking): New function.
        (pk_mi_fd_restore_blocking): New function.
        (pk_mi_loop): Set fd to blocking and restore before returning.
---
 ChangeLog    |  6 +++++
 poke/pk-mi.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/poke/pk-mi.c b/poke/pk-mi.c
index b16a466b..9b61873f 100644
--- a/poke/pk-mi.c
+++ b/poke/pk-mi.c
@@ -24,6 +24,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <assert.h>
+#include <fcntl.h>

 #include "pk-mi-msg.h"
 #include "pk-mi-json.h"
@@ -159,6 +160,33 @@ pk_mi_send_frame_msg (const char *payload)
   fflush (stdout);
 }

+/* Set fd to non-blocking and return old flags or -1 on error. */
+static int pk_mi_fd_set_nonblocking(int fd)
+{
+  int flags = fcntl (fd, F_GETFL, 0);
+
+  if (flags >= 0 && (flags & O_NONBLOCK) == 0)
+    {
+      if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
+        flags = -1;
+    }
+
+  if (flags < 0)
+    perror ("fcntl");
+
+  return flags;
+}
+
+/* Restore flags to fd if it was set to blocking previously. */
+static void pk_mi_fd_restore_blocking(int fd, int flags)
+{
+  if (flags >= 0 && (flags & O_NONBLOCK) == 0)
+    {
+      if (fcntl (fd, F_SETFL, flags) < 0)
+        perror ("fcntl");
+    }
+}
+
 /* This global is used to finalize the MI loop.  */
 static int pk_mi_exit_p;

@@ -166,11 +194,20 @@ static int
 pk_mi_loop (int fd)
 {
   fd_set active_fd_set, read_fd_set;
+  int old_flags;
+  int ret;

   FD_ZERO (&active_fd_set);
   FD_SET (fd, &active_fd_set);

+  /* Make sure that fd is non-blocking.
+   * From 'man 2 select':
+   *  On Linux, select() may report a socket file descriptor as
+   *  "ready for reading", while nevertheless a subsequent read blocks." */
+  old_flags = pk_mi_fd_set_nonblocking (fd);
+
   pk_mi_exit_p = 0;
+
   while (1)
     {
       read_fd_set = active_fd_set;
@@ -183,19 +220,32 @@ pk_mi_loop (int fd)

       if (FD_ISSET (fd, &read_fd_set))
         {
-          int ret = pk_mi_read_from_client (fd);
+          ret = pk_mi_read_from_client (fd);

           if (pk_mi_exit_p)
-            /* Client requested us to close the connection.  */
-            return 1;
+            {
+              /* Client requested us to close the connection.  */
+              ret = 1;
+              break;
+            }
           else if (ret == -1)
-            /* Client closed the connection.  */
-            return 1;
+            {
+              /* Client closed the connection.  */
+              ret = 1;
+              break;
+            }
           else if (ret == -2)
-            /* Protocol error.  */
-            return 0;
+            {
+              /* Protocol error.  */
+              ret = 0;
+              break;
+            }
         }
     }
+
+  pk_mi_fd_restore_blocking (fd, old_flags);
+
+  return ret;
 }

 /* Message sender.  */
--
2.26.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]