These patches add the 'sigdelay' module, that I already presented a couple
of days ago. It blocks a signal from delivery across the entire process, not
only in a single thread.


2026-04-15  Bruno Haible  <[email protected]>

        sigdelay: Add tests.
        * tests/test-sigdelay1.c: New file, based on
        tests/test-pthread_sigmask1.c.
        * tests/test-sigdelay2.c: New file, based on
        tests/test-pthread_sigmask2.c.
        * modules/sigdelay-tests: New file.

        sigdelay: New module.
        * lib/sigdelay.h: New file.
        * lib/sigdelay.c: New file.
        * modules/sigdelay: New file.

2026-04-15  Bruno Haible  <[email protected]>

        pthread_sigmask tests: Enhance test.
        * tests/test-pthread_sigmask2.c (killer_thread1): Renamed from
        killer_thread.
        (killer_thread2): New variable.
        (killer_thread1_func): Renamed from killer_thread_func.
        (killer_thread2_func): New function.
        (main): Add a second test with killer_thread2.

2026-04-15  Bruno Haible  <[email protected]>

        pthread_sigmask tests: More details about VirtualBox / KVM bug.
        * tests/test-pthread_sigmask1.c (main): Refine comment regarding
        VirtualBox / KVM bug.
        * tests/test-pthread_sigmask2.c (main): Likewise.

From 3daeaa60a16bd320950b402b64164f96316c2196 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 15 Apr 2026 13:45:35 +0200
Subject: [PATCH 1/4] pthread_sigmask tests: More details about VirtualBox /
 KVM bug.

* tests/test-pthread_sigmask1.c (main): Refine comment regarding
VirtualBox / KVM bug.
* tests/test-pthread_sigmask2.c (main): Likewise.
---
 ChangeLog                     | 7 +++++++
 tests/test-pthread_sigmask1.c | 3 ++-
 tests/test-pthread_sigmask2.c | 3 ++-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fb6babd1ff..4bef6f5975 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2026-04-15  Bruno Haible  <[email protected]>
+
+	pthread_sigmask tests: More details about VirtualBox / KVM bug.
+	* tests/test-pthread_sigmask1.c (main): Refine comment regarding
+	VirtualBox / KVM bug.
+	* tests/test-pthread_sigmask2.c (main): Likewise.
+
 2026-04-14  Bruno Haible  <[email protected]>
 
 	sigprocmask: On mingw, avoid dependency on libwinpthread.
diff --git a/tests/test-pthread_sigmask1.c b/tests/test-pthread_sigmask1.c
index d97e75d27a..e088eb61ea 100644
--- a/tests/test-pthread_sigmask1.c
+++ b/tests/test-pthread_sigmask1.c
@@ -46,7 +46,8 @@ int
 main ()
 {
   /* This test occasionally fails on Linux (glibc or musl libc), in a
-     VirtualBox VM with paravirtualization = Default or KVM, with ??? 2 CPUs.
+     VirtualBox VM with paravirtualization = Default or KVM, with ??? 2 CPUs,
+     when "Nested VT-x/AMD-V" and "PAE/NX" are not both enabled.
      Skip the test in this situation.  */
   if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
     {
diff --git a/tests/test-pthread_sigmask2.c b/tests/test-pthread_sigmask2.c
index 7c71984ac6..c4b6c1af44 100644
--- a/tests/test-pthread_sigmask2.c
+++ b/tests/test-pthread_sigmask2.c
@@ -53,7 +53,8 @@ int
 main ()
 {
   /* This test occasionally fails on Linux (glibc or musl libc), in a
-     VirtualBox VM with paravirtualization = Default or KVM, with ??? 2 CPUs.
+     VirtualBox VM with paravirtualization = Default or KVM, with ??? 2 CPUs,
+     when "Nested VT-x/AMD-V" and "PAE/NX" are not both enabled.
      Skip the test in this situation.  */
   if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
     {
-- 
2.52.0

>From 2448235dbfa03df0814f3ed823fb9cd7b11cc5ef Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 15 Apr 2026 13:45:44 +0200
Subject: [PATCH 2/4] pthread_sigmask tests: Enhance test.

* tests/test-pthread_sigmask2.c (killer_thread1): Renamed from
killer_thread.
(killer_thread2): New variable.
(killer_thread1_func): Renamed from killer_thread_func.
(killer_thread2_func): New function.
(main): Add a second test with killer_thread2.
---
 ChangeLog                     | 10 ++++++++
 tests/test-pthread_sigmask2.c | 43 +++++++++++++++++++++++++++++------
 2 files changed, 46 insertions(+), 7 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4bef6f5975..318316cd17 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2026-04-15  Bruno Haible  <[email protected]>
+
+	pthread_sigmask tests: Enhance test.
+	* tests/test-pthread_sigmask2.c (killer_thread1): Renamed from
+	killer_thread.
+	(killer_thread2): New variable.
+	(killer_thread1_func): Renamed from killer_thread_func.
+	(killer_thread2_func): New function.
+	(main): Add a second test with killer_thread2.
+
 2026-04-15  Bruno Haible  <[email protected]>
 
 	pthread_sigmask tests: More details about VirtualBox / KVM bug.
diff --git a/tests/test-pthread_sigmask2.c b/tests/test-pthread_sigmask2.c
index c4b6c1af44..31a880a840 100644
--- a/tests/test-pthread_sigmask2.c
+++ b/tests/test-pthread_sigmask2.c
@@ -31,16 +31,25 @@
 #if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
 
 static pthread_t main_thread;
-static pthread_t killer_thread;
+static pthread_t killer_thread1;
+static pthread_t killer_thread2;
 
 static void *
-killer_thread_func (_GL_UNUSED void *arg)
+killer_thread1_func (_GL_UNUSED void *arg)
 {
   sleep (1);
   pthread_kill (main_thread, SIGINT);
   return NULL;
 }
 
+static void *
+killer_thread2_func (_GL_UNUSED void *arg)
+{
+  sleep (1);
+  pthread_kill (pthread_self (), SIGINT);
+  return NULL;
+}
+
 static volatile int sigint_occurred;
 
 static void
@@ -63,10 +72,9 @@ main ()
       return 77;
     }
 
-  sigset_t set;
-
   signal (SIGINT, sigint_handler);
 
+  sigset_t set;
   sigemptyset (&set);
   sigaddset (&set, SIGINT);
 
@@ -81,7 +89,8 @@ main ()
 
   /* Request a SIGINT signal from another thread.  */
   main_thread = pthread_self ();
-  ASSERT (pthread_create (&killer_thread, NULL, killer_thread_func, NULL) == 0);
+  ASSERT (pthread_create (&killer_thread1, NULL, killer_thread1_func, NULL)
+          == 0);
 
   /* Wait.  */
   sleep (2);
@@ -98,9 +107,29 @@ main ()
         before the call to pthread_sigmask() returns."  */
   ASSERT (sigint_occurred == 1);
 
-  /* Clean up the thread.  This avoid a "ThreadSanitizer: thread leak" warning
+  /* Request a SIGINT signal from another thread.  */
+  ASSERT (pthread_create (&killer_thread2, NULL, killer_thread2_func, NULL)
+          == 0);
+
+  /* Block SIGINT.  */
+  ASSERT (pthread_sigmask (SIG_BLOCK, &set, NULL) == 0);
+
+  /* Wait.  */
+  sleep (2);
+
+  /* The signal should have arrived yet, because it is blocked only in the
+     main thread, not in killer_thread2.  */
+  ASSERT (sigint_occurred == 2);
+
+  /* Unblock SIGINT.  */
+  ASSERT (pthread_sigmask (SIG_UNBLOCK, &set, NULL) == 0);
+
+  ASSERT (sigint_occurred == 2);
+
+  /* Clean up the threads.  This avoids a "ThreadSanitizer: thread leak" warning
      from "gcc -fsanitize=thread".  */
-  ASSERT (pthread_join (killer_thread, NULL) == 0);
+  ASSERT (pthread_join (killer_thread1, NULL) == 0);
+  ASSERT (pthread_join (killer_thread2, NULL) == 0);
 
   return test_exit_status;
 }
-- 
2.52.0

From 006d544083a6a595b1220b4fec409ea9f2dbf501 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 15 Apr 2026 13:45:52 +0200
Subject: [PATCH 3/4] sigdelay: New module.

* lib/sigdelay.h: New file.
* lib/sigdelay.c: New file.
* modules/sigdelay: New file.
---
 ChangeLog        |   7 +++
 lib/sigdelay.c   | 119 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/sigdelay.h   |  54 +++++++++++++++++++++
 modules/sigdelay |  31 ++++++++++++
 4 files changed, 211 insertions(+)
 create mode 100644 lib/sigdelay.c
 create mode 100644 lib/sigdelay.h
 create mode 100644 modules/sigdelay

diff --git a/ChangeLog b/ChangeLog
index 318316cd17..b1079e902d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2026-04-15  Bruno Haible  <[email protected]>
+
+	sigdelay: New module.
+	* lib/sigdelay.h: New file.
+	* lib/sigdelay.c: New file.
+	* modules/sigdelay: New file.
+
 2026-04-15  Bruno Haible  <[email protected]>
 
 	pthread_sigmask tests: Enhance test.
diff --git a/lib/sigdelay.c b/lib/sigdelay.c
new file mode 100644
index 0000000000..d69fa6126b
--- /dev/null
+++ b/lib/sigdelay.c
@@ -0,0 +1,119 @@
+/* Delaying the delivery of signals to the current process.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigdelay.h"
+
+#include <errno.h>
+#include <stdcountof.h>
+#include <stdlib.h>
+
+#include "glthread/lock.h"
+#include "thread-optim.h"
+#include "sig-handler.h"
+
+/* State regarding a single signal.  */
+struct state
+{
+  /* Number of times sigdelay (SIG_BLOCK, ...) was invoked for this signal.  */
+  unsigned int count;
+  /* If count > 0: The original action for the signal.  */
+  struct sigaction saved_action;
+  /* Whether this signal was caught and is waiting to be re-delivered.  */
+  unsigned int volatile caught;
+};
+
+/* The state for all signals.
+   Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34.  */
+static struct state states[64];
+
+static _GL_ASYNC_SAFE void
+delaying_handler (int sig)
+{
+  if (sig >= 0 && sig < countof (states))
+    states[sig].caught = 1;
+}
+
+/* Lock that makes sigdelay multi-thread safe.  */
+gl_lock_define_initialized (static, sigdelay_lock)
+
+int
+sigdelay (int how, const sigset_t *restrict set, sigset_t *restrict old_set)
+{
+  if (!(how == SIG_BLOCK || how == SIG_UNBLOCK))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (old_set != NULL)
+    sigemptyset (old_set);
+
+  bool mt = gl_multithreaded ();
+
+  if (mt) gl_lock_lock (sigdelay_lock);
+
+  if (old_set != NULL)
+    for (int sig = 1; sig < countof (states); sig++)
+      if (states[sig].count > 0)
+        sigaddset (old_set, sig);
+
+  for (int sig = 1; sig < countof (states); sig++)
+    if (sigismember (set, sig))
+      {
+        switch (how)
+          {
+          case SIG_BLOCK:
+            if (states[sig].count == 0)
+              {
+                states[sig].caught = 0;
+
+                struct sigaction delaying_action;
+                delaying_action.sa_handler = delaying_handler;
+                delaying_action.sa_flags = SA_NODEFER;
+                sigemptyset (&delaying_action.sa_mask);
+                if (sigaction (sig, &delaying_action,
+                               &states[sig].saved_action) < 0)
+                  abort ();
+              }
+            states[sig].count++;
+            break;
+
+          case SIG_UNBLOCK:
+            if (states[sig].count == 0)
+              /* Invalid call.  */
+              abort ();
+            states[sig].count--;
+            if (states[sig].count == 0)
+              {
+                unsigned int caught = states[sig].caught;
+                if (sigaction (sig, &states[sig].saved_action, NULL) < 0)
+                  abort ();
+                if (caught)
+                  raise (sig);
+              }
+            break;
+          }
+      }
+
+  if (mt) gl_lock_unlock (sigdelay_lock);
+
+  return 0;
+}
diff --git a/lib/sigdelay.h b/lib/sigdelay.h
new file mode 100644
index 0000000000..18489bfb1d
--- /dev/null
+++ b/lib/sigdelay.h
@@ -0,0 +1,54 @@
+/* Delaying the delivery of signals to the current process.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible.  */
+
+#ifndef _SIGDELAY_H
+#define _SIGDELAY_H
+
+#include <signal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* While pthread_sigmask() can block the delivery of a signal to a particular
+   thread and thus ??? in the case of a process with a single thread ??? actually
+   delay the signal, POSIX lacks a facility for blocking or delaying a signal
+   process-wide, i.e. for all threads of the process at once.
+
+   The sigdelay() function is such a facility.
+   It assumes that the signal handler for the affected signals does not change
+   between matching sigdelay (SIG_BLOCK, ...) and sigdelay (SIG_UNBLOCK, ...)
+   invocations.  */
+
+/* With HOW = SIG_BLOCK, this function adds the signals from SET to the set
+   of delayed signals.
+   With HOW = SIG_UNBLOCK, this function removes the signals from SET from the
+   set of delayed signals.
+   This function works cumulatively, that is, when a signal was added N times,
+   it also needs to be removed N times in order to restore the initial state.
+   If OLD_SET is non-NULL, this function also stores the previous set of
+   delayed signals in *OLD_SET.
+   Returns 0 if successful, or -1 with errno set in case of failure.  */
+extern int sigdelay (int how, const sigset_t *restrict set,
+                     sigset_t *restrict old_set);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SIGDELAY_H */
diff --git a/modules/sigdelay b/modules/sigdelay
new file mode 100644
index 0000000000..a370a2d607
--- /dev/null
+++ b/modules/sigdelay
@@ -0,0 +1,31 @@
+Description:
+Delaying the delivery of signals to the current process.
+
+Files:
+lib/sigdelay.h
+lib/sigdelay.c
+
+Depends-on:
+lock
+sigaction
+thread-optim
+raise
+stdcountof-h
+
+configure.ac:
+AC_REQUIRE([AC_C_RESTRICT])
+
+Makefile.am:
+lib_SOURCES += sigdelay.c
+
+Include:
+"sigdelay.h"
+
+Link:
+$(LIBTHREAD)
+
+License:
+LGPLv2+
+
+Maintainer:
+Bruno Haible
-- 
2.52.0

From 001a31578623e5bffceb5845cddaad46a9c9505d Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Wed, 15 Apr 2026 13:45:58 +0200
Subject: [PATCH 4/4] sigdelay: Add tests.

* tests/test-sigdelay1.c: New file, based on
tests/test-pthread_sigmask1.c.
* tests/test-sigdelay2.c: New file, based on
tests/test-pthread_sigmask2.c.
* modules/sigdelay-tests: New file.
---
 ChangeLog              |   7 ++
 modules/sigdelay-tests |  20 ++++++
 tests/test-sigdelay1.c | 104 ++++++++++++++++++++++++++++++
 tests/test-sigdelay2.c | 143 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 274 insertions(+)
 create mode 100644 modules/sigdelay-tests
 create mode 100644 tests/test-sigdelay1.c
 create mode 100644 tests/test-sigdelay2.c

diff --git a/ChangeLog b/ChangeLog
index b1079e902d..761d1edc0c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2026-04-15  Bruno Haible  <[email protected]>
 
+	sigdelay: Add tests.
+	* tests/test-sigdelay1.c: New file, based on
+	tests/test-pthread_sigmask1.c.
+	* tests/test-sigdelay2.c: New file, based on
+	tests/test-pthread_sigmask2.c.
+	* modules/sigdelay-tests: New file.
+
 	sigdelay: New module.
 	* lib/sigdelay.h: New file.
 	* lib/sigdelay.c: New file.
diff --git a/modules/sigdelay-tests b/modules/sigdelay-tests
new file mode 100644
index 0000000000..2c56ff4ba5
--- /dev/null
+++ b/modules/sigdelay-tests
@@ -0,0 +1,20 @@
+Files:
+tests/test-sigdelay1.c
+tests/test-sigdelay2.c
+tests/virtualbox.h
+tests/macros.h
+
+Depends-on:
+inttypes-h
+sleep
+pthread-thread
+streq
+memeq
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-sigdelay1 test-sigdelay2
+check_PROGRAMS += test-sigdelay1 test-sigdelay2
+test_sigdelay1_LDADD = $(LDADD) @LIBTHREAD@
+test_sigdelay2_LDADD = $(LDADD) @LIBMULTITHREAD@
diff --git a/tests/test-sigdelay1.c b/tests/test-sigdelay1.c
new file mode 100644
index 0000000000..a6b3e4b54f
--- /dev/null
+++ b/tests/test-sigdelay1.c
@@ -0,0 +1,104 @@
+/* Test of sigdelay in a single-threaded program.
+   Copyright (C) 2011-2026 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <[email protected]>, 2026.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigdelay.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "virtualbox.h"
+#include "macros.h"
+
+#if !(defined _WIN32 && !defined __CYGWIN__)
+
+static volatile int sigint_occurred;
+
+static void
+sigint_handler (_GL_UNUSED int sig)
+{
+  sigint_occurred++;
+}
+
+int
+main ()
+{
+  /* This test occasionally fails on Linux (glibc or musl libc), in a
+     VirtualBox VM with paravirtualization = Default or KVM, with ??? 2 CPUs,
+     when "Nested VT-x/AMD-V" and "PAE/NX" are not both enabled.
+     Skip the test in this situation.  */
+  if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+    {
+      fputs ("Skipping test: avoiding VirtualBox bug with KVM paravirtualization\n",
+             stderr);
+      return 77;
+    }
+
+  signal (SIGINT, sigint_handler);
+
+  sigset_t set;
+  sigemptyset (&set);
+  sigaddset (&set, SIGINT);
+
+  /* Check error handling.  */
+  ASSERT (sigdelay (1729, &set, NULL) == -1);
+  ASSERT (errno == EINVAL);
+
+  /* Block SIGINT.  */
+  ASSERT (sigdelay (SIG_BLOCK, &set, NULL) == 0);
+
+  /* Request a SIGINT signal from outside.  */
+  char command[80];
+  intmax_t pid = getpid ();
+  sprintf (command, "sh -c 'sleep 1; kill -INT %"PRIdMAX"' &", pid);
+  ASSERT (system (command) == 0);
+
+  /* Wait.  */
+  sleep (2);
+
+  /* The signal should not have arrived yet, because it is blocked.  */
+  ASSERT (sigint_occurred == 0);
+
+  /* Unblock SIGINT.  */
+  ASSERT (sigdelay (SIG_UNBLOCK, &set, NULL) == 0);
+
+  /* The signal should have arrived now.  */
+  ASSERT (sigint_occurred == 1);
+
+  return test_exit_status;
+}
+
+#else
+
+/* On native Windows, getpid() values and the arguments that are passed to
+   the (Cygwin?) 'kill' program are not necessarily related.  */
+
+int
+main ()
+{
+  fputs ("Skipping test: native Windows platform\n", stderr);
+  return 77;
+}
+
+#endif
diff --git a/tests/test-sigdelay2.c b/tests/test-sigdelay2.c
new file mode 100644
index 0000000000..7d15d6de5b
--- /dev/null
+++ b/tests/test-sigdelay2.c
@@ -0,0 +1,143 @@
+/* Test of sigdelay in a multi-threaded process.
+   Copyright (C) 2011-2026 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <[email protected]>, 2026.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include "sigdelay.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "virtualbox.h"
+#include "macros.h"
+
+#if USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS
+
+static pthread_t main_thread;
+static pthread_t killer_thread1;
+static pthread_t killer_thread2;
+
+static void *
+killer_thread1_func (_GL_UNUSED void *arg)
+{
+  sleep (1);
+  pthread_kill (main_thread, SIGINT);
+  return NULL;
+}
+
+static void *
+killer_thread2_func (_GL_UNUSED void *arg)
+{
+  sleep (1);
+  pthread_kill (pthread_self (), SIGINT);
+  return NULL;
+}
+
+static volatile int sigint_occurred;
+
+static void
+sigint_handler (_GL_UNUSED int sig)
+{
+  sigint_occurred++;
+}
+
+int
+main ()
+{
+  /* This test occasionally fails on Linux (glibc or musl libc), in a
+     VirtualBox VM with paravirtualization = Default or KVM, with ??? 2 CPUs,
+     when "Nested VT-x/AMD-V" and "PAE/NX" are not both enabled.
+     Skip the test in this situation.  */
+  if (is_running_under_virtualbox_kvm () && num_cpus () > 1)
+    {
+      fputs ("Skipping test: avoiding VirtualBox bug with KVM paravirtualization\n",
+             stderr);
+      return 77;
+    }
+
+  signal (SIGINT, sigint_handler);
+
+  sigset_t set;
+  sigemptyset (&set);
+  sigaddset (&set, SIGINT);
+
+  /* Check error handling.  */
+  ASSERT (sigdelay (1729, &set, NULL) == -1);
+  ASSERT (errno == EINVAL);
+
+  /* Block SIGINT.  */
+  ASSERT (sigdelay (SIG_BLOCK, &set, NULL) == 0);
+
+  /* Request a SIGINT signal from another thread.  */
+  main_thread = pthread_self ();
+  ASSERT (pthread_create (&killer_thread1, NULL, killer_thread1_func, NULL)
+          == 0);
+
+  /* Wait.  */
+  sleep (2);
+
+  /* The signal should not have arrived yet, because it is blocked.  */
+  ASSERT (sigint_occurred == 0);
+
+  /* Unblock SIGINT.  */
+  ASSERT (sigdelay (SIG_UNBLOCK, &set, NULL) == 0);
+
+  /* The signal should have arrived now.  */
+  ASSERT (sigint_occurred == 1);
+
+  /* Request a SIGINT signal from another thread.  */
+  ASSERT (pthread_create (&killer_thread2, NULL, killer_thread2_func, NULL)
+          == 0);
+
+  /* Block SIGINT.  */
+  ASSERT (sigdelay (SIG_BLOCK, &set, NULL) == 0);
+
+  /* Wait.  */
+  sleep (2);
+
+  /* The signal should not have arrived yet, because it is blocked.  */
+  ASSERT (sigint_occurred == 1);
+
+  /* Unblock SIGINT.  */
+  ASSERT (sigdelay (SIG_UNBLOCK, &set, NULL) == 0);
+
+  /* The signal should have arrived now.  */
+  ASSERT (sigint_occurred == 2);
+
+  /* Clean up the threads.  This avoids a "ThreadSanitizer: thread leak" warning
+     from "gcc -fsanitize=thread".  */
+  ASSERT (pthread_join (killer_thread1, NULL) == 0);
+  ASSERT (pthread_join (killer_thread2, NULL) == 0);
+
+  return test_exit_status;
+}
+
+#else
+
+int
+main ()
+{
+  fputs ("Skipping test: POSIX threads not enabled\n", stderr);
+  return 77;
+}
+
+#endif
-- 
2.52.0

Reply via email to