From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1791) id B2CE53857C5A; Mon, 19 Jun 2023 17:13:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B2CE53857C5A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1687194801; bh=wtjooEP8gC1S//d1L0Cse10+UELiRkEg89O3+E29Owg=; h=From:To:Subject:Date:From; b=GxoItOVlSmh4MJtirWAdbV9W6UVfn99IPS7DZv+w9sLoPshUlRS6TaTZxYnBq5eiX F7cD7eVV1umufMEKJvBtdfGhjLnRphlr/Rkj+H4NV12D21PS46s5/NpKgL55LWXixb 4Shav/4Y1rW+4pOt4kKPHHbsudDnaUlPgawnTCko= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Adhemerval Zanella To: glibc-cvs@sourceware.org Subject: [glibc/azanella/bz30558-posix_timer] linux: Do not spawn a new thread for SIGEV_THREAD (BZ 30558) X-Act-Checkin: glibc X-Git-Author: Adhemerval Zanella X-Git-Refname: refs/heads/azanella/bz30558-posix_timer X-Git-Oldrev: 1d44530a5be2442e064baa48139adc9fdfb1fc6b X-Git-Newrev: 569976529bd5a1f338ba223c11b18ff39834b0e4 Message-Id: <20230619171321.B2CE53857C5A@sourceware.org> Date: Mon, 19 Jun 2023 17:13:21 +0000 (GMT) List-Id: https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=569976529bd5a1f338ba223c11b18ff39834b0e4 commit 569976529bd5a1f338ba223c11b18ff39834b0e4 Author: Adhemerval Zanella Date: Mon Jun 19 14:11:58 2023 -0300 linux: Do not spawn a new thread for SIGEV_THREAD (BZ 30558) Diff: --- nptl/descr.h | 3 + nptl/pthread_cancel.c | 26 +-- rt/Makefile | 1 + sysdeps/nptl/fork.h | 2 - sysdeps/nptl/pthreadP.h | 2 + sysdeps/pthread/Makefile | 1 + sysdeps/pthread/tst-cancel28.c | 20 ++- sysdeps/pthread/tst-cancel31.c | 86 +++++++++ sysdeps/unix/sysv/linux/internal-signals.h | 8 - sysdeps/unix/sysv/linux/kernel-posix-timers.h | 54 +----- sysdeps/unix/sysv/linux/timer_create.c | 239 +++++++++++++++----------- sysdeps/unix/sysv/linux/timer_delete.c | 40 +---- sysdeps/unix/sysv/linux/timer_routines.c | 155 +---------------- 13 files changed, 282 insertions(+), 355 deletions(-) diff --git a/nptl/descr.h b/nptl/descr.h index 746a4b9e4a..0874c2d64c 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -408,6 +408,9 @@ struct pthread /* rseq area registered with the kernel. */ struct rseq rseq_area; + /* POSIX per-process timer. */ + int timerid; + /* This member must be last. */ char end_padding[]; diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index 87c9ef69ad..5f7d997743 100644 --- a/nptl/pthread_cancel.c +++ b/nptl/pthread_cancel.c @@ -67,17 +67,9 @@ sigcancel_handler (int sig, siginfo_t *si, void *ctx) } } -int -__pthread_cancel (pthread_t th) +void +__pthread_cancel_install_handler (void) { - volatile struct pthread *pd = (volatile struct pthread *) th; - - if (pd->tid == 0) - /* The thread has already exited on the kernel side. Its outcome - (regular exit, other cancelation) has already been - determined. */ - return 0; - static int init_sigcancel = 0; if (atomic_load_relaxed (&init_sigcancel) == 0) { @@ -92,6 +84,20 @@ __pthread_cancel (pthread_t th) __libc_sigaction (SIGCANCEL, &sa, NULL); atomic_store_relaxed (&init_sigcancel, 1); } +} + +int +__pthread_cancel (pthread_t th) +{ + volatile struct pthread *pd = (volatile struct pthread *) th; + + if (pd->tid == 0) + /* The thread has already exited on the kernel side. Its outcome + (regular exit, other cancelation) has already been + determined. */ + return 0; + + __pthread_cancel_install_handler (); #ifdef SHARED /* Trigger an error if libgcc_s cannot be loaded. */ diff --git a/rt/Makefile b/rt/Makefile index a97333dc02..0c93a7f79d 100644 --- a/rt/Makefile +++ b/rt/Makefile @@ -99,6 +99,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 LDFLAGS-rt.so = -Wl,--enable-new-dtags,-z,nodelete diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h index ee7967416c..40ed5cf7e7 100644 --- a/sysdeps/nptl/fork.h +++ b/sysdeps/nptl/fork.h @@ -20,7 +20,6 @@ #define _FORK_H #include -#include #include #include #include @@ -45,7 +44,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); } /* In case of a fork() call the memory allocation in the child will be diff --git a/sysdeps/nptl/pthreadP.h b/sysdeps/nptl/pthreadP.h index 54f9198681..df5666c61d 100644 --- a/sysdeps/nptl/pthreadP.h +++ b/sysdeps/nptl/pthreadP.h @@ -663,6 +663,8 @@ extern void __wait_lookup_done (void) attribute_hidden; int __pthread_attr_extension (struct pthread_attr *attr) attribute_hidden __attribute_warn_unused_result__; +void __pthread_cancel_install_handler (void) attribute_hidden; + #ifdef SHARED # define PTHREAD_STATIC_FN_REQUIRE(name) #else diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 5df1109dd3..2eb53adfca 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -106,6 +106,7 @@ tests += \ tst-cancel28 \ tst-cancel29 \ tst-cancel30 \ + tst-cancel31 \ tst-cleanup0 \ tst-cleanup1 \ tst-cleanup2 \ diff --git a/sysdeps/pthread/tst-cancel28.c b/sysdeps/pthread/tst-cancel28.c index ae55f38210..94577def10 100644 --- a/sysdeps/pthread/tst-cancel28.c +++ b/sysdeps/pthread/tst-cancel28.c @@ -29,11 +29,13 @@ static pthread_barrier_t barrier; static pthread_t timer_thread; +static bool cancelled = false; static void cl (void *arg) { - xpthread_barrier_wait (&barrier); + cancelled = true; + xpthread_barrier_wait (&barrier); /* 2 */ } static void @@ -41,7 +43,9 @@ thread_handler (union sigval sv) { timer_thread = pthread_self (); - xpthread_barrier_wait (&barrier); + xpthread_barrier_wait (&barrier); /* 0 */ + + xpthread_barrier_wait (&barrier); /* 1 */ pthread_cleanup_push (cl, NULL); while (1) @@ -65,11 +69,19 @@ do_test (void) trigger.it_value.tv_nsec = 1000000; TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0); - xpthread_barrier_wait (&barrier); + /* Sync to get the helper thread id. */ + xpthread_barrier_wait (&barrier); /* 0 */ xpthread_cancel (timer_thread); - xpthread_barrier_wait (&barrier); + /* Sync to get the pending cancellation to act on next cancellation + entrypoint (clock_nanosleep). */ + xpthread_barrier_wait (&barrier); /* 1 */ + + /* Sync with the cancellation cleanup handler. */ + xpthread_barrier_wait (&barrier); /* 2 */ + + TEST_COMPARE (cancelled, true); return 0; } diff --git a/sysdeps/pthread/tst-cancel31.c b/sysdeps/pthread/tst-cancel31.c new file mode 100644 index 0000000000..26d917c314 --- /dev/null +++ b/sysdeps/pthread/tst-cancel31.c @@ -0,0 +1,86 @@ +/* Check if the thread created by POSIX timer using SIGEV_THREAD is + asynchronously cancellable. + Copyright (C) 2020-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static pthread_barrier_t barrier; +static pthread_t timer_thread; +static bool cancelled = false; + +static void +cl (void *arg) +{ + cancelled = true; + xpthread_barrier_wait (&barrier); /* 1 */ +} + +static void +thread_handler (union sigval sv) +{ + timer_thread = pthread_self (); + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + xpthread_barrier_wait (&barrier); /* 0 */ + + pthread_cleanup_push (cl, NULL); + for (;;); + pthread_cleanup_pop (0); +} + +static int +do_test (void) +{ + struct sigevent sev = { 0 }; + sev.sigev_notify = SIGEV_THREAD; + sev.sigev_notify_function = &thread_handler; + + timer_t timerid; + TEST_COMPARE (timer_create (CLOCK_REALTIME, &sev, &timerid), 0); + + xpthread_barrier_init (&barrier, NULL, 2); + + struct itimerspec trigger = { 0 }; + trigger.it_value.tv_nsec = 1000000; + TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0); + + /* Sync to get the helper thread id. */ + xpthread_barrier_wait (&barrier); /* 0 */ + + xpthread_cancel (timer_thread); + + /* Sync with the cancellation cleanup handler. */ + xpthread_barrier_wait (&barrier); /* 1 */ + + TEST_COMPARE (cancelled, true); + + return 0; +} + +/* A stall in cancellation is a regression. */ +#include diff --git a/sysdeps/unix/sysv/linux/internal-signals.h b/sysdeps/unix/sysv/linux/internal-signals.h index 43c3c0b4a0..e83b48a58e 100644 --- a/sysdeps/unix/sysv/linux/internal-signals.h +++ b/sysdeps/unix/sysv/linux/internal-signals.h @@ -99,12 +99,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-timers.h b/sysdeps/unix/sysv/linux/kernel-posix-timers.h index 9d86bb1dab..cd6bf89acd 100644 --- a/sysdeps/unix/sysv/linux/kernel-posix-timers.h +++ b/sysdeps/unix/sysv/linux/kernel-posix-timers.h @@ -19,29 +19,7 @@ #include #include #include - - -/* 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 extern __typeof (timer_create) __timer_create; libc_hidden_proto (__timer_create) @@ -53,25 +31,9 @@ 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 is always 8-byte aligned. */ static inline timer_t kernel_timer_to_timerid (kernel_timer_t ktimerid) @@ -80,7 +42,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,17 +53,17 @@ 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; + return timerid_to_pthread (timerid)->timerid; else return (kernel_timer_t) ((uintptr_t) timerid); } diff --git a/sysdeps/unix/sysv/linux/timer_create.c b/sysdeps/unix/sysv/linux/timer_create.c index b7e84e2bb1..b4664e9b3e 100644 --- a/sysdeps/unix/sysv/linux/timer_create.c +++ b/sysdeps/unix/sysv/linux/timer_create.c @@ -15,46 +15,147 @@ License along with the GNU C Library; see the file COPYING.LIB. If not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "kernel-posix-timers.h" #include "kernel-posix-cpu-timers.h" +#include #include +struct timer_helper_thread_args_t +{ + /* The barrier is used to synchronize the arguments copy from timer_created + to the timer thread. */ + pthread_barrier_t b; + struct sigevent *evp; +}; + +static void +timer_helper_thread_cleanup (void *arg) +{ + struct pthread *self = THREAD_SELF; + /* Clear the MSB bit set by timer_delete. */ + INTERNAL_SYSCALL_CALL (timer_delete, self->timerid & INT_MAX); + + kernel_timer_t timerid = atomic_load_relaxed (&self->timerid); + atomic_store_relaxed (&self->timerid, timerid | INT_MIN); +} + +static void * +timer_helper_thread (void *arg) +{ + struct timer_helper_thread_args_t *args = arg; + + void (*thrfunc) (sigval_t) = args->evp->sigev_notify_function; + sigval_t sival = args->evp->sigev_value; + __pthread_barrier_wait (&args->b); + + struct pthread *self = THREAD_SELF; + + while (1) + { + siginfo_t si; + while (__sigwaitinfo (&sigtimer_set, &si) < 0) {}; + + if (si.si_code == SI_TIMER) + { + __libc_cleanup_region_start (1, timer_helper_thread_cleanup, NULL); + thrfunc (sival); + __libc_cleanup_region_end (0); + } + + /* timer_delete will set the MSB and signal the thread. */ + if (atomic_load_relaxed (&self->timerid) < 0) + break; + } + + timer_helper_thread_cleanup (NULL); + + return NULL; +} + +static int +timer_create_sigev_thread (clockid_t syscall_clockid, struct sigevent *evp, + timer_t *timerid) +{ + int ret = -1; + + __pthread_cancel_install_handler (); + + pthread_attr_t attr; + if (evp->sigev_notify_attributes != NULL) + __pthread_attr_copy (&attr, evp->sigev_notify_attributes); + else + __pthread_attr_init (&attr); + __pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + /* Block all signals in the helper thread but SIGSETXID and SIGCANCEL. */ + sigset_t ss; + __sigfillset (&ss); + clear_internal_signals (&ss); + ret = __pthread_attr_setsigmask_internal (&attr, &ss); + if (ret != 0) + goto out; + + struct timer_helper_thread_args_t args; + __pthread_barrier_init (&args.b, 0, 2); + args.evp = evp; + + pthread_t th; + ret = __pthread_create (&th, &attr, timer_helper_thread, &args); + if (ret != 0) + { + __set_errno (ret); + goto out; + } + + struct sigevent kevp = { + .sigev_value.sival_ptr = NULL, + .sigev_signo = SIGTIMER, + .sigev_notify = SIGEV_THREAD_ID, + ._sigev_un = { ._pad = { [0] = ((struct pthread *)th)->tid } } + }; + + kernel_timer_t ktimerid; + if (INLINE_SYSCALL_CALL (timer_create, syscall_clockid, &kevp, &ktimerid) < 0) + goto out; + ((struct pthread *)th)->timerid = ktimerid; + + __pthread_barrier_wait (&args.b); + + if (timerid >= 0) + *timerid = pthread_to_timerid (th); + + ret = 0; + +out: + if (&attr != evp->sigev_notify_attributes) + __pthread_attr_destroy (&attr); + + return ret; +} + int ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid) { - { - clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID - ? PROCESS_CLOCK - : clock_id == CLOCK_THREAD_CPUTIME_ID - ? THREAD_CLOCK - : clock_id); - - /* 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)) - { - struct sigevent local_evp; + clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID + ? PROCESS_CLOCK + : clock_id == CLOCK_THREAD_CPUTIME_ID + ? THREAD_CLOCK + : clock_id); + switch (evp != NULL ? evp->sigev_notify : SIGEV_SIGNAL) + { + case SIGEV_NONE: + case SIGEV_SIGNAL: + case SIGEV_THREAD_ID: + { + 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 +165,15 @@ ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid) *timerid = kernel_timer_to_timerid (ktimerid); } - else - { - /* 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); - 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; - } - - /* In any case set the detach flag. */ - __pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED); - - /* 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 } } }; - - /* 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); - } - } + break; + case SIGEV_THREAD: + if (timer_create_sigev_thread (syscall_clockid, evp, timerid) < 0) + return -1; + break; + 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 a611d5013a..ce0c1a777b 100644 --- a/sysdeps/unix/sysv/linux/timer_delete.c +++ b/sysdeps/unix/sysv/linux/timer_delete.c @@ -26,42 +26,18 @@ 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); - - /* 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); - } + struct pthread *th = timerid_to_pthread (timerid); + /* The helper thread itself will be responsible to call the + timer_delete syscall. */ + kernel_timer_t timerid = atomic_load_relaxed (&th->timerid); + atomic_store_relaxed (&th->timerid, timerid | INT_MIN); + __pthread_kill_internal ((pthread_t) th, SIGTIMER); return 0; } - - /* The kernel timer is not known or something else bad happened. - Return the error. */ - return -1; + return INLINE_SYSCALL_CALL (timer_delete, timerid); } versioned_symbol (libc, ___timer_delete, timer_delete, GLIBC_2_34); libc_hidden_ver (___timer_delete, __timer_delete) diff --git a/sysdeps/unix/sysv/linux/timer_routines.c b/sysdeps/unix/sysv/linux/timer_routines.c index 2ad1e7baf2..6e25b021ab 100644 --- a/sysdeps/unix/sysv/linux/timer_routines.c +++ b/sysdeps/unix/sysv/linux/timer_routines.c @@ -1,154 +1 @@ -/* Copyright (C) 2003-2023 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 . */ - -#include -#include -#include -#include -#include -#include -#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); -} +/* Empty. */