This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: pthread_kill: signals remain pending after target thread exits


Sorry for the delay.

From: Corinna Vinschen [corinna-cygwin@cygwin.com]
Sent: Friday, October 23, 2015 5:55 AM
> > I've attached a test case that I *think* gets into the right spot, at
> > least for 64-bit Cygwin 2.0.4.  That is, it hangs trying to receive
> > the signal, instead of terminating.  (This test passes (terminates) in
> > 32-bit Cygwin 1.7.9 and 64-bit Ubuntu 14.04.3 LTS.)
> 
> Thanks for the testcase.  I applied a patch which hopefully works as
> desired, at least to fix the immediate problem of the remaining pending
> signal when a thread exits.  I uploaded a new developer snapshot to
> https://cygwin.com/snapshots.  Please give it a try.

Thanks; that was fast!  I tried replacing cygwin1.dll with cygwin1-20151023.dll .

The original test case now works.  I checked some of my other tests,
and unfortunately some of them failed, so I extracted out a new test
case, which is attached.  My guess is that something is subtly different
about the timing on this test.

This new test hangs about half the time, and I have to use Windows Task Manager
to kill it (not Cygwin "kill"). Sometimes I see "pthread_kill: Unknown error -1" while
it hangs (meaning that pthread_kill returned -1 instead of an error number).

> > > >   - Multiple pending signals targeting different threads could
> > > >   coexist, even if they shared the same signal number.  This happens
> > > >   on Linux (Ubuntu 14.04.3), where I can generate two signals for two
> > > >   different threads, then sleep for a bit in each target thread, and
> > > >   finally have each thread receive its signal with sigwait()--neither
> > > >   signal is lost during the sleeping period.
> > >
> > > That requires to extend the handling for pending signals.  That's
> > > a rather bigger task...
> >
> > Yeah.  It's nice if threads don't interfere with each other, but this
> > part would indeed be harder to change.
> 
> I added that to my neverending TODO list.  Maybe I get around to it at
> one point.

I know the feeling.  No worries, and thanks.

-- John Carey
/* Copyright (c) 2015, Electric Cloud, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* This test program demonstates a Cygwin bug in which a signal sent
 * to a particular thread remains pending after the thread terminates.
 *
 * Somehow even though the original fix worked for test_pending_signal.c,
 * about half the time this test case triggers a hang that must be killed
 * using the Windows Task Manager instead of Cygwin "kill".  Sometimes we
 * see "pthread_kill: Unknown error -1" during the hang.
 */

#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


static void check_syscall(char const *context, int result)
{
    if (result == -1) {
        fprintf(stderr, "%s: %s\n", context, strerror(errno));
        exit(EXIT_FAILURE);
    }
}

static void check_threadcall(char const *context, int error_number)
{
    if (error_number) {
        fprintf(stderr, "%s: %s\n", context, strerror(error_number));
        exit(EXIT_FAILURE);
    }
}


typedef struct shared_struct {
    sigset_t signal_mask;  /* signals to block */
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int eat;
    int done;
} shared_t;


static void *phage_thread (void *arg)
{
    shared_t *shared = (shared_t *)arg;

    check_threadcall("pthread_mutex_lock",
            pthread_mutex_lock(&shared->mutex));

    if (shared->eat <= 1) {
        shared->eat = 1;
        check_threadcall("pthread_cond_broadcast",
                pthread_cond_broadcast(&shared->cond));

        while (shared->eat <= 1) {
            check_threadcall("pthread_cond_wait",
                    pthread_cond_wait(&shared->cond, &shared->mutex));
        }
    }

    check_threadcall("pthread_mutex_unlock",
            pthread_mutex_unlock(&shared->mutex));
    return NULL;
}

static void *signal_thread (void *arg)
{
    shared_t *shared = (shared_t *)arg;
    int sig_caught;
    int ern;

    check_threadcall("sigwait",
            sigwait(&shared->signal_mask, &sig_caught));

    if (sig_caught == SIGUSR2) {
        puts("Received SIGUSR2");
        fflush(stdout);
    } else {
        fprintf (stderr, "\nUnexpected signal %d\n", sig_caught);
    }

    check_threadcall("pthread_mutex_lock",
            pthread_mutex_lock(&shared->mutex));

    shared->done = 1;

    check_threadcall("pthread_cond_broadcast",
            pthread_cond_broadcast(&shared->cond));

    check_threadcall("pthread_mutex_unlock",
            pthread_mutex_unlock(&shared->mutex));

    return NULL;
}


int main(int argc, char **argv)
{
    shared_t *shared = (shared_t *)malloc(sizeof(shared_t));
    pthread_t phage_tid, signal_tid;
    struct timespec ts;
    int ern;

    sigemptyset(&shared->signal_mask);
    sigaddset(&shared->signal_mask, SIGUSR2);

    check_threadcall("pthread_mutex_init",
            pthread_mutex_init(&shared->mutex, NULL));

    check_threadcall("pthread_cond_init",
            pthread_cond_init(&shared->cond, NULL));

    check_threadcall("pthread_sigmask",
            pthread_sigmask(SIG_BLOCK, &shared->signal_mask, NULL));

    shared->eat = 0;
    shared->done = 0;

    check_threadcall("pthread_create",
            pthread_create(&phage_tid, NULL, phage_thread, shared));

    check_threadcall("pthread_mutex_lock",
            pthread_mutex_lock(&shared->mutex));

    while (shared->eat == 0) {
        check_threadcall("pthread_cond_wait",
                pthread_cond_wait(&shared->cond, &shared->mutex));
    }

    check_threadcall("pthread_kill",
            pthread_kill(phage_tid, SIGUSR2));

    shared->eat = 2;

    check_threadcall("pthread_cond_broadcast",
            pthread_cond_broadcast(&shared->cond));

    check_threadcall("pthread_mutex_unlock",
            pthread_mutex_unlock(&shared->mutex));

    check_threadcall("pthread_join",
            pthread_join(phage_tid, NULL));

    check_threadcall("pthread_create",
            pthread_create(&signal_tid, NULL, signal_thread, shared));

    check_threadcall("pthread_mutex_lock",
            pthread_mutex_lock(&shared->mutex));

    for (;;) {
        if (shared->done) {
            check_threadcall("pthread_join",
                    pthread_join(signal_tid, NULL));
            break;
        } {
            ern = pthread_kill(signal_tid, SIGUSR2);
            if (ern != ESRCH) {
                check_threadcall("pthread_kill", ern);
            }

            check_syscall("clock_gettime",
                    clock_gettime(CLOCK_REALTIME, &ts));

            ts.tv_nsec += 100000000;
            if (ts.tv_nsec >= 1000000000) {
                ts.tv_nsec -= 1000000000;
                ts.tv_sec += 1;
            }

            check_threadcall("pthread_cond_timedwait",
                pthread_cond_timedwait(&shared->cond, &shared->mutex, &ts));
        }
    }

    check_threadcall("pthread_mutex_unlock",
            pthread_mutex_unlock(&shared->mutex));

    return EXIT_SUCCESS;
}
--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]