summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-06-02 10:34:55 +0200
committerFlorian Weimer <fweimer@redhat.com>2020-06-02 11:59:18 +0200
commitec41af45a6d25f70f9c7ea15cb831a2b2fea3855 (patch)
tree485ea3129bf12117c40b6784807869564fc8af1e /nptl
parent7538d461134bf306e31b40e4032f0c225bb40d51 (diff)
downloadglibc-ec41af45a6d25f70f9c7ea15cb831a2b2fea3855.tar.gz
glibc-ec41af45a6d25f70f9c7ea15cb831a2b2fea3855.tar.xz
glibc-ec41af45a6d25f70f9c7ea15cb831a2b2fea3855.zip
nptl: Add pthread_attr_setsigmask_np, pthread_attr_getsigmask_np
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'nptl')
-rw-r--r--nptl/Makefile4
-rw-r--r--nptl/Versions3
-rw-r--r--nptl/pthreadP.h10
-rw-r--r--nptl/pthread_attr_copy.c5
-rw-r--r--nptl/pthread_attr_getsigmask.c38
-rw-r--r--nptl/pthread_attr_setsigmask.c34
-rw-r--r--nptl/pthread_attr_setsigmask_internal.c45
-rw-r--r--nptl/pthread_create.c25
-rw-r--r--nptl/tst-pthread-attr-sigmask.c204
9 files changed, 360 insertions, 8 deletions
diff --git a/nptl/Makefile b/nptl/Makefile
index 7f8a80a4d1..b017cb855b 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -47,6 +47,7 @@ routines = \
   pthread_attr_getschedparam \
   pthread_attr_getschedpolicy \
   pthread_attr_getscope \
+  pthread_attr_getsigmask \
   pthread_attr_init \
   pthread_attr_setaffinity \
   pthread_attr_setdetachstate \
@@ -54,6 +55,8 @@ routines = \
   pthread_attr_setschedparam \
   pthread_attr_setschedpolicy \
   pthread_attr_setscope \
+  pthread_attr_setsigmask \
+  pthread_attr_setsigmask_internal \
   pthread_cond_destroy \
   pthread_cond_init \
   pthread_condattr_destroy \
@@ -326,6 +329,7 @@ tests = tst-attr2 tst-attr3 tst-default-attr \
 	tst-thread-affinity-pthread2 \
 	tst-thread-affinity-sched \
 	tst-pthread-defaultattr-free \
+	tst-pthread-attr-sigmask \
 
 
 tests-container =  tst-pthread-getattr
diff --git a/nptl/Versions b/nptl/Versions
index e4696c128f..aed118e717 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -44,7 +44,9 @@ libc {
     thrd_current; thrd_equal; thrd_sleep; thrd_yield;
   }
   GLIBC_2.32 {
+    pthread_attr_getsigmask_np;
     pthread_attr_setaffinity_np;
+    pthread_attr_setsigmask_np;
     pthread_getaffinity_np;
     pthread_getattr_np;
     pthread_sigmask;
@@ -62,6 +64,7 @@ libc {
     __pthread_attr_init; __pthread_attr_destroy;
     __pthread_attr_copy;
     __pthread_getattr_default_np;
+    __pthread_attr_setsigmask_internal;
   }
 }
 
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index 7b3153594e..6f94d6be31 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -528,6 +528,16 @@ extern int __pthread_getaffinity_np (pthread_t th, size_t cpusetsize,
 				     cpu_set_t *cpuset);
 libc_hidden_proto (__pthread_getaffinity_np)
 
+/* Special internal version of pthread_attr_setsigmask_np which does
+   not filter out internal signals from *SIGMASK.  This can be used to
+   launch threads with internal signals blocked.  */
+  extern int __pthread_attr_setsigmask_internal (pthread_attr_t *attr,
+						 const sigset_t *sigmask);
+libc_hidden_proto (__pthread_attr_setsigmask_internal)
+
+extern __typeof (pthread_attr_getsigmask_np) __pthread_attr_getsigmask_np;
+libc_hidden_proto (__pthread_attr_getsigmask_np)
+
 #if IS_IN (libpthread)
 /* Special versions which use non-exported functions.  */
 extern void __pthread_cleanup_push (struct _pthread_cleanup_buffer *buffer,
diff --git a/nptl/pthread_attr_copy.c b/nptl/pthread_attr_copy.c
index eb29557505..5d0c62f65c 100644
--- a/nptl/pthread_attr_copy.c
+++ b/nptl/pthread_attr_copy.c
@@ -42,6 +42,11 @@ __pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source)
         ret = __pthread_attr_setaffinity_np (&temp.external,
                                              isource->extension->cpusetsize,
                                              isource->extension->cpuset);
+
+      /* Propagate the signal mask information.  */
+      if (ret == 0 && isource->extension->sigmask_set)
+        ret = __pthread_attr_setsigmask_internal ((pthread_attr_t *) &temp,
+                                                  &isource->extension->sigmask);
     }
 
   if (ret != 0)
diff --git a/nptl/pthread_attr_getsigmask.c b/nptl/pthread_attr_getsigmask.c
new file mode 100644
index 0000000000..99b9812ef8
--- /dev/null
+++ b/nptl/pthread_attr_getsigmask.c
@@ -0,0 +1,38 @@
+/* Obtain the configured signal mask from a POSIX thread attribute.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <pthreadP.h>
+
+int
+__pthread_attr_getsigmask_np (const pthread_attr_t *attr, sigset_t *sigmask)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+
+  if (iattr->extension == NULL || !iattr->extension->sigmask_set)
+    {
+      __sigemptyset (sigmask);
+      return PTHREAD_ATTR_NO_SIGMASK_NP;
+    }
+  else
+    {
+      *sigmask = iattr->extension->sigmask;
+      return 0;
+    }
+}
+libc_hidden_def (__pthread_attr_getsigmask_np)
+weak_alias (__pthread_attr_getsigmask_np, pthread_attr_getsigmask_np)
diff --git a/nptl/pthread_attr_setsigmask.c b/nptl/pthread_attr_setsigmask.c
new file mode 100644
index 0000000000..4574f51056
--- /dev/null
+++ b/nptl/pthread_attr_setsigmask.c
@@ -0,0 +1,34 @@
+/* Set the signal mask in a POSIX thread attribute.  Public variant.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <pthreadP.h>
+#include <internal-signals.h>
+
+int
+pthread_attr_setsigmask_np (pthread_attr_t *attr, const sigset_t *sigmask)
+{
+  int ret = __pthread_attr_setsigmask_internal (attr, sigmask);
+  if (ret != 0)
+    return ret;
+
+  /* Filter out internal signals.  */
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+  __clear_internal_signals (&iattr->extension->sigmask);
+
+  return 0;
+}
diff --git a/nptl/pthread_attr_setsigmask_internal.c b/nptl/pthread_attr_setsigmask_internal.c
new file mode 100644
index 0000000000..a2941b4ee9
--- /dev/null
+++ b/nptl/pthread_attr_setsigmask_internal.c
@@ -0,0 +1,45 @@
+/* Set the signal mask in a POSIX thread attribute.  Internal variant.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <pthreadP.h>
+#include <internal-signals.h>
+
+int
+__pthread_attr_setsigmask_internal (pthread_attr_t *attr,
+                                    const sigset_t *sigmask)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+
+  if (sigmask == NULL)
+    {
+      /* Mark the signal mask as unset if it is present.  */
+      if (iattr->extension != NULL)
+        iattr->extension->sigmask_set = false;
+      return 0;
+    }
+
+  int ret = __pthread_attr_extension (iattr);
+  if (ret != 0)
+    return ret;
+
+  iattr->extension->sigmask = *sigmask;
+  iattr->extension->sigmask_set = true;
+
+  return 0;
+}
+libc_hidden_def (__pthread_attr_setsigmask_internal)
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index f6418eb5ed..35a9927cf2 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -745,14 +745,23 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
   sigset_t original_sigmask;
   __libc_signal_block_all (&original_sigmask);
 
-  /* Conceptually, the new thread needs to inherit the signal mask of
-     this thread.  Therefore, it needs to restore the saved signal
-     mask of this thread, so save it in the startup information.  */
-  pd->sigmask = original_sigmask;
-
-  /* Reset the cancellation signal mask in case this thread is running
-     cancellation.  */
-  __sigdelset (&pd->sigmask, SIGCANCEL);
+  if (iattr->extension != NULL && iattr->extension->sigmask_set)
+    /* Use the signal mask in the attribute.  The internal signals
+       have already been filtered by the public
+       pthread_attr_setsigmask_np interface.  */
+    pd->sigmask = iattr->extension->sigmask;
+  else
+    {
+      /* Conceptually, the new thread needs to inherit the signal mask
+	 of this thread.  Therefore, it needs to restore the saved
+	 signal mask of this thread, so save it in the startup
+	 information.  */
+      pd->sigmask = original_sigmask;
+
+      /* Reset the cancellation signal mask in case this thread is
+	 running cancellation.  */
+      __sigdelset (&pd->sigmask, SIGCANCEL);
+    }
 
   /* Start the thread.  */
   if (__glibc_unlikely (report_thread_creation (pd)))
diff --git a/nptl/tst-pthread-attr-sigmask.c b/nptl/tst-pthread-attr-sigmask.c
new file mode 100644
index 0000000000..8f854d8d11
--- /dev/null
+++ b/nptl/tst-pthread-attr-sigmask.c
@@ -0,0 +1,204 @@
+/* Tests for pthread_attr_setsigmask_np, pthread_attr_getsigmask_np.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* This thread uses different masked status for SIGUSR1, SIGUSR2,
+   SIGHUP to determine if signal masks are applied to new threads as
+   expected.  */
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xsignal.h>
+#include <support/xthread.h>
+#include <threads.h>
+
+typedef bool signals[_NSIG];
+
+static const char *
+masked_or_unmasked (bool masked)
+{
+  if (masked)
+    return "masked";
+  else
+    return "unmasked";
+}
+
+/* Report an error if ACTUAL_MASK does not match EXPECTED_MASK.
+   CONTEXT is used in error messages.  */
+static void
+check_sigmask (const char *context, signals expected_mask,
+               const sigset_t *actual_mask)
+{
+  for (int sig = 1; sig < _NSIG; ++sig)
+    if (sigismember (actual_mask, sig) != expected_mask[sig])
+      {
+        support_record_failure ();
+        printf ("error: %s: signal %d should be %s, but is %s\n",
+                context, sig,
+                masked_or_unmasked (sigismember (actual_mask, sig)),
+                masked_or_unmasked (expected_mask[sig]));
+      }
+}
+
+/* Report an error if the current thread signal mask does not match
+   EXPECTED_MASK.  CONTEXT is used in error messages.  */
+static void
+check_current_sigmask (const char *context, signals expected_mask)
+{
+  sigset_t actual_mask;
+  xpthread_sigmask (SIG_SETMASK, NULL, &actual_mask);
+  check_sigmask (context, expected_mask, &actual_mask);
+}
+
+/* Thread start routine which checks the current thread signal mask
+   against CLOSURE.  */
+static void *
+check_sigmask_thread_function (void *closure)
+{
+  check_current_sigmask ("on thread", closure);
+  return NULL;
+}
+
+/* Same for C11 threads.  */
+static int
+check_sigmask_thread_function_c11 (void *closure)
+{
+  check_current_sigmask ("on C11 thread", closure);
+  return 0;
+}
+
+/* Launch a POSIX thread with ATTR (which can be NULL) and check that
+   it has the expected signal mask.  */
+static void
+check_posix_thread (pthread_attr_t *attr, signals expected_mask)
+{
+  xpthread_join (xpthread_create (attr, check_sigmask_thread_function,
+                                  expected_mask));
+}
+
+/* Launch a C11 thread and check that it has the expected signal
+   mask.  */
+static void
+check_c11_thread (signals expected_mask)
+{
+  thrd_t thr;
+  TEST_VERIFY_EXIT (thrd_create (&thr, check_sigmask_thread_function_c11,
+                                 expected_mask) == thrd_success);
+  TEST_VERIFY_EXIT (thrd_join (thr, NULL) == thrd_success);
+}
+
+static int
+do_test (void)
+{
+  check_current_sigmask ("initial mask", (signals) { false, });
+  check_posix_thread (NULL, (signals) { false, });
+  check_c11_thread ((signals) { false, });
+
+  sigset_t set;
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR1);
+  xpthread_sigmask (SIG_SETMASK, &set, NULL);
+  check_current_sigmask ("SIGUSR1 masked", (signals) { [SIGUSR1] = true, });
+  /* The signal mask is inherited by the new thread.  */
+  check_posix_thread (NULL, (signals) { [SIGUSR1] = true, });
+  check_c11_thread ((signals) { [SIGUSR1] = true, });
+
+  pthread_attr_t attr;
+  xpthread_attr_init (&attr);
+  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set),
+                PTHREAD_ATTR_NO_SIGMASK_NP);
+  /* By default, the signal mask is inherited (even with an explicit
+     thread attribute).  */
+  check_posix_thread (&attr, (signals) { [SIGUSR1] = true, });
+
+  /* Check that pthread_attr_getsigmask_np can obtain the signal
+     mask.  */
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR2);
+  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, &set), 0);
+  sigemptyset (&set);
+  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set), 0);
+  check_sigmask ("pthread_attr_getsigmask_np", (signals) { [SIGUSR2] = true, },
+                 &set);
+
+  /* Check that a thread is launched with the configured signal
+     mask.  */
+  check_current_sigmask ("SIGUSR1 masked", (signals) { [SIGUSR1] = true, });
+  check_posix_thread (&attr, (signals) { [SIGUSR2] = true, });
+  check_current_sigmask ("SIGUSR1 masked", (signals) { [SIGUSR1] = true, });
+
+  /* But C11 threads remain at inheritance.  */
+  check_c11_thread ((signals) { [SIGUSR1] = true, });
+
+  /* Check that filling the original signal set does not affect thread
+     creation.  */
+  sigfillset (&set);
+  check_posix_thread (&attr, (signals) { [SIGUSR2] = true, });
+
+  /* Check that clearing the signal in the attribute restores
+     inheritance.  */
+  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, NULL), 0);
+  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set),
+                PTHREAD_ATTR_NO_SIGMASK_NP);
+  check_posix_thread (&attr, (signals) { [SIGUSR1] = true, });
+
+  /* Mask SIGHUP via the default thread attribute.  */
+  sigemptyset (&set);
+  sigaddset (&set, SIGHUP);
+  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, &set), 0);
+  TEST_COMPARE (pthread_setattr_default_np (&attr), 0);
+
+  /* Check that the mask was applied to the default attribute.  */
+  xpthread_attr_destroy (&attr);
+  TEST_COMPARE (pthread_getattr_default_np (&attr), 0);
+  sigaddset (&set, SIGHUP);
+  TEST_COMPARE (pthread_attr_getsigmask_np (&attr, &set), 0);
+  check_sigmask ("default attribute", (signals) { [SIGHUP] = true, }, &set);
+  xpthread_attr_destroy (&attr);
+
+  /* Check that the default attribute is applied.  */
+  check_posix_thread (NULL, (signals) { [SIGHUP] = true, });
+  check_c11_thread ((signals) { [SIGHUP] = true, });
+
+  /* An explicit attribute with no signal mask triggers inheritance
+     even if the default has been changed.  */
+  xpthread_attr_init (&attr);
+  check_posix_thread (&attr, (signals) { [SIGUSR1] = true, });
+
+  /* Explicitly setting the signal mask affects the new thread even
+     with a default attribute.  */
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR2);
+  TEST_COMPARE (pthread_attr_setsigmask_np (&attr, &set), 0);
+  check_posix_thread (&attr, (signals) { [SIGUSR2] = true, });
+
+  /* Resetting the default attribute brings back the old inheritance
+     behavior.  */
+  xpthread_attr_destroy (&attr);
+  xpthread_attr_init (&attr);
+  TEST_COMPARE (pthread_setattr_default_np (&attr), 0);
+  xpthread_attr_destroy (&attr);
+  check_posix_thread (NULL, (signals) { [SIGUSR1] = true, });
+  check_c11_thread ((signals) { [SIGUSR1] = true, });
+
+  return 0;
+}
+
+#include <support/test-driver.c>