Debugging zombies: pthread_sigmask and sigwait

Mel Flynn rflynn at acsalaska.net
Wed Apr 11 14:12:05 UTC 2012


Hi,

I'm currently stuck on a bug in Zarafa-spooler that creates zombies. and
working around it by claiming that our pthread library isn't "normal"
which uses standard signals rather then a signal thread.

My limited understanding of these facilities is however not enough to
see the actual problem here and reading of related manpages did not lead
me to a solution either. A test case reproducing the problem is attached.

What happens is that SIGCHLD is never received by the signal thread and
the child processes turn to zombies. Signal counters never go up, not
even for SIGINFO, which I added specifically to see if anything gets
through at all.

The signal thread shows being stuck in sigwait. It's reproducible on
8.3-PRERELEASE of a few days ago (r233768). I'm not able to test it on
anything newer unfortunately, but I suspect this is a bug/linuxism in
the code not in FreeBSD.

Thanks in advance for any insights.
-- 
Mel
-------------- next part --------------
PROG=spoolerbug
NO_MAN=yes
DEBUG_FLAGS=-g3
WARNS=6
WITH_DEBUG=yes
LDFLAGS+=-pthread

.include "../mk/core.mk"
.include <bsd.prog.mk>
-------------- next part --------------
/*
 * vim: ts=4 sw=4 tw=78 noet ai fdm=marker
 */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

#include <sys/types.h>
#include <sys/wait.h>

#include <pthread.h>
#include <signal.h> /* signal related */
#include <unistd.h> /* vfork */

#include <stdlib.h> /* arc4random() */
#include <stdbool.h>
#include <getopt.h>

#include <stdio.h> /* printing */

#include <err.h>

#define SERVER_ITERATIONS 3

/* declarations */
void *signal_handler(void *);
int running_server(void);
void process_signal(int);

/* globals */
pthread_t		signal_thread;
sigset_t		signal_mask;
bool			bQuit = false;
pid_t			lastPid = 0;
char			*szCommand;
size_t			n_sigs_handled = 0;
size_t			n_sigs_child = 0;
size_t			n_sigs_info = 0;

void *
signal_handler(void *args __unused)
{
	int sig;

	while( !bQuit && sigwait(&signal_mask, &sig) == 0 )
	{
		n_sigs_handled++;
		process_signal(sig);
	}

	return NULL;
}

int
running_server(void)
{
	u_int32_t r, max = 10;
	pid_t pid, me;
	int i = 0;

	me = getpid();
	warnx("[master]: Send SIGINFO to %u", (unsigned)me);
	do
	{
		warnx("[master]: lastPid = %u, n_sigs_handled=%zu, n_sigs_child=%zu"
				"n_sigs_info=%zu", (unsigned)lastPid, n_sigs_handled,
				n_sigs_child, n_sigs_info);
		pid = vfork();
		if( pid < 0 )
			break;
		if( pid == 0 )
		{
			execl(szCommand, getprogname(), "-F", NULL);
			_exit(EXIT_FAILURE);
		}
		else
		{
			if( bQuit )
				break;
			warnx("[master]: Child spawned with pid %u", (unsigned)pid);
			r = arc4random() % max;
			sleep((unsigned int)r);
		}
	} while( !bQuit && i++ < SERVER_ITERATIONS );
	return (0);
}

void
process_signal(int sig)
{
	int stat;
	pid_t pid;

	switch(sig)
	{
		case SIGTERM:
		case SIGINT:
			bQuit = true;
			break;
		case SIGCHLD:
			n_sigs_child++;
			while( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
			{
				lastPid = pid;
			}
			break;
		case SIGINFO:
			n_sigs_info++;
			break;
		default:
			signal(sig, SIG_IGN);
			break;
	}
}

int
main(int argc, char *argv[])
{
	bool bForked = false;
	const char *opts = "F";
	int ch, hr, rc;

	szCommand = argv[0];
	while( (ch = getopt(argc, argv, opts)) != -1 )
	{
		if( ch == 'F' )
			bForked = true;
	}

	argc -= optind;
	argv += optind;

	if( !bForked )
	{
		sigemptyset(&signal_mask);
		sigaddset(&signal_mask, SIGTERM);
		sigaddset(&signal_mask, SIGINT);
		sigaddset(&signal_mask, SIGCHLD);
		sigaddset(&signal_mask, SIGINFO);
	}

	daemon(1, 1);
	if( !bForked )
	{
		rc = pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
		if( rc != 0 )
			err(EXIT_FAILURE, "pthread_sigmask()");

		pthread_create(&signal_thread, NULL, signal_handler, NULL);
		hr = running_server();
		warnx("[master]: Joining signal thread");
		pthread_join(signal_thread, NULL);
	}
	else
	{
		printf("Child says hello\n");
		sleep(1);
		hr = 0;
	}

	return (hr);
}


More information about the freebsd-hackers mailing list