/* 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 #include #include #include #include #include #include #include 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; }