1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-08-07 06:43:00 +03:00

nptl: Replace non cancellable pause/nanosleep with futex

To help y2038 work avoid duplicate all the logic of nanosleep on
non cancellable version, the patch replace it with a new futex
operation, lll_timedwait.  The changes are:

  - Add a expected value for __lll_clocklock_wait, so it can be used
    to wait for generic values.

  - Remove its internal atomic operation and move the logic to
    __lll_clocklock.  It makes __lll_clocklock_wait even more generic
    and __lll_clocklock slight faster on fast-path (since it won't
    require a function call anymore).

  - Add lll_timedwait, which uses __lll_clocklock_wait, to replace both
    __pause_nocancel and __nanosleep_nocancel.

It also allows remove the sparc32 __lll_clocklock_wait implementation
(since it is similar to the generic one).

Checked on x86_64-linux-gnu, sparcv9-linux-gnu, and i686-linux-gnu.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
Adhemerval Zanella
2019-10-30 15:56:39 -03:00
parent b580327434
commit 215078017f
7 changed files with 56 additions and 82 deletions

View File

@@ -25,39 +25,38 @@
int int
__lll_clocklock_wait (int *futex, clockid_t clockid, __lll_clocklock_wait (int *futex, int val, clockid_t clockid,
const struct timespec *abstime, int private) const struct timespec *abstime, int private)
{ {
/* Reject invalid timeouts. */ struct timespec ts, *tsp = NULL;
if (! valid_nanoseconds (abstime->tv_nsec))
return EINVAL;
/* Try locking. */ if (abstime != NULL)
while (atomic_exchange_acq (futex, 2) != 0)
{ {
struct timespec ts; /* Reject invalid timeouts. */
if (! valid_nanoseconds (abstime->tv_nsec))
return EINVAL;
/* Get the current time. This can only fail if clockid is not /* Get the current time. This can only fail if clockid is not valid. */
valid. */
if (__glibc_unlikely (__clock_gettime (clockid, &ts) != 0)) if (__glibc_unlikely (__clock_gettime (clockid, &ts) != 0))
return EINVAL; return EINVAL;
/* Compute relative timeout. */ /* Compute relative timeout. */
struct timespec rt; ts.tv_sec = abstime->tv_sec - ts.tv_sec;
rt.tv_sec = abstime->tv_sec - ts.tv_sec; ts.tv_nsec = abstime->tv_nsec - ts.tv_nsec;
rt.tv_nsec = abstime->tv_nsec - ts.tv_nsec; if (ts.tv_nsec < 0)
if (rt.tv_nsec < 0)
{ {
rt.tv_nsec += 1000000000; ts.tv_nsec += 1000000000;
--rt.tv_sec; --ts.tv_sec;
} }
if (rt.tv_sec < 0) if (ts.tv_sec < 0)
return ETIMEDOUT; return ETIMEDOUT;
/* If *futex == 2, wait until woken or timeout. */ tsp = &ts;
lll_futex_timed_wait (futex, 2, &rt, private);
} }
/* If *futex == val, wait until woken or timeout. */
lll_futex_timed_wait (futex, val, tsp, private);
return 0; return 0;
} }

View File

@@ -434,7 +434,8 @@ __pthread_mutex_lock_full (pthread_mutex_t *mutex)
/* Delay the thread indefinitely. */ /* Delay the thread indefinitely. */
while (1) while (1)
__pause_nocancel (); lll_timedwait (&(int){0}, 0, 0 /* ignored */, NULL,
private);
} }
oldval = mutex->__data.__lock; oldval = mutex->__data.__lock;

View File

@@ -401,22 +401,10 @@ __pthread_mutex_clocklock_common (pthread_mutex_t *mutex,
/* Delay the thread until the timeout is reached. /* Delay the thread until the timeout is reached.
Then return ETIMEDOUT. */ Then return ETIMEDOUT. */
struct timespec reltime; do
struct timespec now; e = lll_timedwait (&(int){0}, 0, clockid, abstime,
private);
INTERNAL_SYSCALL (clock_gettime, __err, 2, clockid, while (e != ETIMEDOUT);
&now);
reltime.tv_sec = abstime->tv_sec - now.tv_sec;
reltime.tv_nsec = abstime->tv_nsec - now.tv_nsec;
if (reltime.tv_nsec < 0)
{
reltime.tv_nsec += 1000000000;
--reltime.tv_sec;
}
if (reltime.tv_sec >= 0)
while (__nanosleep_nocancel (&reltime, &reltime) != 0)
continue;
return ETIMEDOUT; return ETIMEDOUT;
} }

View File

@@ -21,6 +21,7 @@
#include <atomic.h> #include <atomic.h>
#include <lowlevellock-futex.h> #include <lowlevellock-futex.h>
#include <time.h>
/* Low-level locks use a combination of atomic operations (to acquire and /* Low-level locks use a combination of atomic operations (to acquire and
release lock ownership) and futex operations (to block until the state release lock ownership) and futex operations (to block until the state
@@ -121,10 +122,12 @@ extern void __lll_lock_wait (int *futex, int private) attribute_hidden;
#define lll_cond_lock(futex, private) __lll_cond_lock (&(futex), private) #define lll_cond_lock(futex, private) __lll_cond_lock (&(futex), private)
extern int __lll_clocklock_wait (int *futex, clockid_t, extern int __lll_clocklock_wait (int *futex, int val, clockid_t,
const struct timespec *, const struct timespec *,
int private) attribute_hidden; int private) attribute_hidden;
#define lll_timedwait(futex, val, clockid, abstime, private) \
__lll_clocklock_wait (futex, val, clockid, abstime, private)
/* As __lll_lock, but with an absolute timeout measured against the clock /* As __lll_lock, but with an absolute timeout measured against the clock
specified in CLOCKID. If the timeout occurs then return ETIMEDOUT. If specified in CLOCKID. If the timeout occurs then return ETIMEDOUT. If
@@ -136,7 +139,15 @@ extern int __lll_clocklock_wait (int *futex, clockid_t,
\ \
if (__glibc_unlikely \ if (__glibc_unlikely \
(atomic_compare_and_exchange_bool_acq (__futex, 1, 0))) \ (atomic_compare_and_exchange_bool_acq (__futex, 1, 0))) \
__val = __lll_clocklock_wait (__futex, clockid, abstime, private); \ { \
while (atomic_exchange_acq (futex, 2) != 0) \
{ \
__val = __lll_clocklock_wait (__futex, 2, clockid, \
abstime, private); \
if (__val == EINVAL || __val == ETIMEDOUT) \
break; \
} \
} \
__val; \ __val; \
}) })
#define lll_clocklock(futex, clockid, abstime, private) \ #define lll_clocklock(futex, clockid, abstime, private) \

View File

@@ -1 +0,0 @@
/* __lll_clocklock_wait is in lowlevellock.c. */

View File

@@ -50,46 +50,4 @@ __lll_lock_wait (int *futex, int private)
} }
while (atomic_compare_and_exchange_val_24_acq (futex, 2, 0) != 0); while (atomic_compare_and_exchange_val_24_acq (futex, 2, 0) != 0);
} }
int
__lll_clocklock_wait (int *futex, clockid_t clockid,
const struct timespec *abstime, int private)
{
/* Reject invalid timeouts. */
if (! valid_nanoseconds (abstime->tv_nsec))
return EINVAL;
do
{
struct timespec ts;
struct timespec rt;
/* Get the current time. This can only fail if clockid is not
valid. */
if (__glibc_unlikely (__clock_gettime (clockid, &ts) != 0))
return EINVAL;
/* Compute relative timeout. */
rt.tv_sec = abstime->tv_sec - ts.tv_sec;
rt.tv_nsec = abstime->tv_nsec - ts.tv_nsec;
if (rt.tv_nsec < 0)
{
rt.tv_nsec += 1000000000;
--rt.tv_sec;
}
/* Already timed out? */
if (rt.tv_sec < 0)
return ETIMEDOUT;
/* Wait. */
int oldval = atomic_compare_and_exchange_val_24_acq (futex, 2, 1);
if (oldval != 0)
lll_futex_timed_wait (futex, 2, &rt, private);
}
while (atomic_compare_and_exchange_val_24_acq (futex, 2, 0) != 0);
return 0;
}
#endif #endif

View File

@@ -24,6 +24,7 @@
#include <bits/pthreadtypes.h> #include <bits/pthreadtypes.h>
#include <atomic.h> #include <atomic.h>
#include <kernel-features.h> #include <kernel-features.h>
#include <errno.h>
#include <lowlevellock-futex.h> #include <lowlevellock-futex.h>
@@ -75,9 +76,13 @@ __lll_cond_lock (int *futex, int private)
#define lll_cond_lock(futex, private) __lll_cond_lock (&(futex), private) #define lll_cond_lock(futex, private) __lll_cond_lock (&(futex), private)
extern int __lll_clocklock_wait (int *futex, clockid_t, const struct timespec *, extern int __lll_clocklock_wait (int *futex, clockid_t, int val,
const struct timespec *,
int private) attribute_hidden; int private) attribute_hidden;
#define lll_timedwait(futex, val, clockid, abstime, private) \
__lll_clocklock_wait (futex, val, clockid, abstime, private)
static inline int static inline int
__attribute__ ((always_inline)) __attribute__ ((always_inline))
__lll_clocklock (int *futex, clockid_t clockid, __lll_clocklock (int *futex, clockid_t clockid,
@@ -87,7 +92,20 @@ __lll_clocklock (int *futex, clockid_t clockid,
int result = 0; int result = 0;
if (__glibc_unlikely (val != 0)) if (__glibc_unlikely (val != 0))
result = __lll_clocklock_wait (futex, clockid, abstime, private); {
do
{
int oldval = atomic_compare_and_exchange_val_24_acq (futex, val, 1);
if (oldval != 0)
{
result = __lll_clocklock_wait (futex, 2, clockid, abstime,
private);
if (result == EINVAL || result == ETIMEDOUT)
break;
}
}
while (atomic_compare_and_exchange_val_24_acq (futex, val, 0) != 0);
}
return result; return result;
} }
#define lll_clocklock(futex, clockid, abstime, private) \ #define lll_clocklock(futex, clockid, abstime, private) \