[PATCH v5] linux: Do not spawn a new thread for SIGEV_THREAD (BZ 30558, 27895, 29705, 32833)

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Wed May 13 20:38:05 GMT 2026



On 13/05/26 15:57, Carlos O'Donell wrote:
> On 5/5/26 11:39 AM, Adhemerval Zanella wrote:
>> The current timer_create SIGEV_THREAD implementation has some
>> downsides:
> 
> Looking forward to v6.

Thanks, I will send a v7 with the comments addressed.

> 
> Please look at the 15 "Review:" items.
> 
>>    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 calls for creating the thread up front for timer_create with SIGEV_THREAD.
> 
>>
>>    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. This calls for having the thread exit when the timer is deleted via timer_delete.
> 
>>
>>    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. This calls for minimal overhead overall.
> 
>>
>>    4. timer_create does not propagate all thread attributes to the new
>>       thread (BZ 27895).
> 
> OK. This calls for creating the thread early with timer_create to propagate the
> thread attributs to the new thread at that deterministic point in time.
> 
>>
>>    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. We need to handle the in-flight delivery gracefully, likely by not
> reusing IDs.
> 
>>
>> 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).
> 
> OK.
> 
>> 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.
> 
> OK.
> 
>> It also prevents the re-use issue when a newly-allocated timer has
>> in-flight events being delivered by the kernel (BZ 32833).
> 
> OK.
> 
>> 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.
> 
> OK.
> 
>> Checked on aarch64-linux-gnu, x86_64-linux-gnu and i686-linux-gnu.
>> -- 
>> 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.
> 
> OK.
> 
>> - Atomic read of self->timerid (timer_create.c:66), to pair correctly
>>    with the atomic_fetch_or_relaxed store in timerid_signal_delete
> 
> Review:
> 
> You can't "pair" two relaxed MO reads becasue they do not synchronize-with.
> 
> You could pair a Release MO and Acquire MO and document them as such.
> 
> Did you mean something else here?

I meant something else indeed, I changed to:

- 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                         |  61 +++++
>>   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                       |  12 +
>>   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 |  75 ++----
>>   sysdeps/unix/sysv/linux/timer_create.c        | 243 +++++++++++-------
>>   sysdeps/unix/sysv/linux/timer_delete.c        |  50 ++--
>>   sysdeps/unix/sysv/linux/timer_routines.c      | 154 -----------
>>   16 files changed, 450 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);
> 
> OK. Refactor for use by the timer helper thread.
> 
>>       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;
> 
> OK. Expands the pthread_t structure to include the per-process timer ID.
> 
>> +
>>     /* getrandom vDSO per-thread opaque state.  */
>>     void *getrandom_buf;
>>   diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
>> index 9a0cefb0f5a..db1815f1511 100644
>> --- a/nptl/pthread_create.c
>> +++ b/nptl/pthread_create.c
>> @@ -92,6 +92,33 @@ 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;
> 
> OK. Resetting.
> 
>> +  __libc_lock_init (result->exit_lock);
>> +  memset (&result->tls_state, 0, sizeof result->tls_state);
>> +
>> +  result->getrandom_buf = NULL;
> 
> Review:
> 
> The current pthread_push/pop always calls the cleanup function to reset.
> 
> Therefore on each timer firing we reset the getrandom buffer?
> 
> Is this OK from a semantic perspective? Particularly if the thread function
> calls getrandom, this is going to always trigger a syscall usage?
> 
> Is there a way to avoid this work? Why do we have to reset at each iteration
> of the loop? To avoid getrandom leakage across timer firings?
> 
> If this is the case we need a comment in the earlier reset loop in timer_helper_thread
> that explains this in more detail.

Indeed this is a real issue here, setting getrandom_buf = NULL orphans the 
per-thread vDSO buffer (mmap'd state never returned to the pool). On the next 
firing, getrandom() has to allocate a fresh buffer.

And since the getrandom internal state is opaque to POSIX definition for
SIGEV_THREAD (TLS dtors must run, __call_tls_dtors, TSD destructors, signal 
mask reset, cancel state reset, DTV cleared, etc.) I think we can just remove
the getrandom_buf reset (it is already done by __getrandom_vdso_release)

I will add a comment stating why it preserves the getrandom_buf.

> 
>> +
>> +  /* 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);
> 
> Review:
> 
> Should we reset timerid just avoid it leaking across threads?

It is not strickly required, timer_create_sigev_thread sets pthr->timerid (to the
new kernel id, or -1) before signaling the setup barrier, the helper thread only
reads it after the first sigwaitinfo wakeup. 

And external readers via timerid_to_pthread need a user-held timer_t which only 
exists post pthr->timerid write.

> 
>> +}
>> +
>>   /* Code to allocate and deallocate a stack.  */
>>   #include "allocatestack.c"
>>   @@ -644,6 +671,40 @@ 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 requires by POSIX on
> 
> Review:
> 
> Typo s/requires by POSIX on/required by POSIX in/g

Ack.

> 
>> +   Realtime Signal Generation and Delivery).  */
>> +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..4eee7421246
>> --- /dev/null
>> +++ b/rt/tst-timer6.c
>> @@ -0,0 +1,79 @@
>> +/* Check re-use timer id for SIGEV_THREAD (BZ 32833)
>> +   Copyright (C) 2025 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 ("time_delete: %m");
> 
> Review:
> 
> Typo s/time_delete/timer_delete/g

Ack.

> 
>> +    }
>> +
>> +  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..edfebfec1f9
>> --- /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) 2025 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..f1a18cdaf89 100644
>> --- a/sysdeps/nptl/pthreadP.h
>> +++ b/sysdeps/nptl/pthreadP.h
>> @@ -667,6 +667,18 @@ 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 internal thread state is if the start thread routine was initially
>> +   called from pthread_create.   It should be used along pthread_cleanup_push
>> +   and pthread_cleanup_pop pthread_reset_cleanup_args_t.  */
> 
> Review:
> 
> /* 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.  */
> 
> 

Ack.

>> +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..a1abef7ae69 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,33 @@ 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);
>> +    {
>> +      struct pthread *pthr = timerid_to_pthread (timerid);
>> +      return pthr->timerid & INT_MAX;
> 
> Review:
> 
> Should this be an atomic Relaxed MO read?
> 
> We are in timer_delete() and reading, while timer_helper_thread is also reading.
> 
> While timer_helper_thread shouldn't write to timerid, it is safest if this is atomic.

It should indeed, I will add the rrequired atomics.

> 
> 
>> +    }
>> +  return (uintptr_t) timerid;
>> +}
>> +
>> +static inline void
>> +timerid_signal_delete (kernel_timer_t *timerid)
>> +{
> 
> Review:
> 
> Add "/* Relaxed MO is sufficient because this is followed by a signal which is full memory barrier.  */"
> 
> Or this should be a Release MO.

Afaik, the relaxed MO is suffice.  The actual synchronization chain is:

Sender (timer_delete):
  atomic_fetch_or_relaxed (&th->timerid, INT_MIN);     [1]
  INTERNAL_SYSCALL_CALL (tgkill, ..., SIGTIMER);       [2]

Receiver (timer_helper_thread):
  __sigwaitinfo (&sigtimer_set, &si);                  [3]
  ... handle si ...
  atomic_load_relaxed (&self->timerid);                [4]

What it requires is  when [4] runs and sees the MSB set, the helper must exit. 
If it doesn't see the MSB, we lose the wakeup forever (sigwaitinfo has already
consumed the SIGTIMER).  So [1] must be globally visible before [3] returns,
and afaik the syscall provides it.

I will add the comment.

> 
>> +  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..3d80d63f269 100644
>> --- a/sysdeps/unix/sysv/linux/timer_create.c
>> +++ b/sysdeps/unix/sysv/linux/timer_create.c
>> @@ -15,46 +15,148 @@
>>      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;
> 
> OK. Copy the function, and sival value.
> 
>> +
>> +  __pthread_barrier_wait (&args->barrier);
> 
> OK. Required wait on timer_create.
> 
>> +  /* timer_create syscall failed.  */
>> +  if (self->exiting)
> 
> OK. Read of self->exiting without atomic.
> OK. No UB because of barrier wait.
> OK. Usually accessing exiting is done with exit_lock held.
> OK. self->exiting is safe to access after timer_delete() (valid self-synchronizing).
> 
>> +    return 0;
>> +
>> +  while (1)
>> +    {
>> +      siginfo_t si;
>> +      while (__sigwaitinfo (&sigtimer_set, &si) < 0);
>> +
>> +      if (si.si_code == SI_TIMER && !setjmp (clargs.jb))
>> +    {
>> +      pthread_cleanup_push (__pthread_reset_state, &clargs);
>> +      thrfunc (sival);
>> +      pthread_cleanup_pop (1);
> 
> Review:
> 
> We always call the thread state reset?
> 
> Why is that?

Yes, it is by design. I have added this comment to explain why:

          /* 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).  */

> 
>> +    }
>> +
>> +      /* timer_delete sets the MSB and sends SIGTIMER to wake this thread.  */
> 
> Review:
> 
> Add "Relaxed MO is sufficient because signaling this thread is a memory barrier."
> 
> or use Acquire MO to sequence-after the Relase MO store in timer_delete.

The relaxed MO is sufficient, as before. I will add the comment.

> 
>> +      if (atomic_load_relaxed (&self->timerid) < 0)
> 
> OK. Relaxed load of timerid from the helper thread. Ordering has to be enforced
> by another operation (signal syscall), and this is expected.
> 
>> +    break;
>> +    }
>> +
>> +  return NULL;
>> +}
>> +
> 
> Review:
> 
> Should describe that errno is set on error for timer_create_sigev_thread.

I added the comment for the function:

/* 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);
> 
> OK. Thread is created via timer_helper_thread.
> 
>> +  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 },
>> +    };
>> +
>> +  kernel_timer_t ktimerid;
>> +  if (INLINE_SYSCALL_CALL (timer_create, clockid, &kevp, &ktimerid) < 0)
>> +    {
>> +      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 avoid pthread_kill to send further signals.
> 
> Review:
> 
> Suggest s/to avoid pthread_kill to sned/to prevent pthread_kill from sending/g

Ack.

> 
> 
>> +     Since the thread should not be user-visible, signal are only sent
> 
> Review:
> 
> Typo s/signal/signals/g

Ack.

> 
>> +     during timer_delete.  */
>> +      pthr->exiting = true;
> 
> OK. Write happens and protected by barrier so no UB.
> 
>> +    }
>> +  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);
> 
> Review:
> 
> Earlier INLINE_SYSCALL_CALLL may set errno.
> 
> If INLINE_SYSCALL_CALL fails it sets errno, and we proceed to barrier wait.
> 
> The call to __pthread_barrier_wait *might* clobber errno, and so the returned
> errno might be wrong, and then timer_create returned errno might be wrong.
> Today in glibc I think it never clobbers, but a correct solution would be to
> save and restore errno.
> 
> This requires timer_create syscall to fail, and pthread_barrier_wait to fail,
> and us to have an implementation that sets errno.
> 
> Unlikely but possible?

It is possible indeed, I changed to INTERNAL_SYSCALL_CALL along with setting
the errno based on the syscall returned value.  It should be more robust
wrt the __pthread_barrier_wait call errno clobbering.

> 
>> +
>> +  if (ktimerid < 0)
>> +    return -1;
>> +
>> +  *timerid = pthread_to_timerid (th);
>> +
>> +  return 0;
>> +}
>> +
>>   int
>>   ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
> 
> OK. timer_create
> 
>>   {
>> -  {
>> -    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 +166,34 @@ ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
>>         *timerid = kernel_timer_to_timerid (ktimerid);
>>         }
>> -    else
>> +      break;
>> +    case SIGEV_THREAD:
> 
> OK. This is the SIGEV_THREAD case.
> 
>>         {
>> -    /* 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);
> 
> OK. Copy attributes.
> 
>> +        if (r != 0)
>> +          {
>> +        __set_errno (r);
>> +        return -1;
>> +          }
>>         }
>> +    else
>> +      __pthread_attr_init (&attr);
>> +    __pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
> 
> OK. 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);
> 
> OK. Uses atomics.
> 
>> +      /* 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);
> 
> OK. This is a memory barrier that the target thread will see the timerid update.
> 
>>         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);
>> -}
> 
> 



More information about the Libc-alpha mailing list