public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
From: Kenton Varda <kenton@cloudflare.com>
To: cygwin@cygwin.com
Subject: Re: sigpending() incorrectly returns signals pending on other threads
Date: Sun, 07 Jul 2019 02:16:00 -0000	[thread overview]
Message-ID: <CAJouXQksOsqWV=pVAJrXPn2FKtAyUO3QTxpugJ9R9rLGEN91UQ@mail.gmail.com> (raw)
In-Reply-To: <CAJouXQne09R+j+k+ZNw5fsZTFqDG5_9iwm9KQnxHoycL+ap_KQ@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1808 bytes --]

I found a second problem which may or may not be related:

If two threads use pthread_kill() to send each other the same signal,
such that the signal should be separately pending on both threads at
the same time, only one of the two signals is actually queued. It
seems that pthread_kill() is ignored if the same signal is already
pending on some other thread.

I've attached another demo program where two threads send each other
the same signal at the same time. On Linux and Mac, both are
delivered. On Cygwin, one or the other signal is delivered randomly,
but never both.

Same version: CYGWIN_NT-6.1 3.0.7(0.338/5/3) 2019-04-30 18:08

-Kenton

On Sat, Jul 6, 2019 at 3:46 PM Kenton Varda <kenton@cloudflare.com> wrote:
>
> Hello Cygwin,
>
> According to the (Linux) man page: "sigpending() returns the set of
> signals that are pending for delivery to the calling thread"
>
> However, on Cygwin, sigpending() seems to return the set of signals
> pending on any thread, as shown in the attached test program.
>
> Among other things, this can cause deadlocks in programs which use
> sigpending() to check for pending signals, then use sigsuspend() to
> induce delivery of the specific signals that are pending. Because the
> signal is not actually pending on the current thread, sigsuspend()
> will unexpectedly block, potentially forever.
>
> Output of test program:
> $ uname -srv
> CYGWIN_NT-6.1 3.0.7(0.338/5/3) 2019-04-30 18:08
> $ gcc -std=c11 -Wall test-sigpending.c -o test-sigpending -pthread &&
> ./test-sigpending
> sending signal to child thread with pthread_kill()...
> sigpending() says signal is pending in main thread (WRONG)
> sigpending() says signal is pending in child thread (CORRECT)
> received signal in child thread (CORRECT)
>
> The program works correctly on Linux.
>
> -Kenton

[-- Attachment #2: test-pthread-kill.c --]
[-- Type: text/x-csrc, Size: 2205 bytes --]

#define _GNU_SOURCE 1

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

#define sys(code) if ((code) < 0) { perror(#code); exit(1); }

pthread_t mainThread;
pthread_t childThread;

unsigned int receipts = 0;

void handler(int signo, siginfo_t* siginfo, void* context) {
  if (pthread_equal(pthread_self(), mainThread)) {
    printf("received signal in main thread\n");
    __atomic_fetch_or(&receipts, 1, __ATOMIC_RELAXED);
  } else if (pthread_equal(pthread_self(), childThread)) {
    printf("received signal in child thread\n");
    __atomic_fetch_or(&receipts, 2, __ATOMIC_RELAXED);
  } else {
    printf("received signal in other thread (?)\n");
  }
}

void* threadMain(void* param) {
  // Send SIGUSR1 to main thread.
  pthread_kill(mainThread, SIGUSR1);

  // Give main thread time to send us SIGUSR1.
  usleep(100000);

  // Unblock SIGUSR1 to see if it gets delivered.
  sigset_t mask;
  sys(sigemptyset(&mask));
  sys(sigaddset(&mask, SIGUSR1));
  sys(pthread_sigmask(SIG_UNBLOCK, &mask, NULL));

  return NULL;
}

int main() {
  // Register a signal handler for SIGUSR1.
  struct sigaction action;
  memset(&action, 0, sizeof(action));
  action.sa_sigaction = &handler;
  action.sa_flags = SA_SIGINFO;
  sys(sigaction(SIGUSR1, &action, NULL));

  // Block SIGUSR1.
  sigset_t mask;
  sys(sigemptyset(&mask));
  sys(sigaddset(&mask, SIGUSR1));
  sys(pthread_sigmask(SIG_BLOCK, &mask, NULL));

  // Start child thread.
  mainThread = pthread_self();
  if (pthread_create(&childThread, NULL, &threadMain, NULL) < 0) {
    fprintf(stderr, "pthread_create failed\n");
    exit(1);
  }

  // Send SIGUSR1 to child thread.
  pthread_kill(childThread, SIGUSR1);

  // Unblock SIGUSR1 to see if it gets delivered to this thread.
  sys(sigemptyset(&mask));
  sys(sigaddset(&mask, SIGUSR1));
  sys(pthread_sigmask(SIG_UNBLOCK, &mask, NULL));

  pthread_join(childThread, NULL);

  if (receipts == 3) {
    printf("both threads received signal (CORRECT)\n");
  } else if (receipts == 1 || receipts == 2) {
    printf("only one thread received signal (WRONG)\n");
  } else {
    printf("signal not delivered at all (WRONG)\n");
  }
}


[-- Attachment #3: Type: text/plain, Size: 219 bytes --]


--
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

  reply	other threads:[~2019-07-07  2:16 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-06 22:46 Kenton Varda
2019-07-07  2:16 ` Kenton Varda [this message]
2019-07-12 15:34   ` Corinna Vinschen
2019-07-14 13:19     ` Houder
2019-07-15  7:53       ` Corinna Vinschen
2019-07-15  9:54         ` Houder
2019-07-12 15:31 ` Corinna Vinschen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAJouXQksOsqWV=pVAJrXPn2FKtAyUO3QTxpugJ9R9rLGEN91UQ@mail.gmail.com' \
    --to=kenton@cloudflare.com \
    --cc=cygwin@cygwin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).