Bug 7510 - Various problems with pthreads and fork/wait/signal handling
Various problems with pthreads and fork/wait/signal handling
Status: CLOSED CURRENTRELEASE
Product: Red Hat Linux
Classification: Retired
Component: glibc (Show other bugs)
6.1
i386 Linux
medium Severity medium
: ---
: ---
Assigned To: Jakub Jelinek
:
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 1999-12-02 10:28 EST by martin
Modified: 2008-05-01 11:37 EDT (History)
1 user (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2003-01-24 18:44:06 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)

  None (edit)
Description martin 1999-12-02 10:28:14 EST
On 'Other UNIXs' (namely AIX 4.2 and HP-UX 11.0) the following program
'works' - that is, it runs to completion without using CTRL-C at the
terminal. On linux it hangs.

The basic idea is is this:

    main():
        Block SIGCHLD using pthread_sigmask/sigprocmask

        Initialize a pthread condition and its mutex

        Start thread_sig_handler thread

        fork()

        (See CHLD for child. This is the parent)

        Wait on the pthread condition

        Wait on end of thread_sig_handler

        exit

    CHLD:
        execl("/bin/sh", "sh", "-c", "echo HI THERE", NULL);

    thread_sig_handler:
        sigwait on SIGCHLD (plus SIGQUIT and SIGINT for safety)

        post the pthread condition

        return

i.e.:

So, basically, one thread is set up as a pseudo signal handler for SIGCHLD
while another thread (or the main thread) does a fork/exec, the parent
waiting until the signal handler has done its bit (signalled by a pthread
condition variable), the child running something that will eventually end,
generating a SIGCHLD signal.

On Linux the handler thread never gets woken up (until CTRL-C).

I tried a modified version too - add a signal handler that does a
pthread_kill(handler_thread, SIGCHLD). That wakes it up alright - but the
handler thread then attempts a waitpid - and receives ECHILD. So it cannot
access the child process.

This is the initial problem. I also have others with pthreads - like an
execl from a forked child seems to kill the signale handler thread in some
circumstances, the nature of which I cannot discover! -

Thirdly note that getpid from a thread does NOT return the 'process id' as
one would hope in a merely threaded program - it returns a completely
different process ID!

In general, it seems that pthreads/fork/execl/wait do not work together on
Linux - and that pthreads is still a bit buggy! and they are essential to
the AIX/HP-UX application that I am porting!

Compile this source to test the original problem it:
....................

/*
 *  sigwaittest.c - a pthreads facility tester!
 *
 * This program tests the ability of a pthreads implementation to handle
 * SIGCHLD correctly - some don't. e.g. AIX and HP-UX send the SIGCHLD
 * signal to any thread that waits on SIGCHLD (correct), whereas Linux 2.2
 * and Solaris 2.5 send it directly to the forking thread (wrong, I think);
 *
 * To compile:
 *
 *  Linux:      cc   -o sigwaittest -lpthread  sigwaittest.c
 *  HP-UX:      cc   -o sigwaittest -lpthread  sigwaittest.c
 *  AIX:        cc_r -o sigwaittest -lpthreads sigwaittest.c
 *  SOLARIS:    CC   -o sigwaittest -lpthread -DSOLARIS
 *                      -D_POSIX_PTHREAD_SEMANTICS sigwaittest.c
 *
 * To run:
 *
 *   Enter
 *     sigwaittest<ENTER>
 *
 *   You SHOULD get the following if it works:
 *
 *      Main: entry - pid=<pid>
 *      Main: Blocking SIGCHLD
 *      Main: Initializing condition
 *      Main: Initializing thread attributes:
 *   Main: kicking off thread
 *      FORKER: forking
 *      CHILD: entry - pid=<pid>
 *      THREAD HANDLER: entry - pid=<pid>
 *      FORKER: got child <pid>!
 *      FORKER: Waiting on condition
 *      THREAD HANDLER: Waiting on sig_chld
 *          <A few seconds wait>
 *      SHL: quiting
 *      THREAD HANDLER: Caught signal <signal number> (SIGCHLD)
 *      THREAD HANDLER: Getting child info
 *      THREAD HANDLER: wait pid=<pid>, stat=0 (errno=0/<err msg>)
 *      THREAD HANDLER: Setting condition
 *      THREAD HANDLER: quiting
 *      FORKER: Condition set; quiting
 *      Main: quiting
 *
 *  or this if it fails:
 *
 *      Main: entry - pid=<pid>
 *      Main: Blocking SIGCHLD
 *      Main: Initializing condition
 *      Main: Initializing thread attributes:
 *      Main: kicking off thread
 *      FORKER: forking
 *      FORKER: got child <pid>!
 *      CHILD: entry - pid=<pid>
 *      FORKER: Waiting on condition
 *      THREAD HANDLER: entry - pid=<pid>
 *      THREAD HANDLER: Waiting on sig_chld
 *          <A few seconds wait>
 *      SHL: quiting
 *          <An infinite wait. Hit CTRL-C to release it>
 *      THREAD HANDLER: Caught signal <signal number> (NOT SIGCHLD)
 *      THREAD HANDLER: Setting condition
 *      THREAD HANDLER: quiting
 *      FORKER: Condition set; quiting
 *      Main: quiting
 *
 *  Note that the order of some messages may differ since we are talking
 *  about multpile threads of execution here.
 */
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
extern char *sys_errlist[];
#if SOLARIS
/*
 * SOLARIS differences
 */
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#define SHL "/sbin/sh"

#else

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

#if _AIX
/*
 * AIX differences
 */
#define pthread_sigmask(__A,__B,__C) sigthreadmask(__A,__B,__C)
#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
#define SHL "/usr/bin/sh"

#elif _HPUX_SOURCE
/*
 * HP-UX
 */
#define SHL "/usr/bin/sh"

#else
/*
 * LINUX
 */
#define SHL "/bin/sh"

#endif
#endif

pthread_mutex_t cond_mutex;
pthread_cond_t  cond;
int done = 0;

void *thread_handler_routine(void *should_i);
void  forker_routine(void);

int main()
{
    pthread_t pt1;
    sigset_t set, old;
    pthread_attr_t pta;

    printf("Main: entry - pid=%i\n", getpid());

    sigfillset(&set);
    sigaddset(&set, SIGCHLD);

    printf("Main: Blocking SIGCHLD\n");

    pthread_sigmask(SIG_BLOCK, &set, &old);

    printf("Main: Initializing condition\n");

    pthread_cond_init( &cond,       NULL);
    pthread_mutex_init(&cond_mutex, NULL);

    printf("Main: Initializing thread attributes:\n");
    pthread_attr_init(&pta);
    pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_JOINABLE);

    printf("Main: kicking off thread\n");

    pthread_create(&pt1, &pta, thread_handler_routine, (void *)0);
    forker_routine();

    pthread_join(pt1, NULL);

    printf("Main: quiting\n");

   return 0;
}


void forker_routine(void)
{

    pid_t chld;

    printf("FORKER: forking\n");

    if ((chld = fork()) == 0)
    {
        printf("CHILD: entry - pid=%i\n", getpid());
        execl(SHL, "sh", "-c", "sleep 10; echo SHELL: quiting", NULL);
        printf("CHILD: quiting\n");
        exit(0);
    }
    else
    {
        printf("FORKER: got child %i!\n", chld);
        printf("FORKER: Waiting on condition\n");

        pthread_mutex_lock(&cond_mutex);
        if (!done)
            pthread_cond_wait(&cond, &cond_mutex);
        pthread_mutex_unlock(&cond_mutex);

        printf("FORKER: Condition set; quiting\n");
        return;
    }
}


void *thread_handler_routine(void *parm)
{
    pid_t pid;
    sigset_t set, old;
    int sig, status;

    printf("THREAD HANDLER: entry - pid=%i\n", getpid());

    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigaddset(&set, SIGQUIT); /* for safety! */
    sigaddset(&set, SIGINT);  /* for safety! */

    printf("THREAD HANDLER: Waiting on sig_chld\n");

    sigwait(&set, &sig);

    printf("THREAD HANDLER: Caught signal %i (%s)\n",
        sig, (sig == SIGCHLD) ? "SIGCHLD" : "NOT SIGCHLD");

    if (sig == SIGCHLD)
    {
        printf("THREAD HANDLER: Getting child info\n");
        errno=0;
        while ((pid = waitpid(-1, &status, WNOHANG)) == -1
                && errno == EINTR);

        printf("THREAD HANDLER: wait: pid=%i, stat=%i (errno=%i/%s)\n",
          pid, status, errno, sys_errlist[errno]);
    }

    printf("THREAD HANDLER: Setting condition\n");

    pthread_mutex_lock(&cond_mutex);
    done = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&cond_mutex);

    printf("THREAD HANDLER: quiting\n");

    return NULL;
}

---------------------------------
Comment 1 Lamar Owen 1999-12-07 11:41:59 EST
GNU PR# 1273: http://www-gnats.gnu.org:8080/cgi-bin/wwwgnats.pl/full/1273
GNU PR# 1305: http://www-gnats.gnu.org:8080/cgi-bin/wwwgnats.pl/full/1305

The short of it is a need to upgrade to 2.1.3 for certain programs.  The
multithreaded webserver AOLserver also has problems with glibc 2.1.2, whereas it
did not have problems with 2.1.1.

--
Lamar Owen
WGCR Internet Radio
Comment 2 Cristian Gafton 2000-05-22 10:53:59 EDT
assign to jakub
Comment 3 Stephen John Smoogen 2003-01-24 18:44:06 EST
Bug 7510 seems to have been fixed in newer versions of glibc that came out after
6.1. You SHOULD test this against the 8.0.93 Phoebe beta since it incorporates a
new threading model in glibc. It will also have more eyes on fixing it in that
release.

Note You need to log in before you can comment on or make changes to this bug.