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
PROG=spoolerbug
NO_MAN=yes
DEBUG_FLAGS=-g3
WARNS=6
WITH_DEBUG=yes
LDFLAGS+=-pthread

.include "../mk/core.mk"
.include <bsd.prog.mk>
/*
 * 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);
}
_______________________________________________
freebsd-hackers@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "freebsd-hackers-unsubscr...@freebsd.org"

Reply via email to