[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