mirror of
https://sourceware.org/git/glibc.git
synced 2025-01-23 00:03:42 +08:00
nptl: Implement raise in terms of pthread_kill
Now that pthread_kill is provided by libc.so it is possible to implement the generic POSIX implementation as 'pthread_kill(pthread_self(), sig)'. For Linux implementation, pthread_kill read the targeting TID from the TCB. For raise, this it not possible because it would make raise fail when issue after vfork (where creates the resulting process has a different TID from the parent, but its TCB is not updated as for pthread_create). To make raise use pthread_kill, it is make usable from vfork by getting the target thread id through gettid syscall. Checked on x86_64-linux-gnu and aarch64-linux-gnu.
This commit is contained in:
parent
8c1c0aae20
commit
f779b1efb3
@ -16,4 +16,9 @@ extern int __pthread_barrier_wait (pthread_barrier_t *__barrier)
|
||||
|
||||
/* This function is called to initialize the pthread library. */
|
||||
extern void __pthread_initialize (void) __attribute__ ((weak));
|
||||
|
||||
extern int __pthread_kill (pthread_t threadid, int signo);
|
||||
|
||||
extern pthread_t __pthread_self (void);
|
||||
|
||||
#endif
|
||||
|
@ -251,6 +251,7 @@ CFLAGS-pthread_clockjoin.c += -fexceptions -fasynchronous-unwind-tables
|
||||
CFLAGS-pthread_once.c += $(uses-callbacks) -fexceptions \
|
||||
-fasynchronous-unwind-tables
|
||||
CFLAGS-pthread_cond_wait.c += -fexceptions -fasynchronous-unwind-tables
|
||||
CFLAGS-pthread_kill.c = -fexceptions -fasynchronous-unwind-tables
|
||||
CFLAGS-sem_wait.c += -fexceptions -fasynchronous-unwind-tables
|
||||
CFLAGS-sem_timedwait.c += -fexceptions -fasynchronous-unwind-tables
|
||||
CFLAGS-sem_clockwait.c = -fexceptions -fasynchronous-unwind-tables
|
||||
|
@ -508,11 +508,13 @@ extern int __pthread_once (pthread_once_t *once_control,
|
||||
libc_hidden_proto (__pthread_once)
|
||||
extern int __pthread_atfork (void (*prepare) (void), void (*parent) (void),
|
||||
void (*child) (void));
|
||||
extern pthread_t __pthread_self (void);
|
||||
libc_hidden_proto (__pthread_self)
|
||||
extern int __pthread_equal (pthread_t thread1, pthread_t thread2);
|
||||
extern int __pthread_detach (pthread_t th);
|
||||
libc_hidden_proto (__pthread_detach)
|
||||
extern int __pthread_kill (pthread_t threadid, int signo);
|
||||
libc_hidden_proto (__pthread_kill)
|
||||
extern int __pthread_cancel (pthread_t th);
|
||||
extern void __pthread_exit (void *value) __attribute__ ((__noreturn__));
|
||||
libc_hidden_proto (__pthread_exit)
|
||||
extern int __pthread_join (pthread_t threadid, void **thread_return);
|
||||
|
@ -28,24 +28,40 @@ __pthread_kill (pthread_t threadid, int signo)
|
||||
if (__is_internal_signal (signo))
|
||||
return EINVAL;
|
||||
|
||||
/* Force load of pd->tid into local variable or register. Otherwise
|
||||
if a thread exits between ESRCH test and tgkill, we might return
|
||||
EINVAL, because pd->tid would be cleared by the kernel. */
|
||||
pid_t tid;
|
||||
struct pthread *pd = (struct pthread *) threadid;
|
||||
pid_t tid = atomic_forced_read (pd->tid);
|
||||
if (__glibc_unlikely (tid <= 0))
|
||||
/* Not a valid thread handle. */
|
||||
return ESRCH;
|
||||
|
||||
/* We have a special syscall to do the work. */
|
||||
pid_t pid = __getpid ();
|
||||
if (pd == THREAD_SELF)
|
||||
/* It is a special case to handle raise() implementation after a vfork
|
||||
call (which does not update the PD tid field). */
|
||||
tid = INLINE_SYSCALL_CALL (gettid);
|
||||
else
|
||||
/* Force load of pd->tid into local variable or register. Otherwise
|
||||
if a thread exits between ESRCH test and tgkill, we might return
|
||||
EINVAL, because pd->tid would be cleared by the kernel. */
|
||||
tid = atomic_forced_read (pd->tid);
|
||||
|
||||
int val = INTERNAL_SYSCALL_CALL (tgkill, pid, tid, signo);
|
||||
return (INTERNAL_SYSCALL_ERROR_P (val)
|
||||
? INTERNAL_SYSCALL_ERRNO (val) : 0);
|
||||
int val;
|
||||
if (__glibc_likely (tid > 0))
|
||||
{
|
||||
pid_t pid = __getpid ();
|
||||
|
||||
val = INTERNAL_SYSCALL_CALL (tgkill, pid, tid, signo);
|
||||
val = (INTERNAL_SYSCALL_ERROR_P (val)
|
||||
? INTERNAL_SYSCALL_ERRNO (val) : 0);
|
||||
}
|
||||
else
|
||||
val = ESRCH;
|
||||
|
||||
return val;
|
||||
}
|
||||
/* Some architectures (for instance arm) might pull raise through libgcc, so
|
||||
avoid the symbol version if it ends up being used on ld.so. */
|
||||
#if !IS_IN(rtld)
|
||||
libc_hidden_def (__pthread_kill)
|
||||
versioned_symbol (libc, __pthread_kill, pthread_kill, GLIBC_2_34);
|
||||
|
||||
#if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34)
|
||||
# if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34)
|
||||
compat_symbol (libc, __pthread_kill, pthread_kill, GLIBC_2_0);
|
||||
# endif
|
||||
#endif
|
||||
|
@ -20,7 +20,9 @@
|
||||
#include <tls.h>
|
||||
|
||||
pthread_t
|
||||
pthread_self (void)
|
||||
__pthread_self (void)
|
||||
{
|
||||
return (pthread_t) THREAD_SELF;
|
||||
}
|
||||
libc_hidden_def (__pthread_self)
|
||||
weak_alias (__pthread_self, pthread_self)
|
||||
|
@ -31,8 +31,6 @@ extern void __pthread_init_static_tls (struct link_map *) attribute_hidden;
|
||||
|
||||
/* These represent the interface used by glibc itself. */
|
||||
|
||||
extern pthread_t __pthread_self (void);
|
||||
extern int __pthread_kill (pthread_t threadid, int signo);
|
||||
extern struct __pthread **__pthread_threads;
|
||||
|
||||
extern int __pthread_mutex_init (pthread_mutex_t *__mutex, const pthread_mutexattr_t *__attr);
|
||||
|
@ -16,13 +16,20 @@
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* Raise the signal SIG. */
|
||||
int
|
||||
raise (int sig)
|
||||
{
|
||||
return __kill (__getpid (), sig);
|
||||
int ret = __pthread_kill (__pthread_self (), sig);
|
||||
if (ret != 0)
|
||||
{
|
||||
__set_errno (ret);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
libc_hidden_def (raise)
|
||||
weak_alias (raise, gsignal)
|
||||
|
@ -1,52 +0,0 @@
|
||||
/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
|
||||
|
||||
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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <signal.h>
|
||||
#include <sysdep.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <internal-signals.h>
|
||||
|
||||
int
|
||||
raise (int sig)
|
||||
{
|
||||
/* rt_sigprocmask may fail if:
|
||||
|
||||
1. sigsetsize != sizeof (sigset_t) (EINVAL)
|
||||
2. a failure in copy from/to user space (EFAULT)
|
||||
3. an invalid 'how' operation (EINVAL)
|
||||
|
||||
The first case is already handle in glibc syscall call by using the arch
|
||||
defined _NSIG. Second case is handled by using a stack allocated mask.
|
||||
The last one should be handled by the block/unblock functions. */
|
||||
|
||||
sigset_t set;
|
||||
__libc_signal_block_app (&set);
|
||||
|
||||
pid_t pid = INTERNAL_SYSCALL_CALL (getpid);
|
||||
pid_t tid = INTERNAL_SYSCALL_CALL (gettid);
|
||||
|
||||
int ret = INLINE_SYSCALL_CALL (tgkill, pid, tid, sig);
|
||||
|
||||
__libc_signal_restore_set (&set);
|
||||
|
||||
return ret;
|
||||
}
|
||||
libc_hidden_def (raise)
|
||||
weak_alias (raise, gsignal)
|
Loading…
Reference in New Issue
Block a user