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,43 +2543,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]);
}