MEDIUM: mworker: leave when the master die

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.
This commit is contained in:
William Lallemand 2018-11-06 17:37:16 +01:00 committed by Willy Tarreau
parent 5d05db8ce1
commit 7216032e6f

View File

@ -2543,11 +2543,18 @@ void deinit(void)
deinit_pollers(); deinit_pollers();
} /* end deinit() */ } /* 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; char c;
int ret;
while (read(fd, &c, 1) == -1) { while (1) {
ret = recv(fd, &c, 1, MSG_PEEK);
if (ret == -1) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
if (errno == EAGAIN) { if (errno == EAGAIN) {
@ -2555,8 +2562,10 @@ void mworker_pipe_handler(int fd)
return; return;
} }
break; break;
} } else if (ret > 0) {
listener_accept(fd);
return;
} else if (ret == 0) {
/* At this step the master is down before /* At this step the master is down before
* this worker perform a 'normal' exit. * this worker perform a 'normal' exit.
* So we want to exit with an error but * So we want to exit with an error but
@ -2565,21 +2574,27 @@ void mworker_pipe_handler(int fd)
* deinit(). * deinit().
*/ */
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
}
}
return; 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() void mworker_pipe_register()
{ {
if (fdtab[proc_self->ipc_fd[1]].owner) /* The iocb should be already initialized with listener_accept */
/* already initialized */ if (fdtab[proc_self->ipc_fd[1]].iocb != listener_accept)
return; abort();
fcntl(proc_self->ipc_fd[1], F_SETFL, O_NONBLOCK); fcntl(proc_self->ipc_fd[1], F_SETFL, O_NONBLOCK);
/* In multi-tread, we need only one thread to process /* In multi-tread, we need only one thread to process
* events on the pipe with master * 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]); fd_want_recv(proc_self->ipc_fd[1]);
} }