aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps')
-rw-r--r--sysdeps/nacl/exit-thread.h4
-rw-r--r--sysdeps/nacl/futex-internal.h248
-rw-r--r--sysdeps/nptl/aio_misc.h16
-rw-r--r--sysdeps/nptl/fork.c3
-rw-r--r--sysdeps/nptl/futex-internal.h203
-rw-r--r--sysdeps/nptl/gai_misc.h16
-rw-r--r--sysdeps/sparc/nptl/pthread_barrier_init.c7
-rw-r--r--sysdeps/sparc/nptl/pthread_barrier_wait.c6
-rw-r--r--sysdeps/sparc/sparc32/pthread_barrier_wait.c6
-rw-r--r--sysdeps/sparc/sparc32/sem_init.c29
-rw-r--r--sysdeps/sparc/sparc32/sem_open.c2
-rw-r--r--sysdeps/sparc/sparc32/sem_post.c26
-rw-r--r--sysdeps/sparc/sparc32/sem_wait.c1
-rw-r--r--sysdeps/sparc/sparc32/sem_waitcommon.c107
-rw-r--r--sysdeps/unix/sysv/linux/futex-internal.h251
15 files changed, 751 insertions, 174 deletions
diff --git a/sysdeps/nacl/exit-thread.h b/sysdeps/nacl/exit-thread.h
index c809405..915f93d 100644
--- a/sysdeps/nacl/exit-thread.h
+++ b/sysdeps/nacl/exit-thread.h
@@ -18,7 +18,7 @@
#include <assert.h>
#include <atomic.h>
-#include <lowlevellock.h>
+#include <futex-internal.h>
#include <nacl-interfaces.h>
#include <nptl/pthreadP.h>
@@ -64,7 +64,7 @@ __exit_thread (void)
assert (NACL_EXITING_TID > 0);
atomic_store_relaxed (&pd->tid, NACL_EXITING_TID);
- lll_futex_wake (&pd->tid, 1, LLL_PRIVATE);
+ futex_wake ((unsigned int *) &pd->tid, 1, FUTEX_PRIVATE);
}
/* This clears PD->tid some time after the thread stack can never
diff --git a/sysdeps/nacl/futex-internal.h b/sysdeps/nacl/futex-internal.h
new file mode 100644
index 0000000..593bb9d
--- /dev/null
+++ b/sysdeps/nacl/futex-internal.h
@@ -0,0 +1,248 @@
+/* futex operations for glibc-internal use. NaCl version.
+ Copyright (C) 2014-2015 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef FUTEX_INTERNAL_H
+#define FUTEX_INTERNAL_H
+
+#include <sysdeps/nptl/futex-internal.h>
+#include <errno.h>
+#include <lowlevellock-futex.h>
+#include <nacl-interfaces.h>
+#include <nptl/pthreadP.h>
+
+/* See sysdeps/nptl/futex-internal.h for documentation; this file only
+ contains NaCl-specific comments.
+
+ There is no support yet for shared futexes nor for exact relative
+ timeouts. */
+
+/* See sysdeps/nptl/futex-internal.h for constraints on the value of the
+ FUTEX_PRIVATE and FUTEX_SHARED constants.
+ Shared futexes are not yet supported, and we never allow clients to
+ actually request shared futexes. Therefore, we do not need a different
+ value. */
+#undef FUTEX_SHARED
+#define FUTEX_SHARED FUTEX_PRIVATE
+
+/* FUTEX_SHARED is not yet supported. */
+static __always_inline int
+futex_supports_pshared (int pshared)
+{
+ if (__glibc_likely (pshared == PTHREAD_PROCESS_PRIVATE))
+ return 0;
+ else if (pshared == PTHREAD_PROCESS_SHARED)
+ return ENOTSUP;
+ else
+ return EINVAL;
+}
+
+/* Relative timeouts are only emulated via absolute timeouts using the
+ system clock. */
+static __always_inline bool
+futex_supports_exact_relative_timeouts (void)
+{
+ return false;
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_wait (unsigned int *futex_word, unsigned int expected, int private)
+{
+ int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ return -err;
+
+ case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_wait_cancelable (unsigned int *futex_word, unsigned int expected,
+ int private)
+{
+ int oldtype;
+ oldtype = __pthread_enable_asynccancel ();
+ int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
+ __pthread_disable_asynccancel (oldtype);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ return -err;
+
+ case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_reltimed_wait (unsigned int *futex_word, unsigned int expected,
+ const struct timespec *reltime, int private)
+{
+ int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ case -ETIMEDOUT:
+ return -err;
+
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_reltimed_wait_cancelable (unsigned int *futex_word,
+ unsigned int expected,
+ const struct timespec *reltime, int private)
+{
+ int oldtype;
+ oldtype = __pthread_enable_asynccancel ();
+ int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
+ __pthread_disable_asynccancel (oldtype);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ case -ETIMEDOUT:
+ return -err;
+
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_abstimed_wait (unsigned int *futex_word, unsigned int expected,
+ const struct timespec *abstime, int private)
+{
+ int err = __nacl_irt_futex.futex_wait_abs ((volatile int *) futex_word,
+ expected, abstime);
+ switch (err)
+ {
+ case 0:
+ case EAGAIN:
+ case EINTR:
+ case ETIMEDOUT:
+ return err;
+
+ case EFAULT: /* Must have been caused by a glibc or application bug. */
+ case EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_abstimed_wait_cancelable (unsigned int *futex_word,
+ unsigned int expected,
+ const struct timespec *abstime, int private)
+{
+ int oldtype;
+ oldtype = __pthread_enable_asynccancel ();
+ int err = __nacl_irt_futex.futex_wait_abs ((volatile int *) futex_word,
+ expected, abstime);
+ __pthread_disable_asynccancel (oldtype);
+ switch (err)
+ {
+ case 0:
+ case EAGAIN:
+ case EINTR:
+ case ETIMEDOUT:
+ return err;
+
+ case EFAULT: /* Must have been caused by a glibc or application bug. */
+ case EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline void
+futex_wake (unsigned int *futex_word, int processes_to_wake, int private)
+{
+ int res = lll_futex_wake (futex_word, processes_to_wake, private);
+ /* No error. Ignore the number of woken processes. */
+ if (res >= 0)
+ return;
+ switch (res)
+ {
+ case -EFAULT: /* Could have happened due to memory reuse. */
+ case -EINVAL: /* Could be either due to incorrect alignment (a bug in
+ glibc or in the application) or due to memory being
+ reused for a PI futex. We cannot distinguish between the
+ two causes, and one of them is correct use, so we do not
+ act in this case. */
+ return;
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+#endif /* futex-internal.h */
diff --git a/sysdeps/nptl/aio_misc.h b/sysdeps/nptl/aio_misc.h
index d5d1c08..4a6ebfc 100644
--- a/sysdeps/nptl/aio_misc.h
+++ b/sysdeps/nptl/aio_misc.h
@@ -22,14 +22,14 @@
#include <assert.h>
#include <nptl/pthreadP.h>
-#include <lowlevellock.h>
+#include <futex-internal.h>
#define DONT_NEED_AIO_MISC_COND 1
#define AIO_MISC_NOTIFY(waitlist) \
do { \
if (*waitlist->counterp > 0 && --*waitlist->counterp == 0) \
- lll_futex_wake (waitlist->counterp, 1, LLL_PRIVATE); \
+ futex_wake ((unsigned int *) waitlist->counterp, 1, FUTEX_PRIVATE); \
} while (0)
#define AIO_MISC_WAIT(result, futex, timeout, cancel) \
@@ -48,9 +48,9 @@
int status; \
do \
{ \
- status = lll_futex_timed_wait (futexaddr, oldval, timeout, \
- LLL_PRIVATE); \
- if (status != -EWOULDBLOCK) \
+ status = futex_reltimed_wait ((unsigned int *) futexaddr, oldval, \
+ timeout, FUTEX_PRIVATE); \
+ if (status != EAGAIN) \
break; \
\
oldval = *futexaddr; \
@@ -60,12 +60,12 @@
if (cancel) \
LIBC_CANCEL_RESET (oldtype); \
\
- if (status == -EINTR) \
+ if (status == EINTR) \
result = EINTR; \
- else if (status == -ETIMEDOUT) \
+ else if (status == ETIMEDOUT) \
result = EAGAIN; \
else \
- assert (status == 0 || status == -EWOULDBLOCK); \
+ assert (status == 0 || status == EAGAIN); \
\
pthread_mutex_lock (&__aio_requests_mutex); \
} \
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index 74482b7..2b9ae4b 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -30,6 +30,7 @@
#include <nptl/pthreadP.h>
#include <fork.h>
#include <arch-fork.h>
+#include <futex-internal.h>
static void
@@ -219,7 +220,7 @@ __libc_fork (void)
if (atomic_decrement_and_test (&allp->handler->refcntr)
&& allp->handler->need_signal)
- lll_futex_wake (&allp->handler->refcntr, 1, LLL_PRIVATE);
+ futex_wake (&allp->handler->refcntr, 1, FUTEX_PRIVATE);
allp = allp->next;
}
diff --git a/sysdeps/nptl/futex-internal.h b/sysdeps/nptl/futex-internal.h
new file mode 100644
index 0000000..4f8c8fe
--- /dev/null
+++ b/sysdeps/nptl/futex-internal.h
@@ -0,0 +1,203 @@
+/* futex operations for glibc-internal use. Stub version; do not include
+ this file directly.
+ Copyright (C) 2014-2015 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef STUB_FUTEX_INTERNAL_H
+#define STUB_FUTEX_INTERNAL_H
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <libc-internal.h>
+
+/* This file defines futex operations used internally in glibc. A futex
+ consists of the so-called futex word in userspace, which is of type
+ unsigned int and represents an application-specific condition, and kernel
+ state associated with this particular futex word (e.g., wait queues). The
+ futex operations we provide are wrappers for the futex syscalls and add
+ glibc-specific error checking of the syscall return value. We abort on
+ error codes that are caused by bugs in glibc or in the calling application,
+ or when an error code is not known. We return error codes that can arise
+ in correct executions to the caller. Each operation calls out exactly the
+ return values that callers need to handle.
+
+ The private flag must be either FUTEX_PRIVATE or FUTEX_SHARED.
+ FUTEX_PRIVATE is always supported, and the implementation can internally
+ use FUTEX_SHARED when FUTEX_PRIVATE is requested. FUTEX_SHARED is not
+ necessarily supported (use futex_supports_pshared to detect this).
+
+ We expect callers to only use these operations if futexes and the
+ specific futex operations being used are supported (e.g., FUTEX_SHARED).
+
+ Given that waking other threads waiting on a futex involves concurrent
+ accesses to the futex word, you must use atomic operations to access the
+ futex word.
+
+ Both absolute and relative timeouts can be used. An absolute timeout
+ expires when the given specific point in time on the CLOCK_REALTIME clock
+ passes, or when it already has passed. A relative timeout expires when
+ the given duration of time on the CLOCK_MONOTONIC clock passes. Relative
+ timeouts may be imprecise (see futex_supports_exact_relative_timeouts).
+
+ Due to POSIX requirements on when synchronization data structures such
+ as mutexes or semaphores can be destroyed and due to the futex design
+ having separate fast/slow paths for wake-ups, we need to consider that
+ futex_wake calls might effectively target a data structure that has been
+ destroyed and reused for another object, or unmapped; thus, some
+ errors or spurious wake-ups can happen in correct executions that would
+ not be possible in a program using just a single futex whose lifetime
+ does not end before the program terminates. For background, see:
+ https://sourceware.org/ml/libc-alpha/2014-04/msg00075.html
+ https://lkml.org/lkml/2014/11/27/472 */
+
+/* Defined this way for interoperability with lowlevellock.
+ FUTEX_PRIVATE must be zero because the initializers for pthread_mutex_t,
+ pthread_rwlock_t, and pthread_cond_t initialize the respective field of
+ those structures to zero, and we want FUTEX_PRIVATE to be the default. */
+#define FUTEX_PRIVATE LLL_PRIVATE
+#define FUTEX_SHARED LLL_SHARED
+#if FUTEX_PRIVATE != 0
+# error FUTEX_PRIVATE must be equal to 0
+#endif
+
+/* Returns EINVAL if PSHARED is neither PTHREAD_PROCESS_PRIVATE nor
+ PTHREAD_PROCESS_SHARED; otherwise, returns 0 if PSHARED is supported, and
+ ENOTSUP if not. */
+static __always_inline int
+futex_supports_pshared (int pshared);
+
+/* Returns true if relative timeouts are robust to concurrent changes to the
+ system clock. If this returns false, relative timeouts can still be used
+ but might be effectively longer or shorter than requested. */
+static __always_inline bool
+futex_supports_exact_relative_timeouts (void);
+
+/* Atomically wrt other futex operations on the same futex, this blocks iff
+ the value *FUTEX_WORD matches the expected value. This is
+ semantically equivalent to:
+ l = <get lock associated with futex> (FUTEX_WORD);
+ wait_flag = <get wait_flag associated with futex> (FUTEX_WORD);
+ lock (l);
+ val = atomic_load_relaxed (FUTEX_WORD);
+ if (val != expected) { unlock (l); return EAGAIN; }
+ atomic_store_relaxed (wait_flag, true);
+ unlock (l);
+ // Now block; can time out in futex_time_wait (see below)
+ while (atomic_load_relaxed(wait_flag) && !<spurious wake-up>);
+
+ Note that no guarantee of a happens-before relation between a woken
+ futex_wait and a futex_wake is documented; however, this does not matter
+ in practice because we have to consider spurious wake-ups (see below),
+ and thus would not be able to reliably reason about which futex_wake woke
+ us.
+
+ Returns 0 if woken by a futex operation or spuriously. (Note that due to
+ the POSIX requirements mentioned above, we need to conservatively assume
+ that unrelated futex_wake operations could wake this futex; it is easiest
+ to just be prepared for spurious wake-ups.)
+ Returns EAGAIN if the futex word did not match the expected value.
+ Returns EINTR if waiting was interrupted by a signal.
+
+ Note that some previous code in glibc assumed the underlying futex
+ operation (e.g., syscall) to start with or include the equivalent of a
+ seq_cst fence; this allows one to avoid an explicit seq_cst fence before
+ a futex_wait call when synchronizing similar to Dekker synchronization.
+ However, we make no such guarantee here. */
+static __always_inline int
+futex_wait (unsigned int *futex_word, unsigned int expected, int private);
+
+/* Like futex_wait but does not provide any indication why we stopped waiting.
+ Thus, when this function returns, you have to always check FUTEX_WORD to
+ determine whether you need to continue waiting, and you cannot detect
+ whether the waiting was interrupted by a signal. Example use:
+ while (atomic_load_relaxed (&futex_word) == 23)
+ futex_wait_simple (&futex_word, 23, FUTEX_PRIVATE);
+ This is common enough to make providing this wrapper worthwhile. */
+static __always_inline void
+futex_wait_simple (unsigned int *futex_word, unsigned int expected,
+ int private)
+{
+ ignore_value (futex_wait (futex_word, expected, private));
+}
+
+
+/* Like futex_wait but is a POSIX cancellation point. */
+static __always_inline int
+futex_wait_cancelable (unsigned int *futex_word, unsigned int expected,
+ int private);
+
+/* Like futex_wait, but will eventually time out (i.e., stop being
+ blocked) after the duration of time provided (i.e., RELTIME) has
+ passed. The caller must provide a normalized RELTIME. RELTIME can also
+ equal NULL, in which case this function behaves equivalent to futex_wait.
+
+ Returns the same values as futex_wait under those same conditions;
+ additionally, returns ETIMEDOUT if the timeout expired.
+ */
+static __always_inline int
+futex_reltimed_wait (unsigned int* futex_word, unsigned int expected,
+ const struct timespec* reltime, int private);
+
+/* Like futex_reltimed_wait but is a POSIX cancellation point. */
+static __always_inline int
+futex_reltimed_wait_cancelable (unsigned int* futex_word,
+ unsigned int expected,
+ const struct timespec* reltime, int private);
+
+/* Like futex_reltimed_wait, but the provided timeout (ABSTIME) is an
+ absolute point in time; a call will time out after this point in time. */
+static __always_inline int
+futex_abstimed_wait (unsigned int* futex_word, unsigned int expected,
+ const struct timespec* abstime, int private);
+
+/* Like futex_reltimed_wait but is a POSIX cancellation point. */
+static __always_inline int
+futex_abstimed_wait_cancelable (unsigned int* futex_word,
+ unsigned int expected,
+ const struct timespec* abstime, int private);
+
+/* Atomically wrt other futex operations on the same futex, this unblocks the
+ specified number of processes, or all processes blocked on this futex if
+ there are fewer than the specified number. Semantically, this is
+ equivalent to:
+ l = <get lock associated with futex> (FUTEX_WORD);
+ lock (l);
+ for (res = 0; PROCESSES_TO_WAKE > 0; PROCESSES_TO_WAKE--, res++) {
+ if (<no process blocked on futex>) break;
+ wf = <get wait_flag of a process blocked on futex> (FUTEX_WORD);
+ // No happens-before guarantee with woken futex_wait (see above)
+ atomic_store_relaxed (wf, 0);
+ }
+ return res;
+
+ Note that we need to support futex_wake calls to past futexes whose memory
+ has potentially been reused due to POSIX' requirements on synchronization
+ object destruction (see above); therefore, we must not report or abort
+ on most errors. */
+static __always_inline void
+futex_wake (unsigned int* futex_word, int processes_to_wake, int private);
+
+/* Calls __libc_fatal with an error message. Convenience function for
+ concrete implementations of the futex interface. */
+static __always_inline __attribute__ ((__noreturn__)) void
+futex_fatal_error (void)
+{
+ __libc_fatal ("The futex facility returned an unexpected error code.");
+}
+
+#endif /* futex-internal.h */
diff --git a/sysdeps/nptl/gai_misc.h b/sysdeps/nptl/gai_misc.h
index a34dbc0..96c8fa0 100644
--- a/sysdeps/nptl/gai_misc.h
+++ b/sysdeps/nptl/gai_misc.h
@@ -23,14 +23,14 @@
#include <assert.h>
#include <signal.h>
#include <nptl/pthreadP.h>
-#include <lowlevellock.h>
+#include <futex-internal.h>
#define DONT_NEED_GAI_MISC_COND 1
#define GAI_MISC_NOTIFY(waitlist) \
do { \
if (*waitlist->counterp > 0 && --*waitlist->counterp == 0) \
- lll_futex_wake (waitlist->counterp, 1, LLL_PRIVATE); \
+ futex_wake ((unsigned int *) waitlist->counterp, 1, FUTEX_PRIVATE); \
} while (0)
#define GAI_MISC_WAIT(result, futex, timeout, cancel) \
@@ -49,9 +49,9 @@
int status; \
do \
{ \
- status = lll_futex_timed_wait (futexaddr, oldval, timeout, \
- LLL_PRIVATE); \
- if (status != -EWOULDBLOCK) \
+ status = futex_reltimed_wait ((unsigned int *) futexaddr, oldval, \
+ timeout, FUTEX_PRIVATE); \
+ if (status != EAGAIN) \
break; \
\
oldval = *futexaddr; \
@@ -61,12 +61,12 @@
if (cancel) \
LIBC_CANCEL_RESET (oldtype); \
\
- if (status == -EINTR) \
+ if (status == EINTR) \
result = EINTR; \
- else if (status == -ETIMEDOUT) \
+ else if (status == ETIMEDOUT) \
result = EAGAIN; \
else \
- assert (status == 0 || status == -EWOULDBLOCK); \
+ assert (status == 0 || status == EAGAIN); \
\
pthread_mutex_lock (&__gai_requests_mutex); \
} \
diff --git a/sysdeps/sparc/nptl/pthread_barrier_init.c b/sysdeps/sparc/nptl/pthread_barrier_init.c
index aa21a63..86ec7d0 100644
--- a/sysdeps/sparc/nptl/pthread_barrier_init.c
+++ b/sysdeps/sparc/nptl/pthread_barrier_init.c
@@ -35,10 +35,9 @@ __pthread_barrier_init (barrier, attr, count)
struct pthread_barrierattr *iattr = (struct pthread_barrierattr *) attr;
if (iattr != NULL)
{
- if (iattr->pshared != PTHREAD_PROCESS_PRIVATE
- && __builtin_expect (iattr->pshared != PTHREAD_PROCESS_SHARED, 0))
- /* Invalid attribute. */
- return EINVAL;
+ int err = futex_supports_pshared (iattr->pshared);
+ if (err != 0)
+ return err;
}
ibarrier = (union sparc_pthread_barrier *) barrier;
diff --git a/sysdeps/sparc/nptl/pthread_barrier_wait.c b/sysdeps/sparc/nptl/pthread_barrier_wait.c
index dd4c336..9e9806a 100644
--- a/sysdeps/sparc/nptl/pthread_barrier_wait.c
+++ b/sysdeps/sparc/nptl/pthread_barrier_wait.c
@@ -21,6 +21,7 @@
#include <lowlevellock.h>
#include <pthreadP.h>
#include <sparc-nptl.h>
+#include <futex-internal.h>
/* Wait on barrier. */
int
@@ -31,6 +32,7 @@ __pthread_barrier_wait (barrier)
= (union sparc_pthread_barrier *) barrier;
int result = 0;
int private = ibarrier->s.pshared ? LLL_SHARED : LLL_PRIVATE;
+ int futex_private = ibarrier->s.pshared ? FUTEX_SHARED : FUTEX_PRIVATE;
/* Make sure we are alone. */
lll_lock (ibarrier->b.lock, private);
@@ -46,7 +48,7 @@ __pthread_barrier_wait (barrier)
++ibarrier->b.curr_event;
/* Wake up everybody. */
- lll_futex_wake (&ibarrier->b.curr_event, INT_MAX, private);
+ futex_wake (&ibarrier->b.curr_event, INT_MAX, futex_private);
/* This is the thread which finished the serialization. */
result = PTHREAD_BARRIER_SERIAL_THREAD;
@@ -62,7 +64,7 @@ __pthread_barrier_wait (barrier)
/* Wait for the event counter of the barrier to change. */
do
- lll_futex_wait (&ibarrier->b.curr_event, event, private);
+ futex_wait_simple (&ibarrier->b.curr_event, event, futex_private);
while (event == ibarrier->b.curr_event);
}
diff --git a/sysdeps/sparc/sparc32/pthread_barrier_wait.c b/sysdeps/sparc/sparc32/pthread_barrier_wait.c
index 81d22b0..5e1aa11 100644
--- a/sysdeps/sparc/sparc32/pthread_barrier_wait.c
+++ b/sysdeps/sparc/sparc32/pthread_barrier_wait.c
@@ -21,6 +21,7 @@
#include <lowlevellock.h>
#include <pthreadP.h>
#include <sparc-nptl.h>
+#include <futex-internal.h>
/* Wait on barrier. */
int
@@ -31,6 +32,7 @@ __pthread_barrier_wait (barrier)
= (union sparc_pthread_barrier *) barrier;
int result = 0;
int private = ibarrier->s.pshared ? LLL_SHARED : LLL_PRIVATE;
+ int futex_private = ibarrier->s.pshared ? FUTEX_SHARED : FUTEX_PRIVATE;
/* Make sure we are alone. */
lll_lock (ibarrier->b.lock, private);
@@ -46,7 +48,7 @@ __pthread_barrier_wait (barrier)
++ibarrier->b.curr_event;
/* Wake up everybody. */
- lll_futex_wake (&ibarrier->b.curr_event, INT_MAX, private);
+ futex_wake (&ibarrier->b.curr_event, INT_MAX, futex_private);
/* This is the thread which finished the serialization. */
result = PTHREAD_BARRIER_SERIAL_THREAD;
@@ -62,7 +64,7 @@ __pthread_barrier_wait (barrier)
/* Wait for the event counter of the barrier to change. */
do
- lll_futex_wait (&ibarrier->b.curr_event, event, private);
+ futex_wait_simple (&ibarrier->b.curr_event, event, futex_private);
while (event == ibarrier->b.curr_event);
}
diff --git a/sysdeps/sparc/sparc32/sem_init.c b/sysdeps/sparc/sparc32/sem_init.c
index 7c46cee..1c7c455 100644
--- a/sysdeps/sparc/sparc32/sem_init.c
+++ b/sysdeps/sparc/sparc32/sem_init.c
@@ -20,23 +20,7 @@
#include <semaphore.h>
#include <shlib-compat.h>
#include "semaphoreP.h"
-#include <kernel-features.h>
-
-/* Returns FUTEX_PRIVATE if pshared is zero and private futexes are supported;
- returns FUTEX_SHARED otherwise.
- TODO Remove when cleaning up the futex API throughout glibc. */
-static __always_inline int
-futex_private_if_supported (int pshared)
-{
- if (pshared != 0)
- return LLL_SHARED;
-#ifdef __ASSUME_PRIVATE_FUTEX
- return LLL_PRIVATE;
-#else
- return THREAD_GETMEM (THREAD_SELF, header.private_futex)
- ^ FUTEX_PRIVATE_FLAG;
-#endif
-}
+#include <futex-internal.h>
int
@@ -49,6 +33,14 @@ __new_sem_init (sem_t *sem, int pshared, unsigned int value)
return -1;
}
+ pshared = pshared != 0 ? PTHREAD_PROCESS_SHARED : PTHREAD_PROCESS_PRIVATE;
+ int err = futex_supports_pshared (pshared);
+ if (err != 0)
+ {
+ __set_errno (err);
+ return -1;
+ }
+
/* Map to the internal type. */
struct new_sem *isem = (struct new_sem *) sem;
@@ -57,7 +49,8 @@ __new_sem_init (sem_t *sem, int pshared, unsigned int value)
isem->pad = 0;
isem->nwaiters = 0;
- isem->private = futex_private_if_supported (pshared);
+ isem->private = (pshared == PTHREAD_PROCESS_PRIVATE
+ ? FUTEX_PRIVATE : FUTEX_SHARED);
return 0;
}
diff --git a/sysdeps/sparc/sparc32/sem_open.c b/sysdeps/sparc/sparc32/sem_open.c
index 2698d19..16cb9ad 100644
--- a/sysdeps/sparc/sparc32/sem_open.c
+++ b/sysdeps/sparc/sparc32/sem_open.c
@@ -199,7 +199,7 @@ sem_open (const char *name, int oflag, ...)
sem.newsem.nwaiters = 0;
/* This always is a shared semaphore. */
- sem.newsem.private = LLL_SHARED;
+ sem.newsem.private = FUTEX_SHARED;
/* Initialize the remaining bytes as well. */
memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0',
diff --git a/sysdeps/sparc/sparc32/sem_post.c b/sysdeps/sparc/sparc32/sem_post.c
index c9f85a0..fd1a2fe 100644
--- a/sysdeps/sparc/sparc32/sem_post.c
+++ b/sysdeps/sparc/sparc32/sem_post.c
@@ -23,34 +23,10 @@
#include <lowlevellock.h>
#include <internaltypes.h>
#include <semaphore.h>
+#include <futex-internal.h>
#include <shlib-compat.h>
-/* Wrapper for lll_futex_wake, with error checking.
- TODO Remove when cleaning up the futex API throughout glibc. */
-static __always_inline void
-futex_wake (unsigned int* futex, int processes_to_wake, int private)
-{
- int res = lll_futex_wake (futex, processes_to_wake, private);
- /* No error. Ignore the number of woken processes. */
- if (res >= 0)
- return;
- switch (res)
- {
- case -EFAULT: /* Could have happened due to memory reuse. */
- case -EINVAL: /* Could be either due to incorrect alignment (a bug in
- glibc or in the application) or due to memory being
- reused for a PI futex. We cannot distinguish between the
- two causes, and one of them is correct use, so we do not
- act in this case. */
- return;
- case -ENOSYS: /* Must have been caused by a glibc bug. */
- /* No other errors are documented at this time. */
- default:
- abort ();
- }
-}
-
/* See sem_wait for an explanation of the algorithm. */
int
diff --git a/sysdeps/sparc/sparc32/sem_wait.c b/sysdeps/sparc/sparc32/sem_wait.c
index c1fd10c..fce7ed4 100644
--- a/sysdeps/sparc/sparc32/sem_wait.c
+++ b/sysdeps/sparc/sparc32/sem_wait.c
@@ -17,6 +17,7 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <lowlevellock.h> /* lll_futex* used by the old code. */
#include "sem_waitcommon.c"
int
diff --git a/sysdeps/sparc/sparc32/sem_waitcommon.c b/sysdeps/sparc/sparc32/sem_waitcommon.c
index 9c1c6a5..9e43d45 100644
--- a/sysdeps/sparc/sparc32/sem_waitcommon.c
+++ b/sysdeps/sparc/sparc32/sem_waitcommon.c
@@ -19,7 +19,7 @@
#include <errno.h>
#include <sysdep.h>
-#include <lowlevellock.h>
+#include <futex-internal.h>
#include <internaltypes.h>
#include <semaphore.h>
#include <sys/time.h>
@@ -28,104 +28,6 @@
#include <shlib-compat.h>
#include <atomic.h>
-/* Wrapper for lll_futex_wait with absolute timeout and error checking.
- TODO Remove when cleaning up the futex API throughout glibc. */
-static __always_inline int
-futex_abstimed_wait (unsigned int* futex, unsigned int expected,
- const struct timespec* abstime, int private, bool cancel)
-{
- int err, oldtype;
- if (abstime == NULL)
- {
- if (cancel)
- oldtype = __pthread_enable_asynccancel ();
- err = lll_futex_wait (futex, expected, private);
- if (cancel)
- __pthread_disable_asynccancel (oldtype);
- }
- else
- {
- struct timeval tv;
- struct timespec rt;
- int sec, nsec;
-
- /* Get the current time. */
- __gettimeofday (&tv, NULL);
-
- /* Compute relative timeout. */
- sec = abstime->tv_sec - tv.tv_sec;
- nsec = abstime->tv_nsec - tv.tv_usec * 1000;
- if (nsec < 0)
- {
- nsec += 1000000000;
- --sec;
- }
-
- /* Already timed out? */
- if (sec < 0)
- return ETIMEDOUT;
-
- /* Do wait. */
- rt.tv_sec = sec;
- rt.tv_nsec = nsec;
- if (cancel)
- oldtype = __pthread_enable_asynccancel ();
- err = lll_futex_timed_wait (futex, expected, &rt, private);
- if (cancel)
- __pthread_disable_asynccancel (oldtype);
- }
- switch (err)
- {
- case 0:
- case -EAGAIN:
- case -EINTR:
- case -ETIMEDOUT:
- return -err;
-
- case -EFAULT: /* Must have been caused by a glibc or application bug. */
- case -EINVAL: /* Either due to wrong alignment or due to the timeout not
- being normalized. Must have been caused by a glibc or
- application bug. */
- case -ENOSYS: /* Must have been caused by a glibc bug. */
- /* No other errors are documented at this time. */
- default:
- abort ();
- }
-}
-
-/* Wrapper for lll_futex_wake, with error checking.
- TODO Remove when cleaning up the futex API throughout glibc. */
-static __always_inline void
-futex_wake (unsigned int* futex, int processes_to_wake, int private)
-{
- int res = lll_futex_wake (futex, processes_to_wake, private);
- /* No error. Ignore the number of woken processes. */
- if (res >= 0)
- return;
- switch (res)
- {
- case -EFAULT: /* Could have happened due to memory reuse. */
- case -EINVAL: /* Could be either due to incorrect alignment (a bug in
- glibc or in the application) or due to memory being
- reused for a PI futex. We cannot distinguish between the
- two causes, and one of them is correct use, so we do not
- act in this case. */
- return;
- case -ENOSYS: /* Must have been caused by a glibc bug. */
- /* No other errors are documented at this time. */
- default:
- abort ();
- }
-}
-
-
-/* Set this to true if you assume that, in contrast to current Linux futex
- documentation, lll_futex_wake can return -EINTR only if interrupted by a
- signal, not spuriously due to some other reason.
- TODO Discuss EINTR conditions with the Linux kernel community. For
- now, we set this to true to not change behavior of semaphores compared
- to previous glibc builds. */
-static const int sem_assume_only_signals_cause_futex_EINTR = 1;
static void
__sem_wait_32_finish (struct new_sem *sem);
@@ -149,8 +51,8 @@ do_futex_wait (struct new_sem *sem, const struct timespec *abstime)
{
int err;
- err = futex_abstimed_wait (&sem->value, SEM_NWAITERS_MASK, abstime,
- sem->private, true);
+ err = futex_abstimed_wait_cancelable (&sem->value, SEM_NWAITERS_MASK,
+ abstime, sem->private);
return err;
}
@@ -202,8 +104,7 @@ __new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime)
__sparc32_atomic_do_unlock24(&sem->pad);
err = do_futex_wait(sem, abstime);
- if (err == ETIMEDOUT ||
- (err == EINTR && sem_assume_only_signals_cause_futex_EINTR))
+ if (err == ETIMEDOUT || err == EINTR)
{
__set_errno (err);
err = -1;
diff --git a/sysdeps/unix/sysv/linux/futex-internal.h b/sysdeps/unix/sysv/linux/futex-internal.h
new file mode 100644
index 0000000..aca0911
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/futex-internal.h
@@ -0,0 +1,251 @@
+/* futex operations for glibc-internal use. Linux version.
+ Copyright (C) 2014-2015 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
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef FUTEX_INTERNAL_H
+#define FUTEX_INTERNAL_H
+
+#include <sysdeps/nptl/futex-internal.h>
+#include <errno.h>
+#include <lowlevellock-futex.h>
+#include <nptl/pthreadP.h>
+
+/* See sysdeps/nptl/futex-internal.h for documentation; this file only
+ contains Linux-specific comments.
+
+ The Linux kernel treats provides absolute timeouts based on the
+ CLOCK_REALTIME clock and relative timeouts measured against the
+ CLOCK_MONOTONIC clock.
+
+ We expect a Linux kernel version of 2.6.22 or more recent (since this
+ version, EINTR is not returned on spurious wake-ups anymore). */
+
+/* FUTEX_SHARED is always supported by the Linux kernel. */
+static __always_inline int
+futex_supports_pshared (int pshared)
+{
+ if (__glibc_likely (pshared == PTHREAD_PROCESS_PRIVATE))
+ return 0;
+ else if (pshared == PTHREAD_PROCESS_SHARED)
+ return 0;
+ else
+ return EINVAL;
+}
+
+/* The Linux kernel supports relative timeouts measured against the
+ CLOCK_MONOTONIC clock. */
+static __always_inline bool
+futex_supports_exact_relative_timeouts (void)
+{
+ return true;
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_wait (unsigned int *futex_word, unsigned int expected, int private)
+{
+ int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ return -err;
+
+ case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_wait_cancelable (unsigned int *futex_word, unsigned int expected,
+ int private)
+{
+ int oldtype;
+ oldtype = __pthread_enable_asynccancel ();
+ int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
+ __pthread_disable_asynccancel (oldtype);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ return -err;
+
+ case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_reltimed_wait (unsigned int *futex_word, unsigned int expected,
+ const struct timespec *reltime, int private)
+{
+ int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ case -ETIMEDOUT:
+ return -err;
+
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_reltimed_wait_cancelable (unsigned int *futex_word,
+ unsigned int expected,
+ const struct timespec *reltime, int private)
+{
+ int oldtype;
+ oldtype = __pthread_enable_asynccancel ();
+ int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
+ __pthread_disable_asynccancel (oldtype);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ case -ETIMEDOUT:
+ return -err;
+
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_abstimed_wait (unsigned int *futex_word, unsigned int expected,
+ const struct timespec *abstime, int private)
+{
+ /* Work around the fact that the kernel rejects negative timeout values
+ despite them being valid. */
+ if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
+ return ETIMEDOUT;
+ int err = lll_futex_timed_wait_bitset (futex_word, expected, abstime,
+ FUTEX_CLOCK_REALTIME, private);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ case -ETIMEDOUT:
+ return -err;
+
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline int
+futex_abstimed_wait_cancelable (unsigned int *futex_word,
+ unsigned int expected,
+ const struct timespec *abstime, int private)
+{
+ /* Work around the fact that the kernel rejects negative timeout values
+ despite them being valid. */
+ if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
+ return ETIMEDOUT;
+ int oldtype;
+ oldtype = __pthread_enable_asynccancel ();
+ int err = lll_futex_timed_wait_bitset (futex_word, expected, abstime,
+ FUTEX_CLOCK_REALTIME, private);
+ __pthread_disable_asynccancel (oldtype);
+ switch (err)
+ {
+ case 0:
+ case -EAGAIN:
+ case -EINTR:
+ case -ETIMEDOUT:
+ return -err;
+
+ case -EFAULT: /* Must have been caused by a glibc or application bug. */
+ case -EINVAL: /* Either due to wrong alignment or due to the timeout not
+ being normalized. Must have been caused by a glibc or
+ application bug. */
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+/* See sysdeps/nptl/futex-internal.h for details. */
+static __always_inline void
+futex_wake (unsigned int *futex_word, int processes_to_wake, int private)
+{
+ int res = lll_futex_wake (futex_word, processes_to_wake, private);
+ /* No error. Ignore the number of woken processes. */
+ if (res >= 0)
+ return;
+ switch (res)
+ {
+ case -EFAULT: /* Could have happened due to memory reuse. */
+ case -EINVAL: /* Could be either due to incorrect alignment (a bug in
+ glibc or in the application) or due to memory being
+ reused for a PI futex. We cannot distinguish between the
+ two causes, and one of them is correct use, so we do not
+ act in this case. */
+ return;
+ case -ENOSYS: /* Must have been caused by a glibc bug. */
+ /* No other errors are documented at this time. */
+ default:
+ futex_fatal_error ();
+ }
+}
+
+#endif /* futex-internal.h */