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