mirror of
git://git.musl-libc.org/musl
synced 2025-03-11 06:07:29 +00:00
add __timedwait backend workaround for old kernels where futex EINTRs
prior to linux 2.6.22, futex wait could fail with EINTR even for non-interrupting (SA_RESTART) signals. this was no problem provided the caller simply restarted the wait, but sem_[timed]wait is required by POSIX to return when interrupted by a signal. commita113434cd6
introduced this behavior, and commitc0ed5a201b
reverted it based on a mistaken belief that it was not required. this belief stems from a bug in the specification: the description requires the function to return when interrupted, but the errors section marks EINTR as a "may fail" condition rather than a "shall fail" one. since there does seem to be significant value in the change made in commitc0ed5a201b
, making it so that programs that call sem_wait without checking for EINTR don't silently make forward progress without obtaining the semaphore or treat it as a fatal error and abort, add a behind-the-scenes mechanism in the __timedwait backend to suppress EINTR in programs that have never installed interrupting signal handlers, and have sigaction track and report this state. this way the semaphore code is not cluttered by workarounds and can be updated (to be done in next commit) to reflect the high-level logic for conforming behavior. these changes are based loosely on a patch by Markus Wichmann, with the main changes being atomic update to flag object and moving the workaround from sem_timedwait to the __timedwait futex backend.
This commit is contained in:
parent
c00cdefa1d
commit
a63c0104e4
@ -156,6 +156,7 @@ extern hidden volatile int __block_new_threads;
|
||||
extern hidden volatile size_t __pthread_tsd_size;
|
||||
extern hidden void *__pthread_tsd_main[];
|
||||
extern hidden volatile int __aio_fut;
|
||||
extern hidden volatile int __eintr_valid_flag;
|
||||
|
||||
hidden int __clone(int (*)(void *), void *, int, void *, ...);
|
||||
hidden int __set_thread_area(void *);
|
||||
|
@ -21,6 +21,8 @@ void __get_handler_set(sigset_t *set)
|
||||
memcpy(set, handler_set, sizeof handler_set);
|
||||
}
|
||||
|
||||
volatile int __eintr_valid_flag;
|
||||
|
||||
int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
|
||||
{
|
||||
struct k_sigaction ksa, ksa_old;
|
||||
@ -43,6 +45,10 @@ int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigact
|
||||
SIGPT_SET, 0, _NSIG/8);
|
||||
unmask_done = 1;
|
||||
}
|
||||
|
||||
if (!(sa->sa_flags & SA_RESTART)) {
|
||||
a_store(&__eintr_valid_flag, 1);
|
||||
}
|
||||
}
|
||||
/* Changing the disposition of SIGABRT to anything but
|
||||
* SIG_DFL requires a lock, so that it cannot be changed
|
||||
|
@ -5,6 +5,9 @@
|
||||
#include "syscall.h"
|
||||
#include "pthread_impl.h"
|
||||
|
||||
static volatile int dummy = 0;
|
||||
weak_alias(dummy, __eintr_valid_flag);
|
||||
|
||||
int __timedwait_cp(volatile int *addr, int val,
|
||||
clockid_t clk, const struct timespec *at, int priv)
|
||||
{
|
||||
@ -28,6 +31,11 @@ int __timedwait_cp(volatile int *addr, int val,
|
||||
r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|priv, val, top);
|
||||
if (r == ENOSYS) r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top);
|
||||
if (r != EINTR && r != ETIMEDOUT && r != ECANCELED) r = 0;
|
||||
/* Mitigate bug in old kernels wrongly reporting EINTR for non-
|
||||
* interrupting (SA_RESTART) signal handlers. This is only practical
|
||||
* when NO interrupting signal handlers have been installed, and
|
||||
* works by sigaction tracking whether that's the case. */
|
||||
if (r == EINTR && !__eintr_valid_flag) r = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user