diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/nptl/dl-tls_init_tp.c | 3 | ||||
-rw-r--r-- | sysdeps/nptl/pthreadP.h | 2 | ||||
-rw-r--r-- | sysdeps/pthread/Makefile | 1 | ||||
-rw-r--r-- | sysdeps/pthread/tst-cancel29.c | 207 |
4 files changed, 209 insertions, 4 deletions
diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c index ca494dd3a5..c2054f8496 100644 --- a/sysdeps/nptl/dl-tls_init_tp.c +++ b/sysdeps/nptl/dl-tls_init_tp.c @@ -94,7 +94,4 @@ __tls_init_tp (void) It will be bigger than it actually is, but for unwind.c/pt-longjmp.c purposes this is good enough. */ THREAD_SETMEM (pd, stackblock_size, (size_t) __libc_stack_end); - - THREAD_SETMEM (pd, cancelstate, PTHREAD_CANCEL_ENABLE); - THREAD_SETMEM (pd, canceltype, PTHREAD_CANCEL_DEFERRED); } diff --git a/sysdeps/nptl/pthreadP.h b/sysdeps/nptl/pthreadP.h index 374657a2fd..b968afc4c6 100644 --- a/sysdeps/nptl/pthreadP.h +++ b/sysdeps/nptl/pthreadP.h @@ -276,7 +276,7 @@ __do_cancel (void) struct pthread *self = THREAD_SELF; /* Make sure we get no more cancellations. */ - THREAD_ATOMIC_BIT_SET (self, cancelhandling, EXITING_BIT); + atomic_bit_set (&self->cancelhandling, EXITING_BIT); __pthread_unwind ((__pthread_unwind_buf_t *) THREAD_GETMEM (self, cleanup_jmp_buf)); diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index c657101696..00419c4d19 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -69,6 +69,7 @@ tests += tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 tst-cancel16 \ tst-cancel18 tst-cancel19 tst-cancel20 tst-cancel21 \ tst-cancel22 tst-cancel23 tst-cancel26 tst-cancel27 tst-cancel28 \ + tst-cancel29 \ tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \ tst-clock1 \ tst-cond-except \ diff --git a/sysdeps/pthread/tst-cancel29.c b/sysdeps/pthread/tst-cancel29.c new file mode 100644 index 0000000000..4f0d99e002 --- /dev/null +++ b/sysdeps/pthread/tst-cancel29.c @@ -0,0 +1,207 @@ +/* Check if a thread that disables cancellation and which call functions + that might be interrupted by a signal do not see the internal SIGCANCEL. + + Copyright (C) 2022 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 <array_length.h> +#include <errno.h> +#include <inttypes.h> +#include <poll.h> +#include <support/check.h> +#include <support/support.h> +#include <support/temp_file.h> +#include <support/xthread.h> +#include <sys/socket.h> +#include <signal.h> +#include <stdio.h> +#include <unistd.h> + +/* On Linux some interfaces are never restarted after being interrupted by + a signal handler, regardless of the use of SA_RESTART. It means that + if asynchronous cancellation is not enabled, the pthread_cancel can not + set the internal SIGCANCEL otherwise the interface might see a spurious + EINTR failure. */ + +static pthread_barrier_t b; + +/* Cleanup handling test. */ +static int cl_called; +static void +cl (void *arg) +{ + ++cl_called; +} + +static void * +tf_sigtimedwait (void *arg) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + xpthread_barrier_wait (&b); + + int r; + pthread_cleanup_push (cl, NULL); + + sigset_t mask; + sigemptyset (&mask); + r = sigtimedwait (&mask, NULL, &(struct timespec) { 0, 250000000 }); + if (r != -1) + return (void*) -1; + if (errno != EAGAIN) + return (void*) -2; + + pthread_cleanup_pop (0); + return NULL; +} + +static void * +tf_poll (void *arg) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + xpthread_barrier_wait (&b); + + int r; + pthread_cleanup_push (cl, NULL); + + r = poll (NULL, 0, 250); + if (r != 0) + return (void*) -1; + + pthread_cleanup_pop (0); + return NULL; +} + +static void * +tf_ppoll (void *arg) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + + xpthread_barrier_wait (&b); + + int r; + pthread_cleanup_push (cl, NULL); + + r = ppoll (NULL, 0, &(struct timespec) { 0, 250000000 }, NULL); + if (r != 0) + return (void*) -1; + + pthread_cleanup_pop (0); + return NULL; +} + +static void * +tf_select (void *arg) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + xpthread_barrier_wait (&b); + + int r; + pthread_cleanup_push (cl, NULL); + + r = select (0, NULL, NULL, NULL, &(struct timeval) { 0, 250000 }); + if (r != 0) + return (void*) -1; + + pthread_cleanup_pop (0); + return NULL; +} + +static void * +tf_pselect (void *arg) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + xpthread_barrier_wait (&b); + + int r; + pthread_cleanup_push (cl, NULL); + + r = pselect (0, NULL, NULL, NULL, &(struct timespec) { 0, 250000000 }, NULL); + if (r != 0) + return (void*) -1; + + pthread_cleanup_pop (0); + return NULL; +} + +static void * +tf_clock_nanosleep (void *arg) +{ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); + xpthread_barrier_wait (&b); + + int r; + pthread_cleanup_push (cl, NULL); + + r = clock_nanosleep (CLOCK_REALTIME, 0, &(struct timespec) { 0, 250000000 }, + NULL); + if (r != 0) + return (void*) -1; + + pthread_cleanup_pop (0); + return NULL; +} + +struct cancel_test_t +{ + const char *name; + void * (*cf) (void *); +} tests[] = +{ + { "sigtimedwait", tf_sigtimedwait, }, + { "poll", tf_poll, }, + { "ppoll", tf_ppoll, }, + { "select", tf_select, }, + { "pselect", tf_pselect , }, + { "clock_nanosleep", tf_clock_nanosleep, }, +}; + +static int +do_test (void) +{ + for (int i = 0; i < array_length (tests); i++) + { + xpthread_barrier_init (&b, NULL, 2); + + cl_called = 0; + + pthread_t th = xpthread_create (NULL, tests[i].cf, NULL); + + xpthread_barrier_wait (&b); + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; + while (nanosleep (&ts, &ts) != 0) + continue; + + xpthread_cancel (th); + + void *status = xpthread_join (th); + if (status != NULL) + printf ("test '%s' failed: %" PRIdPTR "\n", tests[i].name, + (intptr_t) status); + TEST_VERIFY (status == NULL); + + xpthread_barrier_destroy (&b); + + TEST_COMPARE (cl_called, 0); + + printf ("in-time cancel test of '%s' successful\n", tests[i].name); + } + + return 0; +} + +#include <support/test-driver.c> |