[PATCH v6] linux: Do not spawn a new thread for SIGEV_THREAD (BZ 30558, 27895, 29705, 32833)
Carlos O'Donell
carlos@redhat.com
Mon Jul 6 22:09:59 GMT 2026
On 5/14/26 7:14 AM, Adhemerval Zanella wrote:
> The current timer_create SIGEV_THREAD implementation has some
> downsides:
Three issues:
1. timerid_clear is unused but still defined. Please delete.
2. Incomplete sentence "(or force a fresh allocation if)"?
3. Conformance of pthread_cancel vs notification thread. See POC.
> 1. There is no way to report failure at thread creation when a
> timer triggers. It means that it might occur unreported and with
> missed events depending of the system load.
OK. This is indeed the most serious issue. It prevents the use of SIGEV_THREAD
in functional safety contexts.
> 2. The background thread is also kept in the background even when there
> are no more timers, consuming resources and also misleading memory
> profile tools (BZ 29705).
OK. When we delete the timer the helper thread is exited.
> 3. There is a lot of metadata that needs to be kept: a control
> variable for helper thread creation, a list of active SIGEV_THREAD
> timers, atfork handlers to cleanup the list.
OK. We drop all of that.
> 4. timer_create does not propagate all thread attributes to the new
> thread (BZ 27895).
OK. We fix that.
> 5. Kernel might deliver in-flight events for a timer after it was
> destroyed by timer_delete. The timer_helper_thread mechanism to
> handle it does not cover all possible issue, which leads to
> callbacks being wrongly triggered (BZ 32833).
OK.
> This new implementation moves the thread creation to timer_create, so
> any failure is reported to the caller. Also, the same thread will
> serve multiple timers, thus there are no unreported missed events.
> Avoiding parallel timer activation also avoids possible parallel
> timer invocations seeing the same overrun value.
OK.
> To implement using SIGTIMER internally as SIGCANCEL, it requires to
> mask out SIGCANCEL on thread creation. It essentially disables async
> thread cancellation, but POSIX requires that SIGEV_THREAD is always
> created in detached mode and cancelling a detached thread is UB (glibc
> checks the internal tid, but the memory referenced by pthread_t might
> not always be valid at the moment of a pthread_cancel call).
Unfortunately it is not UB if another thread can prove the thread is
alive, and you can, which creates a conformance issue (POC included).
For example:
thread A
-> timer_create, SIGEV_THREAD
-> timer fires
thread S
-> sigwaitinfo returns.
-> run handler
-> Call pthread_self
-> Pass result to thread B thread B
-> enable async cancel -> Read pthread_t
-> while (1); -> pthread_cancel on S
It's possible the handler is passing data to other worker threads
and is sharing enough information to guarnatee that it is alive (and
that pthread_t is valid despite detached state), and needs asynchronous
cancellation, at which point it is valid to act on pthread_cancel().
The implementation prevents asynchronous cancellation from being used
because of the re-used same-signal, with the pthread_cancel() call not
acting.
Do we need another internal signal for this?
Or can we just reject pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, ...)
for the helper thread and document this issue?
A signal needs to be delivered to interrupt the process, but to do that
requires unblocking a signal. We have SIGSETXID unblocked. Could
cancellation requests also be sent via the multiplexed mechansim?
Assisted-by: LLM
~~~
/* POSIX requires SIGEV_THREAD notifications to behave as if a new thread
was created for each delivery. Such a thread must honor asynchronous
cancellation once the notify function enables it.
glibc's SIGEV_THREAD helper blocks all signals except SIGSETXID at thread
creation (see timer_create_sigev_thread), which prevents SIGCANCEL from
being delivered. This test demonstrates the resulting non-conformance:
pthread_cancel on the pthread_t obtained from pthread_self inside the
notify function has no effect and the test times out. */
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <support/check.h>
static pthread_t handler_thread;
static sem_t handler_started;
static void
on_timer (union sigval sv)
{
handler_thread = pthread_self ();
if (sem_post (&handler_started) != 0)
FAIL_EXIT1 ("sem_post from handler: %m");
TEST_COMPARE (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL), 0);
TEST_COMPARE (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL), 0);
while (1)
;
}
static int
do_test (void)
{
if (sem_init (&handler_started, 0, 0) != 0)
FAIL_EXIT1 ("sem_init: %m");
timer_t timerid;
struct sigevent ev =
{
.sigev_notify = SIGEV_THREAD,
.sigev_notify_function = on_timer,
};
if (timer_create (CLOCK_REALTIME, &ev, &timerid) == -1)
FAIL_EXIT1 ("timer_create: %m");
struct itimerspec its =
{ .it_value = { .tv_nsec = 10000000 /* 0.01s */ } };
if (timer_settime (timerid, 0, &its, NULL) == -1)
FAIL_EXIT1 ("timer_settime: %m");
if (sem_wait (&handler_started) != 0)
FAIL_EXIT1 ("sem_wait: %m");
/* Give the handler time to enter the busy loop. */
usleep (10000);
if (pthread_cancel (handler_thread) != 0)
FAIL_EXIT1 ("pthread_cancel: %m");
/* A conforming implementation cancels the notify thread; detect that by
waiting until pthread_kill no longer finds it. With the current glibc
SIGEV_THREAD helper this never happens and the test times out. */
for (;;)
{
int err = pthread_kill (handler_thread, 0);
if (err == ESRCH)
break;
if (err != 0)
FAIL_EXIT1 ("pthread_kill: %m");
usleep (10000);
}
struct itimerspec its_stop = { 0 };
if (timer_settime (timerid, 0, &its_stop, NULL) == -1)
FAIL_EXIT1 ("timer_settime (disarm): %m");
if (timer_delete (timerid) == -1)
FAIL_EXIT1 ("timer_delete: %m");
if (sem_destroy (&handler_started) != 0)
FAIL_EXIT1 ("sem_destroy: %m");
return 0;
}
#define TIMEOUT 3
#include <support/test-driver.c>
~~~
> And to avoid the need to recreate the thread for pthread_exit call
> (and having possible unreported missed events due to failed thread
> creation), the SIGEV_THREAD installs a cleanup handler that resets all
> internal thread state.
>
> It also prevents the re-use issue when a newly-allocated timer has
> in-flight events being delivered by the kernel (BZ 32833).
>
> Performance-wise it uses less CPU time for multiple thread activations,
> although each thread now requires a sigwaitinfo which generates more
> context-switches/page-faults (check comment 7 from BZ 30558). I would
> expect that latency should improve, since it avoids a thread creation
> for each timer expiration.
>
> Checked on aarch64-linux-gnu, x86_64-linux-gnu and i686-linux-gnu.
> --
> Changes from v6:
> - __pthread_init_stack no longer resets getrandom_buf; the per-firing
> reset was orphaning the vDSO buffer. Cleanup is left to
> __getrandom_vdso_release on thread exit.
> - Do not reset timerid on cached-stack reuse: every reader sees a
> value written by timer_create_sigev_thread before the setup barrier.
> - Document the per-firing cleanup in timer_helper_thread: POSIX
> requires SIGEV_THREAD to behave as if a fresh thread serviced each
> notification.
> - timerid_to_kernel_timer uses atomic_load_relaxed; the prior plain
> load raced with timer_delete's atomic_fetch_or_relaxed.
> - timer_create_sigev_thread switched timer_create from
> INLINE_SYSCALL_CALL to INTERNAL_SYSCALL_CALL so errno is set only
> after __pthread_barrier_wait, avoiding clobbering on the failure
> path.
>
> Changes from v5:
> - Call kernel timer_delete synchronously in ___timer_delete. This
> closes the window where the kernel could generate new timer events
> after timer_delete returned to the caller.
> - Use relaxed MO for the read of self->timerid. Both this load and the
> relaxed OR store in timerid_signal_delete are relaxed; the happens-before
> ordering between them is provided by the tgkill/sigwaitinfo signal
> mechanism, not by the atomic operations.
> - __pthread_attr_copy error propagation.
> - Rewrote rt/tst-timer7.c synchronization to avoid possible deadlock
> if scheduling forces the timer to trigger at same time.
> - Extended some comment and fixed some typos.
>
> Changes from v4:
> - Proper setup the thread TLS after each iteration and add a new
> test (tst-timer7.c) to check it.
> - Fix the build against some abi due missing clockid_t definition.
>
> Changes from v3:
> - Move thread reset state to __pthread_reset_state on pthread_create.c.
> - Fixed pthread_attr_t leak after copy.
> - Fixed struct pthread timerid placement.
>
> Changes from v2:
> - Fixed some issues with timer_delete due using timeid to signal the
> thread.
> - Added BZ#32833 as fixed bug.
> - Rebased against master.
> ---
> nptl/allocatestack.c | 23 +-
> nptl/descr.h | 3 +
> nptl/pthread_create.c | 80 ++++++
> rt/Makefile | 5 +-
> rt/tst-timer-sigmask.c | 7 +-
> rt/tst-timer6.c | 79 +++++
> rt/tst-timer7.c | 91 ++++++
> sysdeps/nptl/Makefile | 2 -
> sysdeps/nptl/fork.h | 2 -
> sysdeps/nptl/pthreadP.h | 14 +
> sysdeps/unix/sysv/linux/internal-signals.h | 8 -
> .../unix/sysv/linux/kernel-posix-cpu-timers.h | 2 +
> sysdeps/unix/sysv/linux/kernel-posix-timers.h | 80 +++---
> sysdeps/unix/sysv/linux/timer_create.c | 269 ++++++++++++------
> sysdeps/unix/sysv/linux/timer_delete.c | 50 ++--
> sysdeps/unix/sysv/linux/timer_routines.c | 154 ----------
> 16 files changed, 502 insertions(+), 367 deletions(-)
> create mode 100644 rt/tst-timer6.c
> create mode 100644 rt/tst-timer7.c
> delete mode 100644 sysdeps/unix/sysv/linux/timer_routines.c
>
> diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
> index b2ecb001136..f4eb62369b1 100644
> --- a/nptl/allocatestack.c
> +++ b/nptl/allocatestack.c
> @@ -124,28 +124,7 @@ get_cached_stack (size_t *sizep, void **memp)
> *sizep = result->stackblock_size;
> *memp = result->stackblock;
>
> - /* Cancellation handling is back to the default. */
> - result->cancelhandling = 0;
> - result->cleanup = NULL;
> - result->setup_failed = 0;
> -
> - /* No pending event. */
> - result->nextevent = NULL;
> -
> - result->exiting = false;
> - __libc_lock_init (result->exit_lock);
> - memset (&result->tls_state, 0, sizeof result->tls_state);
> -
> - result->getrandom_buf = NULL;
> -
> - /* Clear the DTV. */
> - dtv_t *dtv = GET_DTV (TLS_TPADJ (result));
> - for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
> - free (dtv[1 + cnt].pointer.to_free);
> - memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
> -
> - /* Re-initialize the TLS. */
> - _dl_allocate_tls_init (TLS_TPADJ (result), false);
> + __pthread_init_stack (result);
>
> return result;
> }
> diff --git a/nptl/descr.h b/nptl/descr.h
> index 627cc3980f0..520307c9738 100644
> --- a/nptl/descr.h
> +++ b/nptl/descr.h
> @@ -414,6 +414,9 @@ struct pthread
> /* Used on strsignal. */
> struct tls_internal_t tls_state;
>
> + /* POSIX per-process timer. */
> + int timerid;
> +
> /* getrandom vDSO per-thread opaque state. */
> void *getrandom_buf;
>
> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
> index 9a0cefb0f5a..12e5e11a0ba 100644
> --- a/nptl/pthread_create.c
> +++ b/nptl/pthread_create.c
> @@ -92,6 +92,41 @@ late_init (void)
> NULL, __NSIG_BYTES);
> }
>
> +static void
> +__pthread_init_stack (struct pthread *result)
> +{
> + /* Cancellation handling is back to the default. */
> + result->cancelhandling = 0;
> + result->cleanup = NULL;
> + result->setup_failed = 0;
> +
> + /* No pending event. */
> + result->nextevent = NULL;
> +
> + result->exiting = false;
> + __libc_lock_init (result->exit_lock);
> + memset (&result->tls_state, 0, sizeof result->tls_state);
> +
> + /* getrandom_buf and timerid are intentionally not reset here:
> +
> + - getrandom_buf: the prior thread's exit path calls
> + __getrandom_vdso_release, and resetting it here in would orphan a live
> + buffer (or force a fresh allocation if).
Missing word after "if"?
> +
> + - timerid: it is meaningful only for SIGEV_THREAD helper threads, and
> + its thread setup unconditionally writes before releasing the helper
> + thread from its setup barrier. */
> +
> + /* Clear the DTV. */
> + dtv_t *dtv = GET_DTV (TLS_TPADJ (result));
> + for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
> + free (dtv[1 + cnt].pointer.to_free);
> + memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
> +
> + /* Re-initialize the TLS. */
> + _dl_allocate_tls_init (TLS_TPADJ (result), false);
> +}
> +
> /* Code to allocate and deallocate a stack. */
> #include "allocatestack.c"
>
> @@ -644,6 +679,51 @@ report_thread_creation (struct pthread *pd)
> return false;
> }
>
> +/* Reset internal thread state as if the start thread routine was initially
> + called from pthread_create. It is used on POSIX timers to reset the
> + SIGEV_THREAD thread after a timer activation (as required by POSIX in
> + Realtime Signal Generation and Delivery): each firing must behave as if a
> + fresh thread was created, so TLS destructors, TSD destructors, libc
> + per-thread state, the DTV, the signal mask, and cancellation state are all
> + reset.
> +
> + The per-thread vDSO getrandom buffer (getrandom_buf) is *not* reset here.
> + It is internal, opaque state that advances forward-securely on each use, so
> + no observable data leaks across firings.
> +
> + The kernel timer id (timerid) is also *not* reset here: it identifies the
> + active timer for this helper thread and timer_delete signals exit by
> + setting its MSB, which the helper thread checks after each firing. */
> +void
> +__pthread_reset_state (void *arg)
> +{
> + struct pthread *self = THREAD_SELF;
> +
> + /* Call destructors for the thread_local TLS variables. */
> + call_function_static_weak (__call_tls_dtors);
> +
> + /* Run the destructor for the thread-local data. */
> + __nptl_deallocate_tsd ();
> +
> + /* Clean up any state libc stored in thread-local variables. */
> + __libc_thread_freeres ();
> +
> + /* Reset internal TCB state. */
> + struct pthread_reset_cleanup_args_t *args = arg;
> + self->cleanup_jmp_buf = args->cleanup_jmp_buf;
> + self->cleanup_jmp_buf->priv.data.prev = NULL;
> + self->cleanup_jmp_buf->priv.data.cleanup = NULL;
> + self->cleanup_jmp_buf->priv.data.canceltype = 0;
> + self->cleanup = NULL;
> + self->exc = (struct _Unwind_Exception) { 0 };
> + self->cancelhandling = 0;
> + self->nextevent = NULL;
> +
> + __pthread_init_stack (self);
> +
> + /* Reset to the expected initial signal mask. */
> + internal_signal_restore_set (&self->sigmask);
> +}
>
> int
> __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
> diff --git a/rt/Makefile b/rt/Makefile
> index 39a3e5620b3..2fe4d7ba6b1 100644
> --- a/rt/Makefile
> +++ b/rt/Makefile
> @@ -76,7 +76,9 @@ tests := tst-shm tst-timer tst-timer2 \
> tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \
> tst-clock_nanosleep2 \
> tst-shm-cancel \
> - tst-mqueue10
> + tst-mqueue10 \
> + tst-timer6 \
> + tst-timer7
> tests-internal := tst-timer-sigmask
>
> tests-time64 := \
> @@ -98,6 +100,7 @@ include ../Rules
> CFLAGS-aio_suspend.c += -fexceptions
> CFLAGS-mq_timedreceive.c += -fexceptions -fasynchronous-unwind-tables
> CFLAGS-mq_timedsend.c += -fexceptions -fasynchronous-unwind-tables
> +CFLAGS-timer_create.c += -fexceptions -fasynchronous-unwind-tables
>
> # Exclude fortified routines from being built with _FORTIFY_SOURCE
> routines_no_fortify += \
> diff --git a/rt/tst-timer-sigmask.c b/rt/tst-timer-sigmask.c
> index 869194bde68..52f44bd5666 100644
> --- a/rt/tst-timer-sigmask.c
> +++ b/rt/tst-timer-sigmask.c
> @@ -39,12 +39,9 @@ thread_handler (union sigval sv)
> for (int sig = 1; sig < NSIG; sig++)
> {
> /* POSIX timers threads created to handle SIGEV_THREAD block all
> - signals except SIGKILL, SIGSTOP and glibc internals ones. */
> + signals except SIGKILL, SIGSTOP, and SIGSETXID. */
> if (sigismember (&ss, sig))
> - {
> - TEST_VERIFY (sig != SIGKILL && sig != SIGSTOP);
> - TEST_VERIFY (!is_internal_signal (sig));
> - }
> + TEST_VERIFY (sig != SIGKILL && sig != SIGSTOP && sig != SIGSETXID);
> if (test_verbose && sigismember (&ss, sig))
> printf ("%d, ", sig);
> }
> diff --git a/rt/tst-timer6.c b/rt/tst-timer6.c
> new file mode 100644
> index 00000000000..545bfa6695b
> --- /dev/null
> +++ b/rt/tst-timer6.c
> @@ -0,0 +1,79 @@
> +/* Check re-use timer id for SIGEV_THREAD (BZ 32833)
> + Copyright (C) 2026 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; see the file COPYING.LIB. If
> + not, see <https://www.gnu.org/licenses/>. */
> +
> +#include <signal.h>
> +#include <time.h>
> +#include <support/check.h>
> +
> +/* The test depends of the system load and scheduler pressure, so the
> + number of iteration is arbitrary to not take too much time. */
> +enum { niters = 1<<13 };
> +
> +static void
> +on_good_timer (union sigval sv)
> +{
> +}
> +
> +static void
> +on_bad_timer (union sigval sv)
> +{
> + FAIL_EXIT1 ("triggered bad timer");
> +}
> +
> +static int
> +do_test (void)
> +{
> + struct itimerspec its_long = { .it_value = { .tv_sec = 180 } };
> + struct itimerspec its_short = { .it_value = { .tv_nsec = 1000 } };
> + struct itimerspec its_zero = { .it_interval = { .tv_sec = 0 } };
> +
> + struct sigevent ev_short =
> + {
> + .sigev_notify = SIGEV_THREAD,
> + .sigev_notify_function = on_good_timer,
> + };
> +
> + struct sigevent ev_long =
> + {
> + .sigev_notify = SIGEV_THREAD,
> + .sigev_notify_function = on_bad_timer,
> + };
> +
> + for (int which = 0; which < niters; which++)
> + {
> + struct sigevent *ev = which & 0x1 ? &ev_short : &ev_long;
> + struct itimerspec *its = which & 0x1 ? &its_short : &its_long;
> +
> + timer_t timerid;
> + if (timer_create (CLOCK_REALTIME, ev, &timerid) == -1)
> + FAIL_EXIT1 ("timer_create: %m");
> +
> + if (timer_settime (timerid, 0, its, NULL) == -1)
> + FAIL_EXIT1 ("timer_settime: %m");
> +
> + if (timer_settime (timerid, 0, &its_zero, NULL) == -1)
> + FAIL_EXIT1 ("timer_settime: %m");
> +
> + if (timer_delete (timerid) == -1)
> + FAIL_EXIT1 ("timer_delete: %m");
> + }
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/rt/tst-timer7.c b/rt/tst-timer7.c
> new file mode 100644
> index 00000000000..80663c1c222
> --- /dev/null
> +++ b/rt/tst-timer7.c
> @@ -0,0 +1,91 @@
> +/* Check if thread local storage is reset on each SIGEV_THREAD trigger.
> + Copyright (C) 2026 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; see the file COPYING.LIB. If
> + not, see <https://www.gnu.org/licenses/>. */
> +
> +#include <array_length.h>
> +#include <semaphore.h>
> +#include <signal.h>
> +#include <string.h>
> +#include <time.h>
> +
> +#include <support/check.h>
> +
> +static sem_t sem;
> +
> +static __thread int var1;
> +#define VAR2_LEN 32
> +static __thread char var2[] = { [0 ... VAR2_LEN] = 0xcc };
> +
> +static const char var2_expected[] = { [0 ... VAR2_LEN] = 0xcc };
> +
> +static void
> +on_timer (union sigval sv)
> +{
> + TEST_COMPARE (var1, 0);
> + TEST_COMPARE_BLOB (var2, array_length (var2),
> + var2_expected, array_length (var2_expected));
> +
> + var1 = 1;
> + memset (var2, 0x00, array_length (var2));
> +
> + sem_post (&sem);
> +}
> +
> +#define NITERS 10
> +
> +static int
> +do_test (void)
> +{
> + const struct itimerspec its =
> + { .it_value = { .tv_nsec = 10000000 /* 0.01s */ },
> + .it_interval = { .tv_nsec = 10000000 /* 0.01s */ } };
> + const struct itimerspec its_stop = { 0 };
> +
> + sem_init (&sem, 0, 0);
> +
> + timer_t timerid;
> + struct sigevent ev =
> + {
> + .sigev_notify = SIGEV_THREAD,
> + .sigev_notify_function = on_timer,
> + };
> + if (timer_create (CLOCK_REALTIME, &ev, &timerid) == -1)
> + FAIL_EXIT1 ("timer_create: %m");
> +
> + if (timer_settime (timerid, 0, &its, NULL) == -1)
> + FAIL_EXIT1 ("timer_settime: %m");
> +
> + for (int i = 0; i < NITERS; i++)
> + {
> + if (sem_wait (&sem) != 0)
> + FAIL_EXIT1 ("sem_wait: %m");
> + }
> +
> + /* Disarm before deleting to minimise the chance of an in-flight
> + invocation racing with timer_delete. */
> + if (timer_settime (timerid, 0, &its_stop, NULL) == -1)
> + FAIL_EXIT1 ("timer_settime: %m");
> +
> + if (timer_delete (timerid) == -1)
> + FAIL_EXIT1 ("timer_delete: %m");
> +
> + sem_destroy (&sem);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/sysdeps/nptl/Makefile b/sysdeps/nptl/Makefile
> index 03c9c056401..47f5ba5824f 100644
> --- a/sysdeps/nptl/Makefile
> +++ b/sysdeps/nptl/Makefile
> @@ -16,8 +16,6 @@
> # <https://www.gnu.org/licenses/>.
>
> ifeq ($(subdir),rt)
> -sysdep_routines += timer_routines
> -
> tests += tst-mqueue8x
> CFLAGS-tst-mqueue8x.c += -fexceptions
> endif
> diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h
> index c09e57c5abd..31ac12812f3 100644
> --- a/sysdeps/nptl/fork.h
> +++ b/sysdeps/nptl/fork.h
> @@ -20,7 +20,6 @@
> #define _FORK_H
>
> #include <assert.h>
> -#include <kernel-posix-timers.h>
> #include <ldsodefs.h>
> #include <list.h>
> #include <mqueue.h>
> @@ -46,7 +45,6 @@ fork_system_setup_after_fork (void)
> __default_pthread_attr_lock = LLL_LOCK_INITIALIZER;
>
> call_function_static_weak (__mq_notify_fork_subprocess);
> - call_function_static_weak (__timer_fork_subprocess);
> call_function_static_weak (__getrandom_fork_subprocess);
> }
>
> diff --git a/sysdeps/nptl/pthreadP.h b/sysdeps/nptl/pthreadP.h
> index de432d40324..1470aaf4856 100644
> --- a/sysdeps/nptl/pthreadP.h
> +++ b/sysdeps/nptl/pthreadP.h
> @@ -667,6 +667,20 @@ int __pthread_attr_extension (struct pthread_attr *attr) attribute_hidden
> # define PTHREAD_STATIC_FN_REQUIRE(name) __asm (".globl " #name);
> #endif
>
> +struct pthread_reset_cleanup_args_t
> +{
> + struct pthread_unwind_buf *cleanup_jmp_buf;
> + jmp_buf jb;
> +};
> +
> +/* Reset the thread's internal state to a point as close to the initial call
> + to pthread_create. It is designed to be used as the argument to
> + pthread_cleanup_push along with a struct pthread_reset_cleanup_args_t
> + pointer in args with a valid cleanup_jmp_buf used to reset the threads own
> + copy. */
> +void __pthread_reset_state (void *arg) attribute_hidden;
> +
> +
> /* Make a deep copy of the attribute *SOURCE in *TARGET. *TARGET is
> not assumed to have been initialized. Returns 0 on success, or a
> positive error code otherwise. */
> diff --git a/sysdeps/unix/sysv/linux/internal-signals.h b/sysdeps/unix/sysv/linux/internal-signals.h
> index a8f1c87b463..02a086f7d5d 100644
> --- a/sysdeps/unix/sysv/linux/internal-signals.h
> +++ b/sysdeps/unix/sysv/linux/internal-signals.h
> @@ -108,12 +108,4 @@ static const sigset_t sigtimer_set = {
> }
> };
>
> -/* Unblock only SIGTIMER. */
> -static inline void
> -signal_unblock_sigtimer (void)
> -{
> - INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sigtimer_set, NULL,
> - __NSIG_BYTES);
> -}
> -
> #endif
> diff --git a/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h b/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h
> index bea1e0e62da..c95b504d5f6 100644
> --- a/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h
> +++ b/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h
> @@ -20,6 +20,8 @@
> #define CPUCLOCK_SCHED 2
> #define CPUCLOCK_MAX 3
>
> +#include <sys/types.h>
> +
> static inline clockid_t
> make_process_cpuclock (unsigned int pid, clockid_t clock)
> {
> diff --git a/sysdeps/unix/sysv/linux/kernel-posix-timers.h b/sysdeps/unix/sysv/linux/kernel-posix-timers.h
> index 9b7859b1c65..ac94fbd601d 100644
> --- a/sysdeps/unix/sysv/linux/kernel-posix-timers.h
> +++ b/sysdeps/unix/sysv/linux/kernel-posix-timers.h
> @@ -19,29 +19,7 @@
> #include <setjmp.h>
> #include <signal.h>
> #include <sys/types.h>
> -
> -
> -/* Nonzero if the system calls are not available. */
> -extern int __no_posix_timers attribute_hidden;
> -
> -/* Callback to start helper thread. */
> -extern void __timer_start_helper_thread (void) attribute_hidden;
> -
> -/* Control variable for helper thread creation. */
> -extern pthread_once_t __timer_helper_once attribute_hidden;
> -
> -/* Called from fork so that the new subprocess re-creates the
> - notification thread if necessary. */
> -void __timer_fork_subprocess (void) attribute_hidden;
> -
> -/* TID of the helper thread. */
> -extern pid_t __timer_helper_tid attribute_hidden;
> -
> -/* List of active SIGEV_THREAD timers. */
> -extern struct timer *__timer_active_sigev_thread attribute_hidden;
> -
> -/* Lock for __timer_active_sigev_thread. */
> -extern pthread_mutex_t __timer_active_sigev_thread_lock attribute_hidden;
> +#include <nptl/descr.h>
>
> extern __typeof (timer_create) __timer_create;
> libc_hidden_proto (__timer_create)
> @@ -53,25 +31,12 @@ libc_hidden_proto (__timer_getoverrun)
> /* Type of timers in the kernel. */
> typedef int kernel_timer_t;
>
> -/* Internal representation of SIGEV_THREAD timer. */
> -struct timer
> -{
> - kernel_timer_t ktimerid;
> -
> - void (*thrfunc) (sigval_t);
> - sigval_t sival;
> - pthread_attr_t attr;
> -
> - /* Next element in list of active SIGEV_THREAD timers. */
> - struct timer *next;
> -};
> -
> -
> /* For !SIGEV_THREAD, the resulting 'timer_t' is the returned kernel timer
> - identifier (kernel_timer_t), while for SIGEV_THREAD it uses the fact malloc
> - returns at least _Alignof (max_align_t) pointers plus that valid
> - kernel_timer_t are always positive to set the MSB bit of the returned
> - 'timer_t' to indicate the timer handles a SIGEV_THREAD. */
> + identifier (kernel_timer_t), while for SIGEV_THREAD it assumes the
> + pthread_t at least 8-bytes aligned.
> +
> + For SIGEV_THREAD, the sign bit (INT_MIN) is set on timer_delete to
> + signal the helper thread to exit its sigwaitinfo loop. */
>
> static inline timer_t
> kernel_timer_to_timerid (kernel_timer_t ktimerid)
> @@ -80,7 +45,7 @@ kernel_timer_to_timerid (kernel_timer_t ktimerid)
> }
>
> static inline timer_t
> -timer_to_timerid (struct timer *ptr)
> +pthread_to_timerid (pthread_t ptr)
> {
> return (timer_t) (INTPTR_MIN | (uintptr_t) ptr >> 1);
> }
> @@ -91,19 +56,38 @@ timer_is_sigev_thread (timer_t timerid)
> return (intptr_t) timerid < 0;
> }
>
> -static inline struct timer *
> -timerid_to_timer (timer_t timerid)
> +static inline struct pthread *
> +timerid_to_pthread (timer_t timerid)
> {
> - return (struct timer *)((uintptr_t) timerid << 1);
> + return (struct pthread *)((uintptr_t) timerid << 1);
> }
>
> static inline kernel_timer_t
> timerid_to_kernel_timer (timer_t timerid)
> {
> if (timer_is_sigev_thread (timerid))
> - return timerid_to_timer (timerid)->ktimerid;
> - else
> - return (kernel_timer_t) ((uintptr_t) timerid);
> + {
> + /* The load is concurrent with timer_delete's atomic OR of INT_MIN (see
> + timerid_signal_delete) when another thread is deleting the same
> + timer. */
> + struct pthread *pthr = timerid_to_pthread (timerid);
> + return atomic_load_relaxed (&pthr->timerid) & INT_MAX;
> + }
> + return (uintptr_t) timerid;
> +}
> +
> +static inline void
> +timerid_signal_delete (kernel_timer_t *timerid)
> +{
> + /* Relaxed MO is sufficient because this is followed by a signal which is a
> + full memory barrier. */
> + atomic_fetch_or_relaxed (timerid, INT_MIN);
> +}
> +
> +static inline kernel_timer_t
> +timerid_clear (kernel_timer_t timerid)
> +{
> + return timerid & INT_MAX;
> }
>
> /* New targets use int instead of timer_t. The difference only
> diff --git a/sysdeps/unix/sysv/linux/timer_create.c b/sysdeps/unix/sysv/linux/timer_create.c
> index 0889ce66f5e..d293892ecf8 100644
> --- a/sysdeps/unix/sysv/linux/timer_create.c
> +++ b/sysdeps/unix/sysv/linux/timer_create.c
> @@ -15,46 +15,174 @@
> License along with the GNU C Library; see the file COPYING.LIB. If
> not, see <https://www.gnu.org/licenses/>. */
>
> -#include <errno.h>
> -#include <pthread.h>
> -#include <signal.h>
> -#include <stdlib.h>
> -#include <string.h>
> -#include <time.h>
> -#include <sysdep.h>
> -#include <internaltypes.h>
> +#include <jmpbuf-unwind.h>
> +#include <kernel-posix-cpu-timers.h>
> +#include <kernel-posix-timers.h>
> +#include <ldsodefs.h>
> +#include <libc-internal.h>
> +#include <libc-lock.h>
> #include <pthreadP.h>
> -#include "kernel-posix-timers.h"
> -#include "kernel-posix-cpu-timers.h"
> #include <shlib-compat.h>
>
> +struct timer_helper_thread_args_t
> +{
> + /* The barrier is used to synchronize the arguments copy from timer_create
> + and the SIGEV_THREAD thread and to instruct the thread to exit if the
> + timer_create syscall fails. */
> + pthread_barrier_t barrier;
> + struct sigevent *evp;
> +};
> +
> +static void *
> +timer_helper_thread (void *arg)
> +{
> + struct pthread *self = THREAD_SELF;
> + struct timer_helper_thread_args_t *args = arg;
> + struct pthread_reset_cleanup_args_t clargs = {
> + .cleanup_jmp_buf = self->cleanup_jmp_buf
> + };
> +
> + void (*thrfunc) (sigval_t) = args->evp->sigev_notify_function;
> + sigval_t sival = args->evp->sigev_value;
> +
> + __pthread_barrier_wait (&args->barrier);
> + /* timer_create syscall failed. */
> + if (self->exiting)
> + return 0;
> +
> + while (1)
> + {
> + siginfo_t si;
> + while (__sigwaitinfo (&sigtimer_set, &si) < 0);
> +
> + if (si.si_code == SI_TIMER && !setjmp (clargs.jb))
> + {
> + /* POSIX requires SIGEV_THREAD notifications to behave as if a new
> + thread was created for each delivery. Since the same helper
> + thread serves multiple firings (and is also reused after a
> + pthread_exit from thrfunc), the cleanup handler resets all
> + observable per-thread state - TLS destructors, TSD, libc
> + thread-local state, DTV, signal mask, and cancellation state -
> + on the way out. State that must survive across firings is
> + intentionally preserved: the vDSO getrandom buffer (opaque,
> + forward-secure) and timerid (used to detect timer_delete via
> + its MSB after each firing). */
> + pthread_cleanup_push (__pthread_reset_state, &clargs);
> + thrfunc (sival);
> + pthread_cleanup_pop (1);
> + }
> +
> + /* timer_delete sets the MSB and sends SIGTIMER to wake this thread.
> + Relaxed MO is sufficient because signaling this thread is a memory
> + barrier. */
> + if (atomic_load_relaxed (&self->timerid) < 0)
> + break;
> + }
> +
> + return NULL;
> +}
> +
> +/* Set up a SIGEV_THREAD timer: spawn the helper thread that will run the
> + user's notify function on each firing, then create the kernel timer bound
> + to that thread's TID. Returns 0 on success and stores the resulting
> + timer_t in *TIMERID; returns -1 with errno set on failure (from
> + __pthread_attr_setsigmask_internal, __pthread_create, or the timer_create
> + syscall). ATTR is consumed but not destroyed by this function. */
> +static int
> +timer_create_sigev_thread (clockid_t clockid, struct sigevent *evp,
> + timer_t *timerid, pthread_attr_t *attr)
> +{
> + /* Block all signals in the helper thread but SIGSETXID. */
> + sigset_t ss;
> + __sigfillset (&ss);
> + __sigdelset (&ss, SIGSETXID);
> + if (__pthread_attr_setsigmask_internal (attr, &ss) < 0)
> + return -1;
> +
> + struct timer_helper_thread_args_t args = { .evp = evp };
> + __pthread_barrier_init (&args.barrier, NULL, 2);
> +
> + pthread_t th;
> + int r = __pthread_create (&th, attr, timer_helper_thread, &args);
> + if (r != 0)
> + {
> + __set_errno (r);
> + return -1;
> + }
> +
> + struct pthread *pthr = (struct pthread *)th;
> + /* SIGEV_THREAD_ID delivers the signal to a specific thread by TID.
> + SIGEV_SIGNAL is not combined here because SIGEV_THREAD_ID already
> + implies signal delivery; the kernel treats them as orthogonal bits
> + and the TID field alone is sufficient to route SIGTIMER correctly. */
> + struct sigevent kevp =
> + {
> + .sigev_value.sival_ptr = NULL,
> + .sigev_signo = SIGTIMER,
> + .sigev_notify = SIGEV_THREAD_ID,
> + ._sigev_un = { ._tid = pthr->tid },
> + };
> +
> + /* Use INTERNAL_SYSCALL_CALL so the error code can be captured directly
> + without going through errno; errno is set only after the barrier
> + wait below, which would otherwise be free to clobber it. */
> + kernel_timer_t ktimerid;
> + long int sc = INTERNAL_SYSCALL_CALL (timer_create, clockid, &kevp,
> + &ktimerid);
> + if (INTERNAL_SYSCALL_ERROR_P (sc))
> + {
> + ktimerid = -1;
> + /* On timer creation failure we need to signal the helper thread to
> + exit and we cannot use a negative timerid value after the
> + pthread_barrier_wait because we cannot distinguish between a timer
> + creation failure and a request to delete a timer if it happens to
> + arrive quickly (e.g. two timers are created in sequence, where the
> + first succeeds).
> +
> + We re-use the 'exiting' member to signal the failure, it is set only
> + at pthread_create to prevent pthread_kill from sending further
> + signals. Since the thread should not be user-visible, signals are
> + only sent during timer_delete. */
> + pthr->exiting = true;
> + }
> + pthr->timerid = ktimerid;
> + /* Signal the thread to continue execution after it copies the arguments
> + or exit if the timer can not be created. */
> + __pthread_barrier_wait (&args.barrier);
> +
> + if (ktimerid < 0)
> + {
> + __set_errno (INTERNAL_SYSCALL_ERRNO (sc));
> + return -1;
> + }
> +
> + *timerid = pthread_to_timerid (th);
> +
> + return 0;
> +}
> +
> int
> ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
> {
> - {
> - clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID
> - ? PROCESS_CLOCK
> - : clock_id == CLOCK_THREAD_CPUTIME_ID
> - ? THREAD_CLOCK
> - : clock_id);
> + clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID
> + ? PROCESS_CLOCK
> + : clock_id == CLOCK_THREAD_CPUTIME_ID
> + ? THREAD_CLOCK
> + : clock_id);
>
> - /* If the user wants notification via a thread we need to handle
> - this special. */
> - if (evp == NULL
> - || __builtin_expect (evp->sigev_notify != SIGEV_THREAD, 1))
> + switch (evp != NULL ? evp->sigev_notify : SIGEV_SIGNAL)
> + {
> + case SIGEV_NONE:
> + case SIGEV_SIGNAL:
> + case SIGEV_THREAD_ID:
> {
> - struct sigevent local_evp;
> -
> + struct sigevent kevp;
> if (evp == NULL)
> {
> - /* The kernel has to pass up the timer ID which is a
> - userlevel object. Therefore we cannot leave it up to
> - the kernel to determine it. */
> - local_evp.sigev_notify = SIGEV_SIGNAL;
> - local_evp.sigev_signo = SIGALRM;
> - local_evp.sigev_value.sival_ptr = NULL;
> -
> - evp = &local_evp;
> + kevp.sigev_notify = SIGEV_SIGNAL;
> + kevp.sigev_signo = SIGALRM;
> + kevp.sigev_value.sival_ptr = NULL;
> + evp = &kevp;
> }
>
> kernel_timer_t ktimerid;
> @@ -64,75 +192,34 @@ ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
>
> *timerid = kernel_timer_to_timerid (ktimerid);
> }
> - else
> + break;
> + case SIGEV_THREAD:
> {
> - /* Create the helper thread. */
> - __pthread_once (&__timer_helper_once, __timer_start_helper_thread);
> - if (__timer_helper_tid == 0)
> - {
> - /* No resources to start the helper thread. */
> - __set_errno (EAGAIN);
> - return -1;
> - }
> -
> - struct timer *newp = malloc (sizeof (struct timer));
> - if (newp == NULL)
> - return -1;
> -
> - /* Copy the thread parameters the user provided. */
> - newp->sival = evp->sigev_value;
> - newp->thrfunc = evp->sigev_notify_function;
> -
> - /* We cannot simply copy the thread attributes since the
> - implementation might keep internal information for
> - each instance. */
> - __pthread_attr_init (&newp->attr);
> + pthread_attr_t attr;
> if (evp->sigev_notify_attributes != NULL)
> {
> - struct pthread_attr *nattr;
> - struct pthread_attr *oattr;
> -
> - nattr = (struct pthread_attr *) &newp->attr;
> - oattr = (struct pthread_attr *) evp->sigev_notify_attributes;
> -
> - nattr->schedparam = oattr->schedparam;
> - nattr->schedpolicy = oattr->schedpolicy;
> - nattr->flags = oattr->flags;
> - nattr->guardsize = oattr->guardsize;
> - nattr->stackaddr = oattr->stackaddr;
> - nattr->stacksize = oattr->stacksize;
> + int r = __pthread_attr_copy (&attr, evp->sigev_notify_attributes);
> + if (r != 0)
> + {
> + __set_errno (r);
> + return -1;
> + }
> }
> + else
> + __pthread_attr_init (&attr);
> + __pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
>
> - /* In any case set the detach flag. */
> - __pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED);
> + int r = timer_create_sigev_thread (syscall_clockid, evp, timerid,
> + &attr);
>
> - /* Create the event structure for the kernel timer. */
> - struct sigevent sev =
> - { .sigev_value.sival_ptr = newp,
> - .sigev_signo = SIGTIMER,
> - .sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID,
> - ._sigev_un = { ._pad = { [0] = __timer_helper_tid } } };
> + __pthread_attr_destroy (&attr);
>
> - /* Create the timer. */
> - int res;
> - res = INTERNAL_SYSCALL_CALL (timer_create, syscall_clockid, &sev,
> - &newp->ktimerid);
> - if (INTERNAL_SYSCALL_ERROR_P (res))
> - {
> - free (newp);
> - __set_errno (INTERNAL_SYSCALL_ERRNO (res));
> - return -1;
> - }
> -
> - /* Add to the queue of active timers with thread delivery. */
> - __pthread_mutex_lock (&__timer_active_sigev_thread_lock);
> - newp->next = __timer_active_sigev_thread;
> - __timer_active_sigev_thread = newp;
> - __pthread_mutex_unlock (&__timer_active_sigev_thread_lock);
> -
> - *timerid = timer_to_timerid (newp);
> + return r;
> }
> - }
> + default:
> + __set_errno (EINVAL);
> + return -1;
> + }
>
> return 0;
> }
> diff --git a/sysdeps/unix/sysv/linux/timer_delete.c b/sysdeps/unix/sysv/linux/timer_delete.c
> index 9a6e74328e7..3422342b5ca 100644
> --- a/sysdeps/unix/sysv/linux/timer_delete.c
> +++ b/sysdeps/unix/sysv/linux/timer_delete.c
> @@ -15,10 +15,8 @@
> License along with the GNU C Library; see the file COPYING.LIB. If
> not, see <https://www.gnu.org/licenses/>. */
>
> -#include <errno.h>
> -#include <stdlib.h>
> +#include <unistd.h>
> #include <time.h>
> -#include <sysdep.h>
> #include "kernel-posix-timers.h"
> #include <pthreadP.h>
> #include <shlib-compat.h>
> @@ -26,42 +24,26 @@
> int
> ___timer_delete (timer_t timerid)
> {
> - kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid);
> - int res = INLINE_SYSCALL_CALL (timer_delete, ktimerid);
> -
> - if (res == 0)
> + if (timer_is_sigev_thread (timerid))
> {
> - if (timer_is_sigev_thread (timerid))
> - {
> - struct timer *kt = timerid_to_timer (timerid);
> + struct pthread *th = timerid_to_pthread (timerid);
> + kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid);
>
> - /* Remove the timer from the list. */
> - __pthread_mutex_lock (&__timer_active_sigev_thread_lock);
> - if (__timer_active_sigev_thread == kt)
> - __timer_active_sigev_thread = kt->next;
> - else
> - {
> - struct timer *prevp = __timer_active_sigev_thread;
> - while (prevp->next != NULL)
> - if (prevp->next == kt)
> - {
> - prevp->next = kt->next;
> - break;
> - }
> - else
> - prevp = prevp->next;
> - }
> - __pthread_mutex_unlock (&__timer_active_sigev_thread_lock);
> -
> - free (kt);
> - }
> + /* Delete the kernel timer first so no new events are generated
> + after this function returns. */
> + int ret = INLINE_SYSCALL_CALL (timer_delete, ktimerid);
> + if (ret != 0)
> + return ret;
>
> + /* Signal the helper thread to exit its sigwaitinfo loop. */
> + timerid_signal_delete (&th->timerid);
> + /* We can send the signal directly instead of through
> + __pthread_kill_internal because the thread is not user-visible
> + and it blocks SIGTIMER. */
> + INTERNAL_SYSCALL_CALL (tgkill, __getpid (), th->tid, SIGTIMER);
> return 0;
> }
> -
> - /* The kernel timer is not known or something else bad happened.
> - Return the error. */
> - return -1;
> + return INLINE_SYSCALL_CALL (timer_delete, timerid);
> }
> versioned_symbol (libc, ___timer_delete, timer_delete, GLIBC_2_34);
> libc_hidden_ver (___timer_delete, __timer_delete)
> diff --git a/sysdeps/unix/sysv/linux/timer_routines.c b/sysdeps/unix/sysv/linux/timer_routines.c
> deleted file mode 100644
> index 7ba6dd78ba7..00000000000
> --- a/sysdeps/unix/sysv/linux/timer_routines.c
> +++ /dev/null
> @@ -1,154 +0,0 @@
> -/* Copyright (C) 2003-2026 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; see the file COPYING.LIB. If
> - not, see <https://www.gnu.org/licenses/>. */
> -
> -#include <errno.h>
> -#include <setjmp.h>
> -#include <signal.h>
> -#include <stdbool.h>
> -#include <sysdep-cancel.h>
> -#include <pthreadP.h>
> -#include "kernel-posix-timers.h"
> -
> -
> -/* List of active SIGEV_THREAD timers. */
> -struct timer *__timer_active_sigev_thread;
> -
> -/* Lock for _timer_active_sigev_thread. */
> -pthread_mutex_t __timer_active_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER;
> -
> -struct thread_start_data
> -{
> - void (*thrfunc) (sigval_t);
> - sigval_t sival;
> -};
> -
> -
> -/* Helper thread to call the user-provided function. */
> -static void *
> -timer_sigev_thread (void *arg)
> -{
> - signal_unblock_sigtimer ();
> -
> - struct thread_start_data *td = (struct thread_start_data *) arg;
> - void (*thrfunc) (sigval_t) = td->thrfunc;
> - sigval_t sival = td->sival;
> -
> - /* The TD object was allocated in timer_helper_thread. */
> - free (td);
> -
> - /* Call the user-provided function. */
> - thrfunc (sival);
> -
> - return NULL;
> -}
> -
> -
> -/* Helper function to support starting threads for SIGEV_THREAD. */
> -static _Noreturn void *
> -timer_helper_thread (void *arg)
> -{
> - /* Endless loop of waiting for signals. The loop is only ended when
> - the thread is canceled. */
> - while (1)
> - {
> - siginfo_t si;
> -
> - while (__sigwaitinfo (&sigtimer_set, &si) < 0);
> - if (si.si_code == SI_TIMER)
> - {
> - 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 (&__timer_active_sigev_thread_lock);
> -
> - struct timer *runp = __timer_active_sigev_thread;
> - while (runp != NULL)
> - if (runp == tk)
> - break;
> - else
> - runp = runp->next;
> -
> - if (runp != 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;
> - __pthread_create (&th, &tk->attr, timer_sigev_thread, td);
> - }
> - }
> -
> - __pthread_mutex_unlock (&__timer_active_sigev_thread_lock);
> - }
> - }
> -}
> -
> -
> -/* Control variable for helper thread creation. */
> -pthread_once_t __timer_helper_once = PTHREAD_ONCE_INIT;
> -
> -
> -/* TID of the helper thread. */
> -pid_t __timer_helper_tid;
> -
> -
> -/* Reset variables so that after a fork a new helper thread gets started. */
> -void
> -__timer_fork_subprocess (void)
> -{
> - __timer_helper_once = PTHREAD_ONCE_INIT;
> - __timer_helper_tid = 0;
> -}
> -
> -
> -void
> -__timer_start_helper_thread (void)
> -{
> - /* The helper thread needs only very little resources
> - and should go away automatically when canceled. */
> - pthread_attr_t attr;
> - __pthread_attr_init (&attr);
> - __pthread_attr_setstacksize (&attr, __pthread_get_minstack (&attr));
> -
> - /* Block all signals in the helper thread but SIGSETXID. */
> - sigset_t ss;
> - __sigfillset (&ss);
> - __sigdelset (&ss, SIGSETXID);
> - int res = __pthread_attr_setsigmask_internal (&attr, &ss);
> - if (res != 0)
> - {
> - __pthread_attr_destroy (&attr);
> - return;
> - }
> -
> - /* Create the helper thread for this timer. */
> - pthread_t th;
> - res = __pthread_create (&th, &attr, timer_helper_thread, NULL);
> - if (res == 0)
> - /* We managed to start the helper thread. */
> - __timer_helper_tid = ((struct pthread *) th)->tid;
> -
> - /* No need for the attribute anymore. */
> - __pthread_attr_destroy (&attr);
> -}
--
Cheers,
Carlos.
More information about the Libc-alpha
mailing list