mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-04 19:00:09 +00:00
linux: Do not spawn a new thread for SIGEV_THREAD (BZ 30558)
This commit is contained in:
parent
99f9ae4ed0
commit
76d0104ced
@ -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[];
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -29,29 +29,43 @@
|
||||
|
||||
static pthread_barrier_t barrier;
|
||||
|
||||
static void
|
||||
__attribute_used__
|
||||
cl (void *arg)
|
||||
{
|
||||
xpthread_barrier_wait (&barrier);
|
||||
}
|
||||
|
||||
static void
|
||||
thread_handler (union sigval sv)
|
||||
{
|
||||
sigset_t ss;
|
||||
sigprocmask (SIG_BLOCK, NULL, &ss);
|
||||
if (test_verbose > 0)
|
||||
printf ("%s: blocked signal mask = { ", __func__);
|
||||
for (int sig = 1; sig < NSIG; sig++)
|
||||
{
|
||||
/* POSIX timers threads created to handle SIGEV_THREAD block all
|
||||
signals except SIGKILL, SIGSTOP and glibc internals ones. */
|
||||
if (sigismember (&ss, sig))
|
||||
{
|
||||
TEST_VERIFY (sig != SIGKILL && sig != SIGSTOP);
|
||||
TEST_VERIFY (!is_internal_signal (sig));
|
||||
}
|
||||
if (test_verbose && sigismember (&ss, sig))
|
||||
printf ("%d, ", sig);
|
||||
}
|
||||
if (test_verbose > 0)
|
||||
printf ("}\n");
|
||||
/* POSIX timers threads created to handle SIGEV_THREAD block all
|
||||
signals except SIGKILL, SIGSTOP, and SIGSETXID. */
|
||||
sigset_t expected_set = {
|
||||
.__val = {[0 ... __NSIG_WORDS-1 ] = -1 }
|
||||
};
|
||||
__sigdelset (&expected_set, SIGSETXID);
|
||||
__sigdelset (&expected_set, SIGKILL);
|
||||
__sigdelset (&expected_set, SIGSTOP);
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
sigset_t ss;
|
||||
sigprocmask (SIG_SETMASK, NULL, &ss);
|
||||
|
||||
TEST_COMPARE_BLOB (&ss.__val, __NSIG_BYTES, &expected_set, __NSIG_BYTES);
|
||||
|
||||
/* Unblock some signals, the signal mask should be reset by the timer_create
|
||||
helper thread. */
|
||||
{
|
||||
sigset_t toset;
|
||||
sigemptyset (&toset);
|
||||
sigaddset (&toset, SIGHUP);
|
||||
sigaddset (&toset, SIGILL);
|
||||
sigprocmask (SIG_UNBLOCK, &toset, NULL);
|
||||
}
|
||||
|
||||
pthread_cleanup_push (cl, NULL);
|
||||
pthread_exit (NULL);
|
||||
pthread_cleanup_pop (0);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -66,11 +80,12 @@ do_test (void)
|
||||
|
||||
xpthread_barrier_init (&barrier, NULL, 2);
|
||||
|
||||
struct itimerspec trigger = { 0 };
|
||||
trigger.it_value.tv_nsec = 1000000;
|
||||
struct itimerspec trigger = { { 0, 1000000 }, { 0, 1000000 } };
|
||||
TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0);
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
enum { TIMERS_TO_CHECK = 2 };
|
||||
for (int i = 0; i < TIMERS_TO_CHECK; i++)
|
||||
xpthread_barrier_wait (&barrier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@
|
||||
#define _FORK_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <kernel-posix-timers.h>
|
||||
#include <ldsodefs.h>
|
||||
#include <list.h>
|
||||
#include <mqueue.h>
|
||||
@ -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
|
||||
|
@ -28,48 +28,64 @@
|
||||
#include <support/xthread.h>
|
||||
|
||||
static pthread_barrier_t barrier;
|
||||
static pthread_t timer_thread;
|
||||
|
||||
#define VALUE1_INITIAL 0
|
||||
static __thread unsigned int value1 = VALUE1_INITIAL;
|
||||
#define VALUE2_INITIAL 0xdeadbeef
|
||||
static __thread unsigned int value2 = VALUE2_INITIAL;
|
||||
|
||||
static int cl_called = 0;
|
||||
|
||||
static void
|
||||
cl (void *arg)
|
||||
{
|
||||
xpthread_barrier_wait (&barrier);
|
||||
cl_called++;
|
||||
xpthread_barrier_wait (&barrier); /* 1 */
|
||||
}
|
||||
|
||||
static void
|
||||
thread_handler (union sigval sv)
|
||||
{
|
||||
timer_thread = pthread_self ();
|
||||
/* Check if thread state is correctly reset after a pthread_exit. */
|
||||
TEST_COMPARE (value1, VALUE1_INITIAL);
|
||||
TEST_COMPARE (value2, VALUE2_INITIAL);
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
value1 = VALUE2_INITIAL;
|
||||
value2 = VALUE1_INITIAL;
|
||||
|
||||
xpthread_barrier_wait (&barrier); /* 0 */
|
||||
|
||||
pthread_cleanup_push (cl, NULL);
|
||||
while (1)
|
||||
clock_nanosleep (CLOCK_REALTIME, 0, &(struct timespec) { 1, 0 }, NULL);
|
||||
pthread_exit (NULL);
|
||||
pthread_cleanup_pop (0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
struct sigevent sev = { 0 };
|
||||
sev.sigev_notify = SIGEV_THREAD;
|
||||
sev.sigev_notify_function = &thread_handler;
|
||||
struct sigevent sev = {
|
||||
.sigev_notify = SIGEV_THREAD,
|
||||
.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;
|
||||
struct itimerspec trigger = { { 0, 1000000 }, { 0, 1000000 } };
|
||||
TEST_COMPARE (timer_settime (timerid, 0, &trigger, NULL), 0);
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
enum { TIMERS_TO_CHECK = 4 };
|
||||
for (int i = 0; i < TIMERS_TO_CHECK; i++)
|
||||
{
|
||||
/* Check the thread local values. */
|
||||
xpthread_barrier_wait (&barrier); /* 0 */
|
||||
|
||||
xpthread_cancel (timer_thread);
|
||||
xpthread_barrier_wait (&barrier); /* 1 */
|
||||
}
|
||||
|
||||
xpthread_barrier_wait (&barrier);
|
||||
TEST_COMPARE (cl_called, TIMERS_TO_CHECK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -8,6 +8,8 @@
|
||||
- A clockid is invalid if bits 2, 1, and 0 are all set.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3))
|
||||
#define CPUCLOCK_PERTHREAD(clock) \
|
||||
(((clock) & (clockid_t) CPUCLOCK_PERTHREAD_MASK) != 0)
|
||||
|
@ -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,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);
|
||||
}
|
||||
|
@ -15,46 +15,198 @@
|
||||
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_created
|
||||
to the timer thread. */
|
||||
pthread_barrier_t b;
|
||||
struct sigevent *evp;
|
||||
};
|
||||
|
||||
extern _Noreturn void __longjmp_cancel (__jmp_buf __env, int __val)
|
||||
attribute_hidden;
|
||||
|
||||
struct cleanup_args_t
|
||||
{
|
||||
struct pthread_unwind_buf *cleanup_jmp_buf;
|
||||
jmp_buf jb;
|
||||
};
|
||||
|
||||
static void
|
||||
timer_helper_thread_cleanup (void *arg)
|
||||
{
|
||||
struct pthread *self = THREAD_SELF;
|
||||
|
||||
/* Call destructors for the thread_local TLS variables. */
|
||||
#ifndef SHARED
|
||||
if (&__call_tls_dtors != NULL)
|
||||
#endif
|
||||
__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 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;
|
||||
|
||||
/* Re-initialize the TLS. */
|
||||
_dl_allocate_tls_init (TLS_TPADJ (self), true);
|
||||
|
||||
internal_sigset_t ss;
|
||||
internal_sigfillset (&ss);
|
||||
internal_sigdelset (&ss, SIGSETXID);
|
||||
internal_sigprocmask (SIG_SETMASK, &ss, NULL);
|
||||
|
||||
/* There is no need to perform any additional cleanup by the frames. */
|
||||
struct __jmp_buf_tag *env = args->jb;
|
||||
__longjmp (env[0].__jmpbuf, 1);
|
||||
}
|
||||
|
||||
static void *
|
||||
timer_helper_thread (void *arg)
|
||||
{
|
||||
struct pthread *self = THREAD_SELF;
|
||||
|
||||
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);
|
||||
|
||||
/* timer_create failed. */
|
||||
if (self->timerid < 0)
|
||||
return 0;
|
||||
|
||||
struct cleanup_args_t clargs = {
|
||||
.cleanup_jmp_buf = self->cleanup_jmp_buf
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
siginfo_t si;
|
||||
while (__sigwaitinfo (&sigtimer_set, &si) < 0) {};
|
||||
|
||||
if (si.si_code == SI_TIMER && !setjmp (clargs.jb))
|
||||
{
|
||||
pthread_cleanup_push (timer_helper_thread_cleanup, &clargs);
|
||||
thrfunc (sival);
|
||||
pthread_cleanup_pop (0);
|
||||
}
|
||||
|
||||
/* timer_delete will set the MSB and signal the thread. */
|
||||
if (atomic_load_relaxed (&self->timerid) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the MSB bit set by timer_delete. */
|
||||
INTERNAL_SYSCALL_CALL (timer_delete, self->timerid & INT_MAX);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
timer_create_sigev_thread (clockid_t syscall_clockid, struct sigevent *evp,
|
||||
timer_t *timerid)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
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. */
|
||||
sigset_t ss;
|
||||
__sigfillset (&ss);
|
||||
__sigdelset (&ss, SIGSETXID);
|
||||
ret = __pthread_attr_setsigmask_internal (&attr, &ss);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
struct timer_helper_thread_args_t args;
|
||||
__pthread_barrier_init (&args.b, NULL, 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 } }
|
||||
};
|
||||
|
||||
struct pthread *pthr = (struct pthread *)th;
|
||||
|
||||
kernel_timer_t ktimerid;
|
||||
if (INLINE_SYSCALL_CALL (timer_create, syscall_clockid, &kevp, &ktimerid) < 0)
|
||||
pthr->timerid = -1;
|
||||
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.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);
|
||||
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 +216,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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 <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);
|
||||
}
|
||||
/* Empty. */
|
||||
|
Loading…
Reference in New Issue
Block a user