@@ -266,7 +266,7 @@ tests = tst-attr2 tst-attr3 tst-default-attr \
tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \
tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 tst-cancel20 \
tst-cancel21 tst-cancel22 tst-cancel23 tst-cancel24 \
- tst-cancel26 tst-cancel27 \
+ tst-cancel26 tst-cancel27 tst-cancel28 \
tst-cancel-self tst-cancel-self-cancelstate \
tst-cancel-self-canceltype tst-cancel-self-testcancel \
tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 tst-cleanup4 \
@@ -574,6 +574,9 @@ $(objpfx)tst-tls6.out: tst-tls6.sh $(objpfx)tst-tls5 \
$(BASH) $< $(common-objpfx) '$(test-via-rtld-prefix)' \
'$(test-wrapper-env)' '$(run-program-env)' > $@; \
$(evaluate-test)
+$(objpfx)tst-cancel28: $(common-objpfx)rt/librt.so
+else
+$(objpfx)tst-cancel28: $(common-objpfx)rt/librt.a
endif
$(objpfx)tst-dlsym1: $(libdl) $(shared-thread-library)
new file mode 100644
@@ -0,0 +1,80 @@
+/* Check if the thread created by POSIX timer using SIGEV_THREAD is
+ cancellable.
+ 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 <stdio.h>
+#include <time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/xthread.h>
+
+static pthread_barrier_t barrier;
+static pthread_t timer_thread;
+
+static void
+cl (void *arg)
+{
+ xpthread_barrier_wait (&barrier);
+}
+
+static void
+thread_handler (union sigval sv)
+{
+ timer_thread = pthread_self ();
+
+ xpthread_barrier_wait (&barrier);
+
+ pthread_cleanup_push (cl, NULL);
+ while (1)
+ clock_nanosleep (CLOCK_REALTIME, 0, &(struct timespec) { 1, 0 }, NULL);
+ pthread_cleanup_pop (0);
+}
+
+static int
+do_test (void)
+{
+ struct sigevent sev = { 0 };
+ sev.sigev_notify = SIGEV_THREAD;
+ sev.sigev_notify_function = &thread_handler;
+
+ timer_t timerid;
+ TEST_COMPARE (timer_create (CLOCK_REALTIME, &sev, &timerid), 0);
+
+ xpthread_barrier_init (&barrier, NULL, 2);
+
+ struct itimerspec trigger = { 0 };
+ trigger.it_value.tv_nsec = 1000000;
+ TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0);
+
+ xpthread_barrier_wait (&barrier);
+
+ xpthread_cancel (timer_thread);
+
+ xpthread_barrier_init (&barrier, NULL, 2);
+ xpthread_barrier_wait (&barrier);
+
+ return 0;
+}
+
+/* A stall in cancellation is a regression. */
+#define TIMEOUT 5
+#include <support/test-driver.c>
@@ -47,6 +47,7 @@ tests := tst-shm tst-timer tst-timer2 \
tst-timer3 tst-timer4 tst-timer5 \
tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \
tst-shm-cancel
+tests-internal := tst-timer-sigmask
extra-libs := librt
extra-libs-others := $(extra-libs)
@@ -63,9 +64,11 @@ LDFLAGS-rt.so = -Wl,--enable-new-dtags,-z,nodelete
$(objpfx)librt.so: $(shared-thread-library)
ifeq (yes,$(build-shared))
-$(addprefix $(objpfx),$(tests)): $(objpfx)librt.so $(shared-thread-library)
+$(addprefix $(objpfx),$(tests) $(tests-internal)): \
+ $(objpfx)librt.so $(shared-thread-library)
else
-$(addprefix $(objpfx),$(tests)): $(objpfx)librt.a $(static-thread-library)
+$(addprefix $(objpfx),$(tests)) $(tests-internal): \
+ $(objpfx)librt.a $(static-thread-library)
endif
tst-mqueue7-ARGS = -- $(host-test-program-cmd)
new file mode 100644
@@ -0,0 +1,85 @@
+/* Check resulting signal mask from POSIX timer using SIGEV_THREAD.
+ 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 <stdio.h>
+#include <time.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/xthread.h>
+
+#include <internal-signals.h>
+
+static pthread_barrier_t barrier;
+static bool thread_handler_failure = false;
+
+static void
+thread_handler (union sigval sv)
+{
+ sigset_t ss;
+ sigprocmask (SIG_BLOCK, NULL, &ss);
+ if (test_verbose > 0)
+ printf ("%s: blocked signal mask = { ", __func__);
+ for (int sig = 1; sig < NSIG; sig++)
+ {
+#ifdef __linux__
+ /* POSIX timers threads created to handle SIGEV_THREAD block all
+ signals except SIGKILL, SIGSTOP and glibc internals one except
+ SIGTIMER. */
+ if (!sigismember (&ss, sig)
+ && (sig != SIGSETXID && sig != SIGCANCEL
+ && sig != SIGKILL && sig != SIGSTOP))
+ {
+ thread_handler_failure = true;
+ }
+#endif
+ if (test_verbose && sigismember (&ss, sig))
+ printf ("%d, ", sig);
+ }
+ if (test_verbose > 0)
+ printf ("}\n");
+
+ xpthread_barrier_wait (&barrier);
+}
+
+static int
+do_test (void)
+{
+ struct sigevent sev = { 0 };
+ sev.sigev_notify = SIGEV_THREAD;
+ sev.sigev_notify_function = &thread_handler;
+
+ timer_t timerid;
+ TEST_COMPARE (timer_create (CLOCK_REALTIME, &sev, &timerid), 0);
+
+ xpthread_barrier_init (&barrier, NULL, 2);
+
+ struct itimerspec trigger = { 0 };
+ trigger.it_value.tv_nsec = 1000000;
+ TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0);
+
+ xpthread_barrier_wait (&barrier);
+
+ TEST_COMPARE (thread_handler_failure, false);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -58,6 +58,11 @@ static const sigset_t sigall_set = {
.__val = {[0 ... _SIGSET_NWORDS-1 ] = -1 }
};
+static const sigset_t sigtimer_set = {
+ .__val = { [0] = __sigmask (SIGTIMER),
+ [1 ... _SIGSET_NWORDS-1] = 0 }
+};
+
/* Block all signals, including internal glibc ones. */
static inline void
__libc_signal_block_all (sigset_t *set)
@@ -76,6 +81,22 @@ __libc_signal_block_app (sigset_t *set)
_NSIG / 8);
}
+/* Block only SIGTIMER and return the previous set on SET. */
+static inline void
+__libc_signal_block_sigtimer (sigset_t *set)
+{
+ INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_BLOCK, &sigtimer_set, set,
+ _NSIG / 8);
+}
+
+/* Unblock only SIGTIMER and return the previous set on SET. */
+static inline void
+__libc_signal_unblock_sigtimer (sigset_t *set)
+{
+ INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sigtimer_set, set,
+ _NSIG / 8);
+}
+
/* Restore current process signal mask. */
static inline void
__libc_signal_restore_set (const sigset_t *set)
@@ -42,15 +42,9 @@ struct thread_start_data
static void *
timer_sigev_thread (void *arg)
{
- /* The parent thread has all signals blocked. This is a bit
- surprising for user code, although valid. We unblock all
- signals. */
- sigset_t ss;
- sigemptyset (&ss);
- INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_SETMASK, &ss, NULL, _NSIG / 8);
+ __libc_signal_unblock_sigtimer (NULL);
struct thread_start_data *td = (struct thread_start_data *) arg;
-
void (*thrfunc) (sigval_t) = td->thrfunc;
sigval_t sival = td->sival;
@@ -68,65 +62,49 @@ timer_sigev_thread (void *arg)
static void *
timer_helper_thread (void *arg)
{
- /* Wait for the SIGTIMER signal, allowing the setXid signal, and
- none else. */
- sigset_t ss;
- sigemptyset (&ss);
- __sigaddset (&ss, SIGTIMER);
-
/* Endless loop of waiting for signals. The loop is only ended when
the thread is canceled. */
while (1)
{
siginfo_t si;
- /* sigwaitinfo cannot be used here, since it deletes
- SIGCANCEL == SIGTIMER from the set. */
-
- /* XXX The size argument hopefully will have to be changed to the
- real size of the user-level sigset_t. */
- int result = SYSCALL_CANCEL (rt_sigtimedwait, &ss, &si, NULL, _NSIG / 8);
-
- if (result > 0)
+ while (sigwaitinfo (&sigtimer_set, &si) < 0);
+ if (si.si_code == SI_TIMER)
{
- if (si.si_code == SI_TIMER)
- {
- struct timer *tk = (struct timer *) si.si_ptr;
+ struct timer *tk = (struct timer *) si.si_ptr;
+
+ /* Check the timer is still used and will not go away
+ while we are reading the values here. */
+ pthread_mutex_lock (&__active_timer_sigev_thread_lock);
- /* Check the timer is still used and will not go away
- while we are reading the values here. */
- pthread_mutex_lock (&__active_timer_sigev_thread_lock);
+ struct timer *runp = __active_timer_sigev_thread;
+ while (runp != NULL)
+ if (runp == tk)
+ break;
+ else
+ runp = runp->next;
- struct timer *runp = __active_timer_sigev_thread;
- while (runp != NULL)
- if (runp == tk)
- break;
- else
- runp = runp->next;
+ if (runp != NULL)
+ {
+ struct thread_start_data *td = malloc (sizeof (*td));
- if (runp != NULL)
+ /* There is not much we can do if the allocation fails. */
+ if (td != NULL)
{
- struct thread_start_data *td = malloc (sizeof (*td));
-
- /* There is not much we can do if the allocation fails. */
- if (td != NULL)
- {
- /* This is the signal we are waiting for. */
- td->thrfunc = tk->thrfunc;
- td->sival = tk->sival;
-
- pthread_t th;
- (void) pthread_create (&th, &tk->attr,
- timer_sigev_thread, td);
- }
- }
+ /* This is the signal we are waiting for. */
+ td->thrfunc = tk->thrfunc;
+ td->sival = tk->sival;
- pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
+ pthread_t th;
+ pthread_create (&th, &tk->attr, timer_sigev_thread, td);
+ }
}
- else if (si.si_code == SI_TKILL)
- /* The thread is canceled. */
- pthread_exit (NULL);
+
+ pthread_mutex_unlock (&__active_timer_sigev_thread_lock);
}
+ else if (si.si_code == SI_TKILL)
+ /* The thread is canceled. */
+ pthread_exit (NULL);
}
}
@@ -160,14 +138,10 @@ __start_helper_thread (void)
/* Block all signals in the helper thread but SIGSETXID. To do this
thoroughly we temporarily have to block all signals here. The
- helper can lose wakeups if SIGCANCEL is not blocked throughout,
- but sigfillset omits it SIGSETXID. So, we add SIGCANCEL back
- explicitly here. */
+ helper can lose wakeups if SIGTIMER is not blocked throughout. */
sigset_t ss;
- sigset_t oss;
- sigfillset (&ss);
- __sigaddset (&ss, SIGCANCEL);
- INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_SETMASK, &ss, &oss, _NSIG / 8);
+ __libc_signal_block_app (&ss);
+ __libc_signal_block_sigtimer (NULL);
/* Create the helper thread for this timer. */
pthread_t th;
@@ -177,7 +151,7 @@ __start_helper_thread (void)
__helper_tid = ((struct pthread *) th)->tid;
/* Restore the signal mask. */
- INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_SETMASK, &oss, NULL, _NSIG / 8);
+ __libc_signal_restore_set (&ss);
/* No need for the attribute anymore. */
(void) pthread_attr_destroy (&attr);