When the master die, the worker should exit too, this is achieved by
checking if the FD of the socketpair/pipe was closed between the master
and the worker.

In the former architecture of the master-worker, there was only a pipe
between the master and the workers, and it was easy to check an EOF on
the pipe FD to exit() the worker.

With the new architecture, we use a socketpair by process, and this
socketpair is also used to accept new connections with the
listener_accept() callback.

This accept callback can't handle the EOF and the exit of the process,
because it's very specific to the master worker. This is why we
transformed the mworker_pipe_handler() function in a wrapper which check
if there is an EOF and exit the process, and if not call
listener_accept() to achieve the accept.
---
 src/haproxy.c | 57 ++++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index d092a9bc3..edf69d1fd 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2541,43 +2541,58 @@ void deinit(void)
        deinit_pollers();
 } /* end deinit() */
 
-void mworker_pipe_handler(int fd)
+
+
+/* This is a wrapper for the sockpair FD, It tests if the socket received an
+ * EOF, if not, it calls listener_accept */
+void mworker_accept_wrapper(int fd)
 {
        char c;
+       int ret;
 
-       while (read(fd, &c, 1) == -1) {
-               if (errno == EINTR)
-                       continue;
-               if (errno == EAGAIN) {
-                       fd_cant_recv(fd);
+       while (1) {
+               ret = recv(fd, &c, 1, MSG_PEEK);
+               if (ret == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       if (errno == EAGAIN) {
+                               fd_cant_recv(fd);
+                               return;
+                       }
+                       break;
+               } else if (ret > 0) {
+                       listener_accept(fd);
                        return;
+               } else if (ret == 0) {
+                       /* At this step the master is down before
+                        * this worker perform a 'normal' exit.
+                        * So we want to exit with an error but
+                        * other threads could currently process
+                        * some stuff so we can't perform a clean
+                        * deinit().
+                        */
+                       exit(EXIT_FAILURE);
                }
-               break;
        }
-
-       /* At this step the master is down before
-        * this worker perform a 'normal' exit.
-        * So we want to exit with an error but
-        * other threads could currently process
-        * some stuff so we can't perform a clean
-        * deinit().
-        */
-       exit(EXIT_FAILURE);
        return;
 }
 
-/* should only be called once per process */
+/*
+ * Should only be called once per process
+ * This function register the accept wrapper for the sockpair of the master 
worker
+ */
+
 void mworker_pipe_register()
 {
-       if (fdtab[proc_self->ipc_fd[1]].owner)
-               /* already initialized */
-               return;
+       /* The iocb should be already initialized with listener_accept */
+       if (fdtab[proc_self->ipc_fd[1]].iocb != listener_accept)
+               abort();
 
        fcntl(proc_self->ipc_fd[1], F_SETFL, O_NONBLOCK);
        /* In multi-tread, we need only one thread to process
         * events on the pipe with master
         */
-       fd_insert(proc_self->ipc_fd[1], proc_self->ipc_fd, 
mworker_pipe_handler, 1);
+       fd_insert(proc_self->ipc_fd[1], proc_self->ipc_fd, 
mworker_accept_wrapper, 1);
        fd_want_recv(proc_self->ipc_fd[1]);
 }
 
-- 
2.16.4


Reply via email to