public inbox for cygwin@cygwin.com
 help / color / mirror / Atom feed
* sigpending() incorrectly returns signals pending on other threads
@ 2019-07-06 22:46 Kenton Varda
  2019-07-07  2:16 ` Kenton Varda
  2019-07-12 15:31 ` Corinna Vinschen
  0 siblings, 2 replies; 7+ messages in thread
From: Kenton Varda @ 2019-07-06 22:46 UTC (permalink / raw)
  To: cygwin

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

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-sigpending.c --]
[-- Type: text/x-csrc, Size: 2518 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;

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

void* threadMain(void* param) {
  // Give main thread time to send us SIGUSR1.
  usleep(100000);

  // Check what signals are pending with sigpending().
  sigset_t pending;
  sys(sigpending(&pending));
  if (sigismember(&pending, SIGUSR1)) {
    printf("sigpending() says signal is pending in child thread (CORRECT)\n");
  } else {
    printf("sigpending() says signal is NOT pending in child thread (WRONG)\n");
  }

  // Unblock SIGUSR1 to see if it gets delivered.
  sigset_t mask;
  sys(sigemptyset(&mask));
  sys(sigaddset(&mask, SIGUSR1));
  sys(sigprocmask(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(sigprocmask(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.
  printf("sending signal to child thread with pthread_kill()...\n");
  if (pthread_kill(childThread, SIGUSR1) < 0) {
    fprintf(stderr, "pthread_kill failed\n");
    exit(1);
  }

  // Check if sigpending() returns SIGUSR1.
  sigset_t pending;
  sys(sigpending(&pending));
  if (sigismember(&pending, SIGUSR1)) {
    printf("sigpending() says signal is pending in main thread (WRONG)\n");
  } else {
    printf("sigpending() says signal is NOT pending in main thread (CORRECT)\n");
  }

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

  pthread_join(childThread, NULL);
}


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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: sigpending() incorrectly returns signals pending on other threads
  2019-07-06 22:46 sigpending() incorrectly returns signals pending on other threads Kenton Varda
@ 2019-07-07  2:16 ` Kenton Varda
  2019-07-12 15:34   ` Corinna Vinschen
  2019-07-12 15:31 ` Corinna Vinschen
  1 sibling, 1 reply; 7+ messages in thread
From: Kenton Varda @ 2019-07-07  2:16 UTC (permalink / raw)
  To: cygwin

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: sigpending() incorrectly returns signals pending on other threads
  2019-07-06 22:46 sigpending() incorrectly returns signals pending on other threads Kenton Varda
  2019-07-07  2:16 ` Kenton Varda
@ 2019-07-12 15:31 ` Corinna Vinschen
  1 sibling, 0 replies; 7+ messages in thread
From: Corinna Vinschen @ 2019-07-12 15:31 UTC (permalink / raw)
  To: Kenton Varda; +Cc: cygwin

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

On Jul  6 15:46, Kenton Varda 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)

Thanks for the testcase.  This should work now in current git master.
I uploaded new developer snapshots to https://cygwin.com/snapshots/
Please give them a try.


Thanks,
Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: sigpending() incorrectly returns signals pending on other threads
  2019-07-07  2:16 ` Kenton Varda
@ 2019-07-12 15:34   ` Corinna Vinschen
  2019-07-14 13:19     ` Houder
  0 siblings, 1 reply; 7+ messages in thread
From: Corinna Vinschen @ 2019-07-12 15:34 UTC (permalink / raw)
  To: Kenton Varda; +Cc: cygwin

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

On Jul  6 19:15, Kenton Varda wrote:
> 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.

The current signal mechanism in Cygwin only allows for a signal to be
queued once.  Changing that is a pretty ambitioned task, which I simply
don't have enought time for.  However, patches to change that are more
than welcome.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: sigpending() incorrectly returns signals pending on other threads
  2019-07-12 15:34   ` Corinna Vinschen
@ 2019-07-14 13:19     ` Houder
  2019-07-15  7:53       ` Corinna Vinschen
  0 siblings, 1 reply; 7+ messages in thread
From: Houder @ 2019-07-14 13:19 UTC (permalink / raw)
  To: cygwin

On Fri, 12 Jul 2019 17:33:51, Corinna Vinschen  wrote:

> On Jul  6 19:15, Kenton Varda wrote:

> > 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.
> 
> The current signal mechanism in Cygwin only allows for a signal to be
> queued once.  Changing that is a pretty ambitioned task, which I simply
> don't have enought time for.  However, patches to change that are more
> than welcome.

.. uhm, just a note in the interest of accuracy ...

 - standard signals (which include USRSIG1 and USRSIG2) are not queued
   (traditional signal semantics)
 - only real-time signals should be queued ...

The executive (cygwin1.dll) must maintain a record of the signals that
are pending for the process as a whole,
 as well as a record of the signals that are pending for each thread.

A call to sigpending() returns the union of the set of signals that are
pending for the process
 and those that are pending for the calling thread.

Source: 33.2.1 (How the Unix Signal Model maps to threads) of LPI.

Undoubtedly, the signal mechanism in Cygwin must be "adapted", but
as far as I can tell, there is no requirement to "queue" any of the
standard signals ...

Regards
Henri


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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: sigpending() incorrectly returns signals pending on other threads
  2019-07-14 13:19     ` Houder
@ 2019-07-15  7:53       ` Corinna Vinschen
  2019-07-15  9:54         ` Houder
  0 siblings, 1 reply; 7+ messages in thread
From: Corinna Vinschen @ 2019-07-15  7:53 UTC (permalink / raw)
  To: cygwin

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

On Jul 14 15:19, Houder wrote:
> On Fri, 12 Jul 2019 17:33:51, Corinna Vinschen  wrote:
> 
> > On Jul  6 19:15, Kenton Varda wrote:
> 
> > > 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.
> > 
> > The current signal mechanism in Cygwin only allows for a signal to be
> > queued once.  Changing that is a pretty ambitioned task, which I simply
> > don't have enought time for.  However, patches to change that are more
> > than welcome.
> 
> .. uhm, just a note in the interest of accuracy ...
> 
>  - standard signals (which include USRSIG1 and USRSIG2) are not queued
>    (traditional signal semantics)
>  - only real-time signals should be queued ...
> 
> The executive (cygwin1.dll) must maintain a record of the signals that
> are pending for the process as a whole,
>  as well as a record of the signals that are pending for each thread.

Yeah, the latter is missing so far.

> A call to sigpending() returns the union of the set of signals that are
> pending for the process
>  and those that are pending for the calling thread.
> 
> Source: 33.2.1 (How the Unix Signal Model maps to threads) of LPI.
> 
> Undoubtedly, the signal mechanism in Cygwin must be "adapted", but
> as far as I can tell, there is no requirement to "queue" any of the
> standard signals ...

I think queing here means just what you outline above.  It's kind of a
queue of pending signals and in Cygwin it's actually literally a queue.


Corinna

-- 
Corinna Vinschen
Cygwin Maintainer

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: sigpending() incorrectly returns signals pending on other threads
  2019-07-15  7:53       ` Corinna Vinschen
@ 2019-07-15  9:54         ` Houder
  0 siblings, 0 replies; 7+ messages in thread
From: Houder @ 2019-07-15  9:54 UTC (permalink / raw)
  To: cygwin

On Mon, 15 Jul 2019 09:53:27, Corinna Vinschen  wrote:

> On Jul 14 15:19, Houder wrote:

> > .. uhm, just a note in the interest of accuracy ...
> >
> >  - standard signals (which include USRSIG1 and USRSIG2) are not queued
> >    (traditional signal semantics)
> >  - only real-time signals should be queued ...
> 
> I think queing here means just what you outline above.  It's kind of a
> queue of pending signals and in Cygwin it's actually literally a queue.

> It's >>> a kind of _queue_ <<< of pending signals ...

Not a Q if it would mean that a specific signal can be inserted more than
once in this Q ...

I am fully aware that you know all about Unix/Linux, but

    for the sake of clarity:

20.12 (Signals are not queued) of LPI

    "The set of pending signals is only a "mask"; it indicates whether or
     not a signal has occurred, but NOT how many times it has occurred. In
     other words, if the same signal is generated multiple times while it
     is blocked, then it is recorded in the set of pending signals, and
     later delivered, just ONCE. (One of the differences between standard
     and realtime signals is that realtime signals are queued, as discussed
     in Section 22.8.)

     Listing 20-6 and Listing 20-7 show two programs that can be used to
     observe that signals are not queued. ... "

Meaning the queue in Cygwin is not really a Q ...

Regards,
Henri


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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-07-15  9:54 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-06 22:46 sigpending() incorrectly returns signals pending on other threads Kenton Varda
2019-07-07  2:16 ` Kenton Varda
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

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