-
Notifications
You must be signed in to change notification settings - Fork 155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deadlock on shutdown #5309
Comments
Further debugging shows that the child process is not getting reaped by This only seems to happen on shutdown, but perhaps during normal operation the missing SIGCHLD doesn't matter because subsequent SIGCHLD signals are detected and cause To fix this, I've added an explicit call to }
if (in_shutdown)
reap_child();
gettimeofday(&now, 0);
child_janitor(now);
do_prom_report(now);
}
finished: |
Shot in the dark, but does this make any difference:
I think it shouldn't make a difference -- One thing I worry a little about is that |
That patch is against the master branch, but I think it should apply cleanly to most versions, this stuff hasn't changed in a while. |
I think the earlier (Linux) report of this was fixed by #3449. It detects when So it might simply be that, during shutdown and the flurry of processes exiting, some of those SIGCHLD signals wind up interrupting the waitpid, and that's how they get missed. If this is what's happening, it should be an easy fix, I'll have a new patch for you to try shortly. |
Try this on for size:
|
Thanks. I've tried this one, and it didn't help. Regarding the comment about the order mattering: if the ordering were the other way around, it would be possible for:
This would not be an issue when I'll try the other patch next. |
Thanks. I've tried this one too, and it didn't work unfortunately. I also (separately) tried the fix in #3449, which is not in 3.8.5, but it also didn't help. Note also Dilyan's comment on my post on the info mailing list recently: Let me see what other information I can gather. |
Calling Will investigate further. |
Interesting. I wonder if there's a race in our signal block/unblock stuff around the pselect. The waitpid documentation talks about a bunch of slightly different behaviours depending on the status of the SIGCHLD handler, which I don't completely understand at a first read. But if there's a gap around the pselect that the signal can surprise us in, then maybe we're hitting one of those slightly different behaviours when it does. |
Another data point is that there is that Digging into the FreeBSD kernel code, I see the code below. Note the comment in particular. I'm not an expert in the kernel code, but it seems to imply that only signals that occur during the int
kern_pselect(struct thread *td, int nd, fd_set *in, fd_set *ou, fd_set *ex,
struct timeval *tvp, sigset_t *uset, int abi_nfdbits)
{
int error;
if (uset != NULL) {
error = kern_sigprocmask(td, SIG_SETMASK, uset,
&td->td_oldsigmask, 0);
if (error != 0)
return (error);
td->td_pflags |= TDP_OLDMASK;
}
error = kern_select(td, nd, in, ou, ex, tvp, abi_nfdbits);
if (uset != NULL) {
/*
* Make sure that ast() is called on return to
* usermode and TDP_OLDMASK is cleared, restoring old
* sigmask. If we didn't get interrupted, then the caller is
* likely not expecting a signal to hit that should normally be
* blocked by its signal mask, so we restore the mask before
* any signals could be delivered.
*/
if (error == EINTR) {
ast_sched(td, TDA_SIGSUSPEND);
} else {
/* *select(2) should never restart. */
MPASS(error != ERESTART);
ast_sched(td, TDA_PSELECT);
}
}
return (error);
} Looking at the FreeBSD commit history, it looks like this changes was made about 3 months ago, which is when I started seeing the problem, so the change in Cyrus version is probably just coincidental. The change is discussed here: https://reviews.freebsd.org/D47738 and here: https://reviews.freebsd.org/D47741. The particular issue that they were seeing was slightly different - in their case the signal is received after the One of the comments says:
This thread, from StackOverflow suggests that this (the new behaviour on FreeBSD, as of 3 months ago) is the correct behaviour, and that it matches the Linux behaviour: https://stackoverflow.com/questions/62315082/pselect-on-linux-does-not-deliver-signals-if-events-are-pending. That thread quotes the Posix specification on this: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html
In my case, one of the selected file descriptors is ready. So, I think the new behaviour of FreeBSD's I'll write some test code for this scenario. Potential fixes:
The first two will introduce 2 (for the first option) or 1 (for the second option) additional system calls, which could perhaps be a performance issue, but I don't know how significant that will be. |
Testing with separate test code on Linux (kernel 5.15.88) and FreeBSD (13.4-STABLE):
I consider 4 scenarios for completeness. Scenario 1 below is the one I'm seeing in
The results for the above scenarios are:
|
That's some good research!
This feels the right track to me. Like, particular kernel implementations/fixes aside, it sounds like we can't portably rely on pselect to deliver signals which were already pending before the pselect call, and so we should open a little window of our own for them to arrive in. In fact, here's what the Linux man page for pselect says (emphasis mine):
So it looks like opening a little window is exactly what we were meant to be doing all along. It's interesting that it suggests putting that window before the pselect call, rather than after. I think I would put it in I'm not sure how the two extra syscalls might affect performance, but if we were supposed to be doing that all along, it can't hurt that bad? |
Sorry @elliefm, this was meant to be a reply to your comment #5309 (comment). I suspect we need to put the additional calls to I don't know if Greg Banks, who originally made that change, is still available to comment. So this (in /* run any scheduled processes */
if (!in_shutdown)
spawn_schedule(now);
#if HAVE_PSELECT
sigset_t old_sigmask;
sigprocmask(SIG_SETMASK, &pselect_sigmask, &old_sigmask);
sigprocmask(SIG_SETMASK, &old_sigmask, &pselect_sigmask);
#endif
/* reap first, that way if we need to babysit we will */
if (gotsigchld) {
/* order matters here */
gotsigchld = 0;
reap_child();
} rather than this: static int myselect(int nfds, fd_set *rfds, fd_set *wfds,
fd_set *efds, struct timeval *tout)
{
#if HAVE_PSELECT
sigset_t old_sigmask;
sigprocmask(SIG_SETMASK, &pselect_sigmask, &old_sigmask);
sigprocmask(SIG_SETMASK, &old_sigmask, &pselect_sigmask);
/* pselect() closes the race between SIGCHLD arriving
* and select() sleeping for up to 10 seconds. */
struct timespec ts, *tsptr = NULL;
if (tout) {
ts.tv_sec = tout->tv_sec;
ts.tv_nsec = tout->tv_usec * 1000;
tsptr = &ts;
}
return pselect(nfds, rfds, wfds, efds, tsptr, &pselect_sigmask);
#else
return select(nfds, rfds, wfds, efds, tout);
#endif
} |
At least version 3.8.4 of cyrus imapd sometimes (quite often) hangs indefinitely on shutdown.
My original mail regarding this is here:
https://cyrus.topicbox.com/groups/info/Te092838a4fe380d4/version-3-8-4-hangup-on-shutdown
The problem seems to have started somewhere between version 3.4 and version 3.8.4, but I note that there was at least one previous report on an earlier version: https://cyrus.topicbox.com/groups/devel/Tddff7ee5049ffbc4 It could just be that timing of the shutdown has changed subtly.
My debugging attempts show that it's waiting here (below) for at least one of the child processes to stop:
It looks like one of the child processes is not getting correctly waited on:
I'm looking into this to try and get more details.
Note that this is on FreeBSD 13.4, but the previous reporter was apparently using some Linux variety.
The text was updated successfully, but these errors were encountered: