From e4e5ca2cd874ea05befaa0eb43bd9946e56bcab1 Mon Sep 17 00:00:00 2001
From: Roman Tsisyk <roman@tsisyk.com>
Date: Mon, 16 Feb 2015 19:59:03 +0300
Subject: [PATCH] Fix accept(2) error handling in tcp_server_loop()

This patch also fixes EINTR and EWOULDBLOCK handling for
socket:read() and socket:write() wrappers.
---
 src/lua/bsdsocket.lua | 29 +++++++++++++++++++++++------
 src/sio.cc            |  3 ++-
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua
index b3c3003ee6..3d31387139 100644
--- a/src/lua/bsdsocket.lua
+++ b/src/lua/bsdsocket.lua
@@ -516,9 +516,20 @@ socket_methods.accept = function(self)
         self._errno = box.errno()
         return nil
     end
-    return bless_socket(cfd), from
+    local c = bless_socket(cfd)
+    if c == nil then
+        self._errno = box.errno()
+        return nil
+    end
+    return c, from
 end
 
+local errno_is_transient = {
+    [box.errno.EAGAIN] = true;
+    [box.errno.EWOULDBLOCK] = true;
+    [box.errno.EINTR] = true;
+}
+
 local function readchunk(self, size, timeout)
     if self.rbuf == nil then
         self.rbuf = ''
@@ -554,7 +565,7 @@ local function readchunk(self, size, timeout)
                 self.rbuf = nil
                 return data
             end
-        elseif self:errno() ~= box.errno.EAGAIN then
+        elseif not errno_is_transient[self:errno()] then
             self._errno = box.errno()
             return nil
         end
@@ -639,7 +650,7 @@ local function readline(self, limit, eol, timeout)
                return data
             end
 
-        elseif self:errno() ~= box.errno.EAGAIN then
+        elseif not error_is_transient[self:errno()] then
             self._errno = box.errno()
             return nil
         end
@@ -679,7 +690,7 @@ socket_methods.write = function(self, octets, timeout)
 
         local written = self:syswrite(octets)
         if written == nil then
-            if self:errno() ~= box.errno.EAGAIN then
+            if not errno_is_transient[self:errno()] then
                 return false
             end
         end
@@ -984,17 +995,23 @@ local function tcp_server_handler(server, sc, from)
 end
 
 local function tcp_server_loop(server, s, addr)
+    print('started')
     box.fiber.name(sprintf("%s/listen/%s:%s", server.name, addr.host, addr.port))
     while s:readable() do
         local sc, from = s:accept()
         if sc == nil then
-            break
+            if not errno_is_transient[s:errno()] then
+                print('tcp_server: failed to accept client: '..s:error())
+            end
+        else
+            box.fiber.wrap(tcp_server_handler, server, sc, from)
         end
-        box.fiber.wrap(tcp_server_handler, server, sc, from)
     end
+    -- Socket was closed
     if addr.family == 'AF_UNIX' and addr.port then
         os_remove(addr.port) -- remove unix socket
     end
+    print('stopped')
 end
 
 local function tcp_server_usage()
diff --git a/src/sio.cc b/src/sio.cc
index 230f17537e..909c76af33 100644
--- a/src/sio.cc
+++ b/src/sio.cc
@@ -226,7 +226,8 @@ sio_accept(int fd, struct sockaddr_in *addr, socklen_t *addrlen)
 {
 	/* Accept a connection. */
 	int newfd = accept(fd, (struct sockaddr *) addr, addrlen);
-	if (newfd < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+	if (newfd < 0 && errno != EAGAIN && errno != EWOULDBLOCK &&
+	    errno != EINTR)
 		tnt_raise(SocketError, fd, "accept");
 	return newfd;
 }
-- 
GitLab