]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
sigprocmask: On mingw, avoid dependency on libwinpthread. master
authorBruno Haible <bruno@clisp.org>
Tue, 14 Apr 2026 19:56:00 +0000 (21:56 +0200)
committerBruno Haible <bruno@clisp.org>
Tue, 14 Apr 2026 19:56:00 +0000 (21:56 +0200)
* lib/sigprocmask.c: Include windows-tls.h, windows-once.h.
(thread_local): Remove macro.
(struct per_thread): New type.
(tls_key): New variable.
(keys_init): New function.
(keys_init_once): New variable.
(get_per_thread): New function.
(per_thread_singleton): New variable.
(blocked_set, pending_array): Remove variables.
(sigpending, override_handler, pthread_sigmask, _gl_raise_SIGPIPE):
Invoke get_per_thread, to access blocked_set or pending_array.
* modules/sigprocmask (Depends-on): Add windows-once, windows-tls.

ChangeLog
lib/sigprocmask.c
modules/sigprocmask

index 9015a55ae8ab8f560fac47e698b07144b600d581..fb6babd1ff97628d10a957d645681321a27c72fb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2026-04-14  Bruno Haible  <bruno@clisp.org>
+
+       sigprocmask: On mingw, avoid dependency on libwinpthread.
+       * lib/sigprocmask.c: Include windows-tls.h, windows-once.h.
+       (thread_local): Remove macro.
+       (struct per_thread): New type.
+       (tls_key): New variable.
+       (keys_init): New function.
+       (keys_init_once): New variable.
+       (get_per_thread): New function.
+       (per_thread_singleton): New variable.
+       (blocked_set, pending_array): Remove variables.
+       (sigpending, override_handler, pthread_sigmask, _gl_raise_SIGPIPE):
+       Invoke get_per_thread, to access blocked_set or pending_array.
+       * modules/sigprocmask (Depends-on): Add windows-once, windows-tls.
+
 2026-04-14  Bruno Haible  <bruno@clisp.org>
 
        sigaction: Fix test failure on native Windows.
index 2c5ad72ab211ab086a3972ff20d7f31f4e64bf57..ecba32c35a98c20794837e92f81b0b7ab4b00f6d 100644 (file)
 # include "msvc-inval.h"
 #endif
 
-#if GNULIB_SIGPROCMASK_SINGLE_THREAD
+#if !GNULIB_SIGPROCMASK_SINGLE_THREAD
+# include "windows-spin.h"
+#else
 # define glwthread_spin_lock(lock)
 # define glwthread_spin_unlock(lock)
-#else
-# include "windows-spin.h"
+#endif
+
+#if !GNULIB_SIGPROCMASK_SINGLE_THREAD
+# include "windows-tls.h"
+# include "windows-once.h"
 #endif
 
 /* We assume that a platform without POSIX signal blocking functions
@@ -181,7 +186,6 @@ sigdelset (sigset_t *set, int sig)
     }
 }
 
-
 int
 sigfillset (sigset_t *set)
 {
@@ -189,28 +193,67 @@ sigfillset (sigset_t *set)
   return 0;
 }
 
-/* Storage class specifier for thread-local storage.  */
-#undef thread_local
-#if defined _MSC_VER
-/* <https://learn.microsoft.com/en-us/cpp/parallel/thread-local-storage-tls> */
-# define thread_local __declspec(thread)
+/* Thread-local storage.
+   We don't use 'thread_local' here, since on mingw it (or '__thread') pulls in
+   libwinpthread, which we want to avoid.  See gl_AVOID_WINPTHREAD.  */
+
+struct per_thread
+{
+  /* Set of currently blocked signals.  */
+  sigset_t blocked_set /* = 0 */;
+  /* Set of currently blocked and pending signals.  */
+  volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
+};
+
+#if !GNULIB_SIGPROCMASK_SINGLE_THREAD
+
+static glwthread_tls_key_t tls_key; /* TLS key for a 'struct per_thread *' */
+
+static void
+keys_init (void)
+{
+  if (glwthread_tls_key_create (&tls_key, free) != 0)
+    /* Should not happen.  */
+    abort ();
+  /* The per-thread initial value is NULL.  */
+}
+
+/* Ensure that keys_init is called once only.  */
+static glwthread_once_t keys_init_once = GLWTHREAD_ONCE_INIT;
+
+/* Returns the per-thread data belonging to the current thread.  */
+static struct per_thread *
+get_per_thread (void)
+{
+  glwthread_once (&keys_init_once, keys_init);
+  struct per_thread *data = glwthread_tls_get (tls_key);
+  if (data == NULL)
+    {
+      data = (struct per_thread *) calloc (1, sizeof (struct per_thread));
+      if (data == NULL)
+        /* Should not happen.  */
+        abort ();
+      glwthread_tls_set (tls_key, data);
+    }
+  return data;
+}
+
 #else
-/* <https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html> */
-# define thread_local __thread
-#endif
 
-/* Set of currently blocked signals.  */
-static thread_local sigset_t blocked_set /* = 0 */;
+/* Assume a single thread.  */
+static struct per_thread per_thread_singleton;
 
-/* Set of currently blocked and pending signals.  */
-static thread_local volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
+# define get_per_thread() (&per_thread_singleton)
+
+#endif
 
 int
 sigpending (sigset_t *set)
 {
+  const struct per_thread *pt = get_per_thread ();
   sigset_t pending = 0;
   for (int sig = 0; sig < NSIG; sig++)
-    if (pending_array[sig])
+    if (pt->pending_array[sig])
       pending |= 1U << sig;
 
   *set = pending;
@@ -246,10 +289,11 @@ override_handler (int sig)
      when we reinstall it will trigger the default handler; oh well.  */
   signal (sig, override_handler);
 
-  if ((blocked_set >> sig) & 1)
+  struct per_thread *pt = get_per_thread ();
+  if ((pt->blocked_set >> sig) & 1)
     {
       /* The signal is blocked in the current thread.  */
-      pending_array[sig] = 1;
+      pt->pending_array[sig] = 1;
     }
   else
     {
@@ -278,8 +322,10 @@ override_handler (int sig)
 int
 pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
 {
+  struct per_thread *pt = get_per_thread ();
+
   if (old_set != NULL)
-    *old_set = blocked_set;
+    *old_set = pt->blocked_set;
 
   if (set != NULL)
     {
@@ -287,27 +333,27 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
       switch (operation)
         {
         case SIG_BLOCK:
-          new_blocked_set = blocked_set | *set;
+          new_blocked_set = pt->blocked_set | *set;
           break;
         case SIG_SETMASK:
           new_blocked_set = *set;
           break;
         case SIG_UNBLOCK:
-          new_blocked_set = blocked_set & ~*set;
+          new_blocked_set = pt->blocked_set & ~*set;
           break;
         default:
           return EINVAL;
         }
 
-      sigset_t to_unblock = blocked_set & ~new_blocked_set;
-      sigset_t to_block = new_blocked_set & ~blocked_set;
+      sigset_t to_unblock = pt->blocked_set & ~ new_blocked_set;
+      sigset_t to_block = new_blocked_set & ~ pt->blocked_set;
 
       if (to_block != 0)
         for (int sig = 0; sig < NSIG; sig++)
           if ((to_block >> sig) & 1)
             {
-              /* All pending_array[sig] remain zero.  */
-              blocked_set |= 1U << sig;
+              /* All pt->pending_array[sig] remain zero.  */
+              pt->blocked_set |= 1U << sig;
               if (!overrides[sig].overridden)
                 {
                   glwthread_spin_lock (&overrides_mt_lock);
@@ -316,8 +362,8 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
                     {
                       /* Now it's OK to install the override_handler:
                          - If it gets invoked in this thread, there is no race
-                           condition since blocked_set already has the sig bit
-                           set.
+                           condition since pt->blocked_set already has the sig
+                           bit set.
                          - If it gets invoked in another thread, the
                            overrides_handler_lock protects it from proceeding
                            until we have stored the old_handler.  For this case,
@@ -348,9 +394,9 @@ pthread_sigmask (int operation, const sigset_t *set, sigset_t *old_set)
           for (int sig = 0; sig < NSIG; sig++)
             if ((to_unblock >> sig) & 1)
               {
-                received[sig] = pending_array[sig];
-                blocked_set &= ~(1U << sig);
-                pending_array[sig] = 0;
+                received[sig] = pt->pending_array[sig];
+                pt->blocked_set &= ~(1U << sig);
+                pt->pending_array[sig] = 0;
               }
             else
               received[sig] = 0;
@@ -437,9 +483,10 @@ _gl_raise_SIGPIPE (void)
      any thread of the current process.  In the SIGPIPE emulation here, we do
      it slightly differently: we deliver it to the current thread always,
      like  raise (SIGPIPE)  would do.  */
-  if (blocked_set & (1U << SIGPIPE))
+  struct per_thread *pt = get_per_thread ();
+  if (pt->blocked_set & (1U << SIGPIPE))
     /* The signal is blocked in the current thread.  */
-    pending_array[SIGPIPE] = 1;
+    pt->pending_array[SIGPIPE] = 1;
   else
     {
       handler_t handler = SIGPIPE_handler;
index f229eeedd83103d6d32d475a1b60925437d7cab8..657ef18f1f12d4c686947fa74936631bb7ad1842 100644 (file)
@@ -10,7 +10,9 @@ signal-h
 stdint-h        [test $HAVE_POSIX_SIGNALBLOCKING = 0]
 raise           [test $HAVE_POSIX_SIGNALBLOCKING = 0]
 msvc-inval      [test $HAVE_POSIX_SIGNALBLOCKING = 0]
+windows-once    [test $HAVE_POSIX_SIGNALBLOCKING = 0]
 windows-spin    [test $HAVE_POSIX_SIGNALBLOCKING = 0]
+windows-tls     [test $HAVE_POSIX_SIGNALBLOCKING = 0]
 
 configure.ac:
 gl_SIGNALBLOCKING