From ec41af45a6d25f70f9c7ea15cb831a2b2fea3855 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 2 Jun 2020 10:34:55 +0200 Subject: nptl: Add pthread_attr_setsigmask_np, pthread_attr_getsigmask_np Reviewed-by: Carlos O'Donell Tested-by: Carlos O'Donell --- nptl/Makefile | 4 + nptl/Versions | 3 + nptl/pthreadP.h | 10 ++ nptl/pthread_attr_copy.c | 5 + nptl/pthread_attr_getsigmask.c | 38 ++++++ nptl/pthread_attr_setsigmask.c | 34 ++++++ nptl/pthread_attr_setsigmask_internal.c | 45 +++++++ nptl/pthread_create.c | 25 ++-- nptl/tst-pthread-attr-sigmask.c | 204 ++++++++++++++++++++++++++++++++ 9 files changed, 360 insertions(+), 8 deletions(-) create mode 100644 nptl/pthread_attr_getsigmask.c create mode 100644 nptl/pthread_attr_setsigmask.c create mode 100644 nptl/pthread_attr_setsigmask_internal.c create mode 100644 nptl/tst-pthread-attr-sigmask.c (limited to 'nptl') diff --git a/nptl/Makefile b/nptl/Makefile index 7f8a80a..b017cb8 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 e4696c1..aed118e 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 7b31535..6f94d6b 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 eb29557..5d0c62f 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 0000000..99b9812 --- /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 + . */ + +#include + +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 0000000..4574f51 --- /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 + . */ + +#include +#include + +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 0000000..a2941b4 --- /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 + . */ + +#include +#include + +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 f6418eb..35a9927 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 0000000..8f854d8 --- /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 + . */ + +/* This thread uses different masked status for SIGUSR1, SIGUSR2, + SIGHUP to determine if signal masks are applied to new threads as + expected. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 -- cgit v1.1