Mike Crowe
2017-06-22 17:05:31 UTC
C++11's std::condition_variable::wait_until specifies the clock to be used
at the time of the wait and permits separate waits on the same condition
variable instance using different clocks.
Unfortunately pthread_cond_timedwait always uses the clock that was
specified (or defaulted) when pthread_cond_init was called. libstdc++'s
current implementation converts all waits to
std::chrono::system_clock (i.e. CLOCK_REALTIME) which can race against
the system clock changing.
Let's invent a brand-new function pthread_cond_timedwaitonclock_np which
accepts both the clock and the time point as parameters is straightforward
and means that the C++11 standard behaviour can be implemented in libstdc++
on Linux at least.
Earlier versions of this patch were proposed in 2015 but further progress
stalled waiting for the assembly implementation of pthread_cond_timedwait.S
to be removed by Torvald Riegel. This happened in
ed19993b5b0d05d62cc883571519a67dae481a14.
See the following threads for previous versions and discussion:
* https://sourceware.org/ml/libc-alpha/2015-07/msg00193.html
* https://sourceware.org/ml/libc-alpha/2015-08/msg00186.html
* https://sourceware.org/ml/libc-alpha/2015-08/msg00230.html
Some of the abilist updates for other architectures appear to have gone
missing during the rebase. If this change is reviewed favourably then I
shall try to include them.
Signed-off-by: Mike Crowe <***@mcrowe.com>
---
ChangeLog | 48 +++++
manual/threads.texi | 21 +++
nptl/Makefile | 4 +-
nptl/Versions | 7 +
nptl/forward.c | 5 +
nptl/nptl-init.c | 1 +
nptl/pthreadP.h | 4 +
nptl/pthread_cond_wait.c | 28 ++-
nptl/tst-cond11-onclock.c | 209 +++++++++++++++++++++
nptl/tst-cond5-onclock.c | 114 +++++++++++
sysdeps/nptl/pthread-functions.h | 4 +
sysdeps/nptl/pthread.h | 15 ++
sysdeps/unix/sysv/linux/arm/libc.abilist | 1 +
sysdeps/unix/sysv/linux/arm/libpthread.abilist | 1 +
sysdeps/unix/sysv/linux/i386/libc.abilist | 1 +
sysdeps/unix/sysv/linux/i386/libpthread.abilist | 1 +
.../sysv/linux/m68k/coldfire/libpthread.abilist | 1 +
.../unix/sysv/linux/m68k/m680x0/libpthread.abilist | 1 +
sysdeps/unix/sysv/linux/x86_64/64/libc.abilist | 1 +
.../unix/sysv/linux/x86_64/64/libpthread.abilist | 2 +
sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist | 1 +
.../unix/sysv/linux/x86_64/x32/libpthread.abilist | 1 +
22 files changed, 466 insertions(+), 5 deletions(-)
create mode 100644 nptl/tst-cond11-onclock.c
create mode 100644 nptl/tst-cond5-onclock.c
diff --git a/ChangeLog b/ChangeLog
index 46a70ff7ce..385deb472f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,51 @@
+2017-06-22 Mike Crowe <***@mcrowe.com>
+
+ * sysdeps/nptl/pthread.h: Add pthread_cond_timedwaitonclock_np
+ declaration
+ * nptl/pthread_cond.c:
+ (__pthread_cond_wait_common): Add clockid parameter to determine
+ which clock to use.
+ (__pthread_cond_wait): Add dummy clockid parameter when calling
+ __pthread_cond_wait_common.
+ (__pthread_cond_timedwait): Read default clock set during
+ pthread_cond_init to supply as clockid parameter in call to
+ __pthread_cond_wait_common.
+ (__pthread_cond_timedwaitonclock_np): New function that supports
+ specifying clock to use at call time rather than initialisation
+ time.
+ (pthread_cond_timdwaitonclock_np): Weak alias for
+ __pthread_cond_timedwaitonclock_np
+ * nptl/tst-cond5-onclock.c: New file to test
+ pthread_cond_timedwaitonclock_np
+ * nptl/tst-cond11-onclock.c: New file to test
+ pthread_cond_timedwaitonclock_np
+ * nptl/Makefile: Add tst-cond5-onclock and tst-cond11-onclock
+ * nptl/Versions (GLIBC_2.26): Add pthread_cond_timedwaitonclock_np
+ to both libc and libpthread
+ * nptl/pthread_functions.h: Add
+ ptr___pthread_cond_timedwaitonclock_np
+ * nptl/forward.c (pthread_functions): Forward
+ pthread_cond_timedwaitonclock_np from libc to libpthread3
+ * nptl/nptl-init.c: Forwarding function initialisation for
+ pthread_cond_timedwaitonclock_np
+ * nptl/pthreadP.h (__pthread_cond_timedwait_np): Add
+ * manual/threads.texi: Add documentation for
+ pthread_cond_timedwaitonclock_np
+ * sysdeps/unix/sysv/linux/aarch64/libc.abilist: Add
+ pthread_cond_timedwaitonclock_np
+ * sysdeps/unix/sysv/linux/arm/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/arm/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/i386/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/i386/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/64/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist: Likewise
+
2017-06-21 Florian Weimer <***@redhat.com>
* intl/loadmsgcat.c: Remove alloca support.
diff --git a/manual/threads.texi b/manual/threads.texi
index 769d974d50..8b3652c859 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -124,6 +124,27 @@ The system does not have sufficient memory.
@end table
@end deftypefun
+@comment pthread.h
+@comment GNU
+@deftypefun int pthread_cond_timedwaitonclock_np (pthread_cond_t *@var{cond}, pthread_mutex_t *@var{mutex},
+ clockid_t @var{clockid}, const struct timespec *@var{abstime})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock}}
+@c If exactly the same function with arguments is called from a signal
+@c handler that interrupts between the mutex unlock and sleep then it
+@c will unlock the mutex twice resulting in undefined behaviour. Keep
+@c in mind that the unlock and sleep are only atomic with respect to other
+@c threads (really a happens-after relationship for pthread_cond_broadcast
+@c and pthread_cond_signal).
+@c In the AC case we would cancel the thread and the mutex would remain
+@c locked and we can't recover from that.
+Behaves like POSIX @code{pthread_cond_timedwait} except the time
+@var{abstime} is measured against the clock specified by @var{clockid}
+rather than the clock specified or defaulted when @code{pthread_cond_init}
+was called. Currently, @code{clockid} must be either @code{CLOCK_MONOTONIC}
+or @code{CLOCK_REALTIME}. This variant is necessary in order to implement
+C++11's @code{std::condition_variable::wait_until} method correctly.
+@end deftypefun
+
@c FIXME these are undocumented:
@c pthread_atfork
@c pthread_attr_destroy
diff --git a/nptl/Makefile b/nptl/Makefile
index 853da72e74..8cf661dc64 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -235,8 +235,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
tst-mutexpi5a tst-mutexpi6 tst-mutexpi7 tst-mutexpi7a \
tst-mutexpi9 \
tst-spin1 tst-spin2 tst-spin3 tst-spin4 \
- tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \
- tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond12 tst-cond13 \
+ tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond5-onclock tst-cond6 tst-cond7 \
+ tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond11-onclock tst-cond12 tst-cond13 \
tst-cond14 tst-cond15 tst-cond16 tst-cond17 tst-cond18 tst-cond19 \
tst-cond20 tst-cond21 tst-cond22 tst-cond23 tst-cond24 tst-cond25 \
tst-cond-except \
diff --git a/nptl/Versions b/nptl/Versions
index 0ae5def464..1cf8c2fbad 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -28,6 +28,9 @@ libc {
pthread_cond_wait; pthread_cond_signal;
pthread_cond_broadcast; pthread_cond_timedwait;
}
+ GLIBC_2.26 {
+ pthread_cond_timedwaitonclock_np;
+ }
GLIBC_PRIVATE {
__libc_alloca_cutoff;
# Internal libc interface to libpthread
@@ -265,6 +268,10 @@ libpthread {
GLIBC_2.22 {
}
+ GLIBC_2.26 {
+ pthread_cond_timedwaitonclock_np;
+ }
+
GLIBC_PRIVATE {
__pthread_initialize_minimal;
__pthread_clock_gettime; __pthread_clock_settime;
diff --git a/nptl/forward.c b/nptl/forward.c
index ac96765f29..c5fcb2f066 100644
--- a/nptl/forward.c
+++ b/nptl/forward.c
@@ -164,6 +164,11 @@ FORWARD (__pthread_cond_timedwait,
const struct timespec *abstime), (cond, mutex, abstime), 0)
versioned_symbol (libc, __pthread_cond_timedwait, pthread_cond_timedwait,
GLIBC_2_3_2);
+FORWARD (__pthread_cond_timedwaitonclock_np,
+ (pthread_cond_t *cond, pthread_mutex_t *mutex, clockid_t clockid,
+ const struct timespec *abstime), (cond, mutex, clockid, abstime),
+ 0)
+weak_alias (__pthread_cond_timedwaitonclock_np, pthread_cond_timedwaitonclock_np);
FORWARD (pthread_equal, (pthread_t thread1, pthread_t thread2),
diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
index 29216077a2..b913515fe9 100644
--- a/nptl/nptl-init.c
+++ b/nptl/nptl-init.c
@@ -106,6 +106,7 @@ static const struct pthread_functions pthread_functions =
.ptr___pthread_cond_signal = __pthread_cond_signal,
.ptr___pthread_cond_wait = __pthread_cond_wait,
.ptr___pthread_cond_timedwait = __pthread_cond_timedwait,
+ .ptr___pthread_cond_timedwaitonclock_np = __pthread_cond_timedwaitonclock_np,
# if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
.ptr___pthread_cond_broadcast_2_0 = __pthread_cond_broadcast_2_0,
.ptr___pthread_cond_destroy_2_0 = __pthread_cond_destroy_2_0,
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index 7fc1e50f78..c7e7587479 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -488,6 +488,10 @@ extern int __pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
extern int __pthread_cond_timedwait (pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *abstime);
+extern int __pthread_cond_timedwaitonclock_np (pthread_cond_t *cond,
+ pthread_mutex_t *mutex,
+ clockid_t clockid,
+ const struct timespec *abstime);
extern int __pthread_condattr_destroy (pthread_condattr_t *attr);
extern int __pthread_condattr_init (pthread_condattr_t *attr);
extern int __pthread_key_create (pthread_key_t *key, void (*destr) (void *));
diff --git a/nptl/pthread_cond_wait.c b/nptl/pthread_cond_wait.c
index 7812b94a3a..d0166b86d5 100644
--- a/nptl/pthread_cond_wait.c
+++ b/nptl/pthread_cond_wait.c
@@ -378,6 +378,7 @@ __condvar_cleanup_waiting (void *arg)
*/
static __always_inline int
__pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ clockid_t clockid,
const struct timespec *abstime)
{
const int maxspin = 0;
@@ -510,7 +511,7 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
if (__glibc_unlikely (abstime->tv_sec < 0))
err = ETIMEDOUT;
- else if ((flags & __PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0)
+ else if (clockid == CLOCK_MONOTONIC)
{
/* CLOCK_MONOTONIC is requested. */
struct timespec rt;
@@ -652,7 +653,7 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
int
__pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
{
- return __pthread_cond_wait_common (cond, mutex, NULL);
+ return __pthread_cond_wait_common (cond, mutex, CLOCK_REALTIME, NULL);
}
/* See __pthread_cond_wait_common. */
@@ -664,10 +665,31 @@ __pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
it can assume that abstime is not NULL. */
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
return EINVAL;
- return __pthread_cond_wait_common (cond, mutex, abstime);
+
+ clockid_t clockid = ((cond->__data.__wrefs & __PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+ return __pthread_cond_wait_common (cond, mutex, clockid, abstime);
+}
+
+/* See __pthread_cond_wait_common. */
+int
+__pthread_cond_timedwaitonclock_np (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ clockid_t clockid,
+ const struct timespec *abstime)
+{
+ /* Check parameter validity. This should also tell the compiler that
+ it can assume that abstime is not NULL. */
+ if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
+ return EINVAL;
+
+ /* We only support CLOCK_REALTIME and CLOCK_MONOTONIC */
+ if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+ return EINVAL;
+
+ return __pthread_cond_wait_common (cond, mutex, clockid, abstime);
}
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
GLIBC_2_3_2);
versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
GLIBC_2_3_2);
+weak_alias (__pthread_cond_timedwaitonclock_np, pthread_cond_timedwaitonclock_np);
diff --git a/nptl/tst-cond11-onclock.c b/nptl/tst-cond11-onclock.c
new file mode 100644
index 0000000000..439cd404de
--- /dev/null
+++ b/nptl/tst-cond11-onclock.c
@@ -0,0 +1,209 @@
+/* Test pthread_cond_timedwaitonclock_np, based on tst-cond11.c
+ Copyright (C) 2003-2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Mike Crowe <***@mcrowe.com>, 2015.
+
+ 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/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+
+#if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0
+static int
+run_test (clockid_t attr_clock, clockid_t wait_clock)
+{
+ pthread_condattr_t condattr;
+ pthread_cond_t cond;
+ pthread_mutexattr_t mutattr;
+ pthread_mutex_t mut;
+
+ printf ("attr_clock = %d, wait_clock = %d\n", (int) attr_clock, (int) wait_clock);
+
+ if (pthread_condattr_init (&condattr) != 0)
+ {
+ puts ("condattr_init failed");
+ return 1;
+ }
+
+ if (pthread_condattr_setclock (&condattr, attr_clock) != 0)
+ {
+ puts ("condattr_setclock failed");
+ return 1;
+ }
+
+ clockid_t cl2;
+ if (pthread_condattr_getclock (&condattr, &cl2) != 0)
+ {
+ puts ("condattr_getclock failed");
+ return 1;
+ }
+ if (attr_clock != cl2)
+ {
+ printf ("condattr_getclock returned wrong value: %d, expected %d\n",
+ (int) cl2, (int) attr_clock);
+ return 1;
+ }
+
+ if (pthread_cond_init (&cond, &condattr) != 0)
+ {
+ puts ("cond_init failed");
+ return 1;
+ }
+
+ if (pthread_condattr_destroy (&condattr) != 0)
+ {
+ puts ("condattr_destroy failed");
+ return 1;
+ }
+
+ if (pthread_mutexattr_init (&mutattr) != 0)
+ {
+ puts ("mutexattr_init failed");
+ return 1;
+ }
+
+ if (pthread_mutexattr_settype (&mutattr, PTHREAD_MUTEX_ERRORCHECK) != 0)
+ {
+ puts ("mutexattr_settype failed");
+ return 1;
+ }
+
+ if (pthread_mutex_init (&mut, &mutattr) != 0)
+ {
+ puts ("mutex_init failed");
+ return 1;
+ }
+
+ if (pthread_mutexattr_destroy (&mutattr) != 0)
+ {
+ puts ("mutexattr_destroy failed");
+ return 1;
+ }
+
+ if (pthread_mutex_lock (&mut) != 0)
+ {
+ puts ("mutex_lock failed");
+ return 1;
+ }
+
+ if (pthread_mutex_lock (&mut) != EDEADLK)
+ {
+ puts ("2nd mutex_lock did not return EDEADLK");
+ return 1;
+ }
+
+ struct timespec ts;
+ if (clock_gettime (wait_clock, &ts) != 0)
+ {
+ puts ("clock_gettime failed");
+ return 1;
+ }
+
+ /* Wait one second. */
+ ++ts.tv_sec;
+
+ int e = pthread_cond_timedwaitonclock_np (&cond, &mut, wait_clock, &ts);
+ if (e == 0)
+ {
+ puts ("cond_timedwait succeeded");
+ return 1;
+ }
+ else if (e != ETIMEDOUT)
+ {
+ puts ("cond_timedwait did not return ETIMEDOUT");
+ return 1;
+ }
+
+ struct timespec ts2;
+ if (clock_gettime (wait_clock, &ts2) != 0)
+ {
+ puts ("second clock_gettime failed");
+ return 1;
+ }
+
+ if (ts2.tv_sec < ts.tv_sec
+ || (ts2.tv_sec == ts.tv_sec && ts2.tv_nsec < ts.tv_nsec))
+ {
+ puts ("timeout too short");
+ return 1;
+ }
+
+ if (pthread_mutex_unlock (&mut) != 0)
+ {
+ puts ("mutex_unlock failed");
+ return 1;
+ }
+
+ if (pthread_mutex_destroy (&mut) != 0)
+ {
+ puts ("mutex_destroy failed");
+ return 1;
+ }
+
+ if (pthread_cond_destroy (&cond) != 0)
+ {
+ puts ("cond_destroy failed");
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int
+do_test (void)
+{
+#if !defined _POSIX_CLOCK_SELECTION || _POSIX_CLOCK_SELECTION == -1
+
+ puts ("_POSIX_CLOCK_SELECTION not supported, test skipped");
+ return 0;
+
+#else
+
+ int res = run_test (CLOCK_REALTIME, CLOCK_REALTIME);
+
+# if defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0
+# if _POSIX_MONOTONIC_CLOCK == 0
+ int e = sysconf (_SC_MONOTONIC_CLOCK);
+ if (e < 0)
+ puts ("CLOCK_MONOTONIC not supported");
+ else if (e == 0)
+ {
+ puts ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0");
+ res = 1;
+ }
+ else
+# endif
+ {
+ res |= run_test (CLOCK_REALTIME, CLOCK_MONOTONIC);
+ res |= run_test (CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+ res |= run_test (CLOCK_MONOTONIC, CLOCK_REALTIME);
+ }
+# else
+ puts ("_POSIX_MONOTONIC_CLOCK not defined");
+# endif
+
+ return res;
+#endif
+}
+
+#define TIMEOUT 12
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nptl/tst-cond5-onclock.c b/nptl/tst-cond5-onclock.c
new file mode 100644
index 0000000000..adb3342781
--- /dev/null
+++ b/nptl/tst-cond5-onclock.c
@@ -0,0 +1,114 @@
+/* Test pthread_cond_timedwaitonclock_np, based on tst-cond5.c
+ Copyright (C) 2002-2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Mike Crowe <***@mcrowe.com>, 2015.
+
+ 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/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+
+static pthread_mutex_t mut;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+
+static int
+do_test_onclock(clockid_t clockid)
+{
+ pthread_mutexattr_t ma;
+ int err;
+ struct timespec ts;
+
+ if (pthread_mutexattr_init (&ma) != 0)
+ {
+ puts ("mutexattr_init failed");
+ exit (1);
+ }
+
+ if (pthread_mutexattr_settype (&ma, PTHREAD_MUTEX_ERRORCHECK) != 0)
+ {
+ puts ("mutexattr_settype failed");
+ exit (1);
+ }
+
+ if (pthread_mutex_init (&mut, &ma) != 0)
+ {
+ puts ("mutex_init failed");
+ exit (1);
+ }
+
+ /* Get the mutex. */
+ if (pthread_mutex_lock (&mut) != 0)
+ {
+ puts ("mutex_lock failed");
+ exit (1);
+ }
+
+ /* Waiting for the condition will fail. But we want the timeout here. */
+ if (clock_gettime (clockid, &ts) != 0)
+ {
+ puts ("clock_gettime failed");
+ exit (1);
+ }
+
+ ts.tv_nsec += 500000000;
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ++ts.tv_sec;
+ }
+ err = pthread_cond_timedwaitonclock_np (&cond, &mut, clockid, &ts);
+ if (err == 0)
+ {
+ /* This could in theory happen but here without any signal and
+ additional waiter it should not. */
+ puts ("cond_timedwait succeeded");
+ exit (1);
+ }
+ else if (err != ETIMEDOUT)
+ {
+ printf ("cond_timedwait returned with %s\n", strerror (err));
+ exit (1);
+ }
+
+ err = pthread_mutex_unlock (&mut);
+ if (err != 0)
+ {
+ printf ("mutex_unlock failed: %s\n", strerror (err));
+ exit (1);
+ }
+
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ int rc;
+ rc = do_test_onclock(CLOCK_MONOTONIC);
+ if (rc == 0)
+ rc = do_test_onclock(CLOCK_REALTIME);
+
+ return rc;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/nptl/pthread-functions.h b/sysdeps/nptl/pthread-functions.h
index 4006fc6c25..8225fdbfca 100644
--- a/sysdeps/nptl/pthread-functions.h
+++ b/sysdeps/nptl/pthread-functions.h
@@ -55,6 +55,10 @@ struct pthread_functions
int (*ptr___pthread_cond_wait) (pthread_cond_t *, pthread_mutex_t *);
int (*ptr___pthread_cond_timedwait) (pthread_cond_t *, pthread_mutex_t *,
const struct timespec *);
+ int (*ptr___pthread_cond_timedwaitonclock_np) (pthread_cond_t *,
+ pthread_mutex_t *,
+ clockid_t,
+ const struct timespec *);
int (*ptr___pthread_cond_broadcast_2_0) (pthread_cond_2_0_t *);
int (*ptr___pthread_cond_destroy_2_0) (pthread_cond_2_0_t *);
int (*ptr___pthread_cond_init_2_0) (pthread_cond_2_0_t *,
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 632ea7bc36..e95df8f592 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -1003,6 +1003,21 @@ extern int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
const struct timespec *__restrict __abstime)
__nonnull ((1, 2, 3));
+#ifdef __USE_GNU
+/* Wait for condition variable COND to be signaled or broadcast until
+ ABSTIME measured by the specified clock. MUTEX is assumed to be
+ locked before. CLOCK is the clock to use. ABSTIME is an absolute
+ time specification against CLOCK's epoch.
+
+ This function is a cancellation point and therefore not marked with
+ __THROW. */
+extern int pthread_cond_timedwaitonclock_np (pthread_cond_t *__restrict __cond,
+ pthread_mutex_t *__restrict __mutex,
+ __clockid_t __clock_id,
+ const struct timespec *__restrict __abstime)
+ __nonnull ((1, 2, 4));
+#endif
+
/* Functions for handling condition variable attributes. */
/* Initialize condition variable attribute ATTR. */
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index d2a206a8df..b0ecb71d3b 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -101,6 +101,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/arm/libpthread.abilist b/sysdeps/unix/sysv/linux/arm/libpthread.abilist
index 91545c1542..32b3331c19 100644
--- a/sysdeps/unix/sysv/linux/arm/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libpthread.abilist
@@ -9,6 +9,7 @@ GLIBC_2.12 pthread_setname_np F
GLIBC_2.18 GLIBC_2.18 A
GLIBC_2.18 pthread_getattr_default_np F
GLIBC_2.18 pthread_setattr_default_np F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.4 GLIBC_2.4 A
GLIBC_2.4 _IO_flockfile F
GLIBC_2.4 _IO_ftrylockfile F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 2ff1998ac9..1bcf512fa1 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2023,6 +2023,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/i386/libpthread.abilist b/sysdeps/unix/sysv/linux/i386/libpthread.abilist
index 8f9c3254be..3ae0e37475 100644
--- a/sysdeps/unix/sysv/linux/i386/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libpthread.abilist
@@ -216,6 +216,7 @@ GLIBC_2.2.3 GLIBC_2.2.3 A
GLIBC_2.2.3 pthread_getattr_np F
GLIBC_2.2.6 GLIBC_2.2.6 A
GLIBC_2.2.6 __nanosleep F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.3.2 GLIBC_2.3.2 A
GLIBC_2.3.2 pthread_cond_broadcast F
GLIBC_2.3.2 pthread_cond_destroy F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist
index 91545c1542..32b3331c19 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist
@@ -9,6 +9,7 @@ GLIBC_2.12 pthread_setname_np F
GLIBC_2.18 GLIBC_2.18 A
GLIBC_2.18 pthread_getattr_default_np F
GLIBC_2.18 pthread_setattr_default_np F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.4 GLIBC_2.4 A
GLIBC_2.4 _IO_flockfile F
GLIBC_2.4 _IO_ftrylockfile F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist
index 8f9c3254be..3ae0e37475 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist
@@ -216,6 +216,7 @@ GLIBC_2.2.3 GLIBC_2.2.3 A
GLIBC_2.2.3 pthread_getattr_np F
GLIBC_2.2.6 GLIBC_2.2.6 A
GLIBC_2.2.6 __nanosleep F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.3.2 GLIBC_2.3.2 A
GLIBC_2.3.2 pthread_cond_broadcast F
GLIBC_2.3.2 pthread_cond_destroy F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 513524d932..13f13e04db 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1864,6 +1864,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
index 85365c057c..accb1d8d6f 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
@@ -203,6 +203,8 @@ GLIBC_2.2.5 waitpid F
GLIBC_2.2.5 write F
GLIBC_2.2.6 GLIBC_2.2.6 A
GLIBC_2.2.6 __nanosleep F
+GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.3.2 GLIBC_2.3.2 A
GLIBC_2.3.2 pthread_cond_broadcast F
GLIBC_2.3.2 pthread_cond_destroy F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0c557e9f43..db930f6307 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2107,6 +2107,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist
index 6cd0fc3487..d89762bd33 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist
@@ -224,3 +224,4 @@ GLIBC_2.16 write F
GLIBC_2.18 GLIBC_2.18 A
GLIBC_2.18 pthread_getattr_default_np F
GLIBC_2.18 pthread_setattr_default_np F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
at the time of the wait and permits separate waits on the same condition
variable instance using different clocks.
Unfortunately pthread_cond_timedwait always uses the clock that was
specified (or defaulted) when pthread_cond_init was called. libstdc++'s
current implementation converts all waits to
std::chrono::system_clock (i.e. CLOCK_REALTIME) which can race against
the system clock changing.
Let's invent a brand-new function pthread_cond_timedwaitonclock_np which
accepts both the clock and the time point as parameters is straightforward
and means that the C++11 standard behaviour can be implemented in libstdc++
on Linux at least.
Earlier versions of this patch were proposed in 2015 but further progress
stalled waiting for the assembly implementation of pthread_cond_timedwait.S
to be removed by Torvald Riegel. This happened in
ed19993b5b0d05d62cc883571519a67dae481a14.
See the following threads for previous versions and discussion:
* https://sourceware.org/ml/libc-alpha/2015-07/msg00193.html
* https://sourceware.org/ml/libc-alpha/2015-08/msg00186.html
* https://sourceware.org/ml/libc-alpha/2015-08/msg00230.html
Some of the abilist updates for other architectures appear to have gone
missing during the rebase. If this change is reviewed favourably then I
shall try to include them.
Signed-off-by: Mike Crowe <***@mcrowe.com>
---
ChangeLog | 48 +++++
manual/threads.texi | 21 +++
nptl/Makefile | 4 +-
nptl/Versions | 7 +
nptl/forward.c | 5 +
nptl/nptl-init.c | 1 +
nptl/pthreadP.h | 4 +
nptl/pthread_cond_wait.c | 28 ++-
nptl/tst-cond11-onclock.c | 209 +++++++++++++++++++++
nptl/tst-cond5-onclock.c | 114 +++++++++++
sysdeps/nptl/pthread-functions.h | 4 +
sysdeps/nptl/pthread.h | 15 ++
sysdeps/unix/sysv/linux/arm/libc.abilist | 1 +
sysdeps/unix/sysv/linux/arm/libpthread.abilist | 1 +
sysdeps/unix/sysv/linux/i386/libc.abilist | 1 +
sysdeps/unix/sysv/linux/i386/libpthread.abilist | 1 +
.../sysv/linux/m68k/coldfire/libpthread.abilist | 1 +
.../unix/sysv/linux/m68k/m680x0/libpthread.abilist | 1 +
sysdeps/unix/sysv/linux/x86_64/64/libc.abilist | 1 +
.../unix/sysv/linux/x86_64/64/libpthread.abilist | 2 +
sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist | 1 +
.../unix/sysv/linux/x86_64/x32/libpthread.abilist | 1 +
22 files changed, 466 insertions(+), 5 deletions(-)
create mode 100644 nptl/tst-cond11-onclock.c
create mode 100644 nptl/tst-cond5-onclock.c
diff --git a/ChangeLog b/ChangeLog
index 46a70ff7ce..385deb472f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,51 @@
+2017-06-22 Mike Crowe <***@mcrowe.com>
+
+ * sysdeps/nptl/pthread.h: Add pthread_cond_timedwaitonclock_np
+ declaration
+ * nptl/pthread_cond.c:
+ (__pthread_cond_wait_common): Add clockid parameter to determine
+ which clock to use.
+ (__pthread_cond_wait): Add dummy clockid parameter when calling
+ __pthread_cond_wait_common.
+ (__pthread_cond_timedwait): Read default clock set during
+ pthread_cond_init to supply as clockid parameter in call to
+ __pthread_cond_wait_common.
+ (__pthread_cond_timedwaitonclock_np): New function that supports
+ specifying clock to use at call time rather than initialisation
+ time.
+ (pthread_cond_timdwaitonclock_np): Weak alias for
+ __pthread_cond_timedwaitonclock_np
+ * nptl/tst-cond5-onclock.c: New file to test
+ pthread_cond_timedwaitonclock_np
+ * nptl/tst-cond11-onclock.c: New file to test
+ pthread_cond_timedwaitonclock_np
+ * nptl/Makefile: Add tst-cond5-onclock and tst-cond11-onclock
+ * nptl/Versions (GLIBC_2.26): Add pthread_cond_timedwaitonclock_np
+ to both libc and libpthread
+ * nptl/pthread_functions.h: Add
+ ptr___pthread_cond_timedwaitonclock_np
+ * nptl/forward.c (pthread_functions): Forward
+ pthread_cond_timedwaitonclock_np from libc to libpthread3
+ * nptl/nptl-init.c: Forwarding function initialisation for
+ pthread_cond_timedwaitonclock_np
+ * nptl/pthreadP.h (__pthread_cond_timedwait_np): Add
+ * manual/threads.texi: Add documentation for
+ pthread_cond_timedwaitonclock_np
+ * sysdeps/unix/sysv/linux/aarch64/libc.abilist: Add
+ pthread_cond_timedwaitonclock_np
+ * sysdeps/unix/sysv/linux/arm/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/arm/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/i386/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/i386/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/64/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist: Likewise
+ * sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist: Likewise
+
2017-06-21 Florian Weimer <***@redhat.com>
* intl/loadmsgcat.c: Remove alloca support.
diff --git a/manual/threads.texi b/manual/threads.texi
index 769d974d50..8b3652c859 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -124,6 +124,27 @@ The system does not have sufficient memory.
@end table
@end deftypefun
+@comment pthread.h
+@comment GNU
+@deftypefun int pthread_cond_timedwaitonclock_np (pthread_cond_t *@var{cond}, pthread_mutex_t *@var{mutex},
+ clockid_t @var{clockid}, const struct timespec *@var{abstime})
+@safety{@prelim{}@mtsafe{}@asunsafe{@asulock{}}@acunsafe{@aculock}}
+@c If exactly the same function with arguments is called from a signal
+@c handler that interrupts between the mutex unlock and sleep then it
+@c will unlock the mutex twice resulting in undefined behaviour. Keep
+@c in mind that the unlock and sleep are only atomic with respect to other
+@c threads (really a happens-after relationship for pthread_cond_broadcast
+@c and pthread_cond_signal).
+@c In the AC case we would cancel the thread and the mutex would remain
+@c locked and we can't recover from that.
+Behaves like POSIX @code{pthread_cond_timedwait} except the time
+@var{abstime} is measured against the clock specified by @var{clockid}
+rather than the clock specified or defaulted when @code{pthread_cond_init}
+was called. Currently, @code{clockid} must be either @code{CLOCK_MONOTONIC}
+or @code{CLOCK_REALTIME}. This variant is necessary in order to implement
+C++11's @code{std::condition_variable::wait_until} method correctly.
+@end deftypefun
+
@c FIXME these are undocumented:
@c pthread_atfork
@c pthread_attr_destroy
diff --git a/nptl/Makefile b/nptl/Makefile
index 853da72e74..8cf661dc64 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -235,8 +235,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
tst-mutexpi5a tst-mutexpi6 tst-mutexpi7 tst-mutexpi7a \
tst-mutexpi9 \
tst-spin1 tst-spin2 tst-spin3 tst-spin4 \
- tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond6 tst-cond7 \
- tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond12 tst-cond13 \
+ tst-cond1 tst-cond2 tst-cond3 tst-cond4 tst-cond5 tst-cond5-onclock tst-cond6 tst-cond7 \
+ tst-cond8 tst-cond9 tst-cond10 tst-cond11 tst-cond11-onclock tst-cond12 tst-cond13 \
tst-cond14 tst-cond15 tst-cond16 tst-cond17 tst-cond18 tst-cond19 \
tst-cond20 tst-cond21 tst-cond22 tst-cond23 tst-cond24 tst-cond25 \
tst-cond-except \
diff --git a/nptl/Versions b/nptl/Versions
index 0ae5def464..1cf8c2fbad 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -28,6 +28,9 @@ libc {
pthread_cond_wait; pthread_cond_signal;
pthread_cond_broadcast; pthread_cond_timedwait;
}
+ GLIBC_2.26 {
+ pthread_cond_timedwaitonclock_np;
+ }
GLIBC_PRIVATE {
__libc_alloca_cutoff;
# Internal libc interface to libpthread
@@ -265,6 +268,10 @@ libpthread {
GLIBC_2.22 {
}
+ GLIBC_2.26 {
+ pthread_cond_timedwaitonclock_np;
+ }
+
GLIBC_PRIVATE {
__pthread_initialize_minimal;
__pthread_clock_gettime; __pthread_clock_settime;
diff --git a/nptl/forward.c b/nptl/forward.c
index ac96765f29..c5fcb2f066 100644
--- a/nptl/forward.c
+++ b/nptl/forward.c
@@ -164,6 +164,11 @@ FORWARD (__pthread_cond_timedwait,
const struct timespec *abstime), (cond, mutex, abstime), 0)
versioned_symbol (libc, __pthread_cond_timedwait, pthread_cond_timedwait,
GLIBC_2_3_2);
+FORWARD (__pthread_cond_timedwaitonclock_np,
+ (pthread_cond_t *cond, pthread_mutex_t *mutex, clockid_t clockid,
+ const struct timespec *abstime), (cond, mutex, clockid, abstime),
+ 0)
+weak_alias (__pthread_cond_timedwaitonclock_np, pthread_cond_timedwaitonclock_np);
FORWARD (pthread_equal, (pthread_t thread1, pthread_t thread2),
diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
index 29216077a2..b913515fe9 100644
--- a/nptl/nptl-init.c
+++ b/nptl/nptl-init.c
@@ -106,6 +106,7 @@ static const struct pthread_functions pthread_functions =
.ptr___pthread_cond_signal = __pthread_cond_signal,
.ptr___pthread_cond_wait = __pthread_cond_wait,
.ptr___pthread_cond_timedwait = __pthread_cond_timedwait,
+ .ptr___pthread_cond_timedwaitonclock_np = __pthread_cond_timedwaitonclock_np,
# if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_3_2)
.ptr___pthread_cond_broadcast_2_0 = __pthread_cond_broadcast_2_0,
.ptr___pthread_cond_destroy_2_0 = __pthread_cond_destroy_2_0,
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index 7fc1e50f78..c7e7587479 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -488,6 +488,10 @@ extern int __pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
extern int __pthread_cond_timedwait (pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *abstime);
+extern int __pthread_cond_timedwaitonclock_np (pthread_cond_t *cond,
+ pthread_mutex_t *mutex,
+ clockid_t clockid,
+ const struct timespec *abstime);
extern int __pthread_condattr_destroy (pthread_condattr_t *attr);
extern int __pthread_condattr_init (pthread_condattr_t *attr);
extern int __pthread_key_create (pthread_key_t *key, void (*destr) (void *));
diff --git a/nptl/pthread_cond_wait.c b/nptl/pthread_cond_wait.c
index 7812b94a3a..d0166b86d5 100644
--- a/nptl/pthread_cond_wait.c
+++ b/nptl/pthread_cond_wait.c
@@ -378,6 +378,7 @@ __condvar_cleanup_waiting (void *arg)
*/
static __always_inline int
__pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ clockid_t clockid,
const struct timespec *abstime)
{
const int maxspin = 0;
@@ -510,7 +511,7 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
if (__glibc_unlikely (abstime->tv_sec < 0))
err = ETIMEDOUT;
- else if ((flags & __PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0)
+ else if (clockid == CLOCK_MONOTONIC)
{
/* CLOCK_MONOTONIC is requested. */
struct timespec rt;
@@ -652,7 +653,7 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
int
__pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
{
- return __pthread_cond_wait_common (cond, mutex, NULL);
+ return __pthread_cond_wait_common (cond, mutex, CLOCK_REALTIME, NULL);
}
/* See __pthread_cond_wait_common. */
@@ -664,10 +665,31 @@ __pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex,
it can assume that abstime is not NULL. */
if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
return EINVAL;
- return __pthread_cond_wait_common (cond, mutex, abstime);
+
+ clockid_t clockid = ((cond->__data.__wrefs & __PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+ return __pthread_cond_wait_common (cond, mutex, clockid, abstime);
+}
+
+/* See __pthread_cond_wait_common. */
+int
+__pthread_cond_timedwaitonclock_np (pthread_cond_t *cond, pthread_mutex_t *mutex,
+ clockid_t clockid,
+ const struct timespec *abstime)
+{
+ /* Check parameter validity. This should also tell the compiler that
+ it can assume that abstime is not NULL. */
+ if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
+ return EINVAL;
+
+ /* We only support CLOCK_REALTIME and CLOCK_MONOTONIC */
+ if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+ return EINVAL;
+
+ return __pthread_cond_wait_common (cond, mutex, clockid, abstime);
}
versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
GLIBC_2_3_2);
versioned_symbol (libpthread, __pthread_cond_timedwait, pthread_cond_timedwait,
GLIBC_2_3_2);
+weak_alias (__pthread_cond_timedwaitonclock_np, pthread_cond_timedwaitonclock_np);
diff --git a/nptl/tst-cond11-onclock.c b/nptl/tst-cond11-onclock.c
new file mode 100644
index 0000000000..439cd404de
--- /dev/null
+++ b/nptl/tst-cond11-onclock.c
@@ -0,0 +1,209 @@
+/* Test pthread_cond_timedwaitonclock_np, based on tst-cond11.c
+ Copyright (C) 2003-2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Mike Crowe <***@mcrowe.com>, 2015.
+
+ 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/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+
+#if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0
+static int
+run_test (clockid_t attr_clock, clockid_t wait_clock)
+{
+ pthread_condattr_t condattr;
+ pthread_cond_t cond;
+ pthread_mutexattr_t mutattr;
+ pthread_mutex_t mut;
+
+ printf ("attr_clock = %d, wait_clock = %d\n", (int) attr_clock, (int) wait_clock);
+
+ if (pthread_condattr_init (&condattr) != 0)
+ {
+ puts ("condattr_init failed");
+ return 1;
+ }
+
+ if (pthread_condattr_setclock (&condattr, attr_clock) != 0)
+ {
+ puts ("condattr_setclock failed");
+ return 1;
+ }
+
+ clockid_t cl2;
+ if (pthread_condattr_getclock (&condattr, &cl2) != 0)
+ {
+ puts ("condattr_getclock failed");
+ return 1;
+ }
+ if (attr_clock != cl2)
+ {
+ printf ("condattr_getclock returned wrong value: %d, expected %d\n",
+ (int) cl2, (int) attr_clock);
+ return 1;
+ }
+
+ if (pthread_cond_init (&cond, &condattr) != 0)
+ {
+ puts ("cond_init failed");
+ return 1;
+ }
+
+ if (pthread_condattr_destroy (&condattr) != 0)
+ {
+ puts ("condattr_destroy failed");
+ return 1;
+ }
+
+ if (pthread_mutexattr_init (&mutattr) != 0)
+ {
+ puts ("mutexattr_init failed");
+ return 1;
+ }
+
+ if (pthread_mutexattr_settype (&mutattr, PTHREAD_MUTEX_ERRORCHECK) != 0)
+ {
+ puts ("mutexattr_settype failed");
+ return 1;
+ }
+
+ if (pthread_mutex_init (&mut, &mutattr) != 0)
+ {
+ puts ("mutex_init failed");
+ return 1;
+ }
+
+ if (pthread_mutexattr_destroy (&mutattr) != 0)
+ {
+ puts ("mutexattr_destroy failed");
+ return 1;
+ }
+
+ if (pthread_mutex_lock (&mut) != 0)
+ {
+ puts ("mutex_lock failed");
+ return 1;
+ }
+
+ if (pthread_mutex_lock (&mut) != EDEADLK)
+ {
+ puts ("2nd mutex_lock did not return EDEADLK");
+ return 1;
+ }
+
+ struct timespec ts;
+ if (clock_gettime (wait_clock, &ts) != 0)
+ {
+ puts ("clock_gettime failed");
+ return 1;
+ }
+
+ /* Wait one second. */
+ ++ts.tv_sec;
+
+ int e = pthread_cond_timedwaitonclock_np (&cond, &mut, wait_clock, &ts);
+ if (e == 0)
+ {
+ puts ("cond_timedwait succeeded");
+ return 1;
+ }
+ else if (e != ETIMEDOUT)
+ {
+ puts ("cond_timedwait did not return ETIMEDOUT");
+ return 1;
+ }
+
+ struct timespec ts2;
+ if (clock_gettime (wait_clock, &ts2) != 0)
+ {
+ puts ("second clock_gettime failed");
+ return 1;
+ }
+
+ if (ts2.tv_sec < ts.tv_sec
+ || (ts2.tv_sec == ts.tv_sec && ts2.tv_nsec < ts.tv_nsec))
+ {
+ puts ("timeout too short");
+ return 1;
+ }
+
+ if (pthread_mutex_unlock (&mut) != 0)
+ {
+ puts ("mutex_unlock failed");
+ return 1;
+ }
+
+ if (pthread_mutex_destroy (&mut) != 0)
+ {
+ puts ("mutex_destroy failed");
+ return 1;
+ }
+
+ if (pthread_cond_destroy (&cond) != 0)
+ {
+ puts ("cond_destroy failed");
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+
+static int
+do_test (void)
+{
+#if !defined _POSIX_CLOCK_SELECTION || _POSIX_CLOCK_SELECTION == -1
+
+ puts ("_POSIX_CLOCK_SELECTION not supported, test skipped");
+ return 0;
+
+#else
+
+ int res = run_test (CLOCK_REALTIME, CLOCK_REALTIME);
+
+# if defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0
+# if _POSIX_MONOTONIC_CLOCK == 0
+ int e = sysconf (_SC_MONOTONIC_CLOCK);
+ if (e < 0)
+ puts ("CLOCK_MONOTONIC not supported");
+ else if (e == 0)
+ {
+ puts ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0");
+ res = 1;
+ }
+ else
+# endif
+ {
+ res |= run_test (CLOCK_REALTIME, CLOCK_MONOTONIC);
+ res |= run_test (CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+ res |= run_test (CLOCK_MONOTONIC, CLOCK_REALTIME);
+ }
+# else
+ puts ("_POSIX_MONOTONIC_CLOCK not defined");
+# endif
+
+ return res;
+#endif
+}
+
+#define TIMEOUT 12
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nptl/tst-cond5-onclock.c b/nptl/tst-cond5-onclock.c
new file mode 100644
index 0000000000..adb3342781
--- /dev/null
+++ b/nptl/tst-cond5-onclock.c
@@ -0,0 +1,114 @@
+/* Test pthread_cond_timedwaitonclock_np, based on tst-cond5.c
+ Copyright (C) 2002-2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Mike Crowe <***@mcrowe.com>, 2015.
+
+ 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/>. */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+
+static pthread_mutex_t mut;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+
+static int
+do_test_onclock(clockid_t clockid)
+{
+ pthread_mutexattr_t ma;
+ int err;
+ struct timespec ts;
+
+ if (pthread_mutexattr_init (&ma) != 0)
+ {
+ puts ("mutexattr_init failed");
+ exit (1);
+ }
+
+ if (pthread_mutexattr_settype (&ma, PTHREAD_MUTEX_ERRORCHECK) != 0)
+ {
+ puts ("mutexattr_settype failed");
+ exit (1);
+ }
+
+ if (pthread_mutex_init (&mut, &ma) != 0)
+ {
+ puts ("mutex_init failed");
+ exit (1);
+ }
+
+ /* Get the mutex. */
+ if (pthread_mutex_lock (&mut) != 0)
+ {
+ puts ("mutex_lock failed");
+ exit (1);
+ }
+
+ /* Waiting for the condition will fail. But we want the timeout here. */
+ if (clock_gettime (clockid, &ts) != 0)
+ {
+ puts ("clock_gettime failed");
+ exit (1);
+ }
+
+ ts.tv_nsec += 500000000;
+ if (ts.tv_nsec >= 1000000000)
+ {
+ ts.tv_nsec -= 1000000000;
+ ++ts.tv_sec;
+ }
+ err = pthread_cond_timedwaitonclock_np (&cond, &mut, clockid, &ts);
+ if (err == 0)
+ {
+ /* This could in theory happen but here without any signal and
+ additional waiter it should not. */
+ puts ("cond_timedwait succeeded");
+ exit (1);
+ }
+ else if (err != ETIMEDOUT)
+ {
+ printf ("cond_timedwait returned with %s\n", strerror (err));
+ exit (1);
+ }
+
+ err = pthread_mutex_unlock (&mut);
+ if (err != 0)
+ {
+ printf ("mutex_unlock failed: %s\n", strerror (err));
+ exit (1);
+ }
+
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ int rc;
+ rc = do_test_onclock(CLOCK_MONOTONIC);
+ if (rc == 0)
+ rc = do_test_onclock(CLOCK_REALTIME);
+
+ return rc;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/nptl/pthread-functions.h b/sysdeps/nptl/pthread-functions.h
index 4006fc6c25..8225fdbfca 100644
--- a/sysdeps/nptl/pthread-functions.h
+++ b/sysdeps/nptl/pthread-functions.h
@@ -55,6 +55,10 @@ struct pthread_functions
int (*ptr___pthread_cond_wait) (pthread_cond_t *, pthread_mutex_t *);
int (*ptr___pthread_cond_timedwait) (pthread_cond_t *, pthread_mutex_t *,
const struct timespec *);
+ int (*ptr___pthread_cond_timedwaitonclock_np) (pthread_cond_t *,
+ pthread_mutex_t *,
+ clockid_t,
+ const struct timespec *);
int (*ptr___pthread_cond_broadcast_2_0) (pthread_cond_2_0_t *);
int (*ptr___pthread_cond_destroy_2_0) (pthread_cond_2_0_t *);
int (*ptr___pthread_cond_init_2_0) (pthread_cond_2_0_t *,
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 632ea7bc36..e95df8f592 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -1003,6 +1003,21 @@ extern int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
const struct timespec *__restrict __abstime)
__nonnull ((1, 2, 3));
+#ifdef __USE_GNU
+/* Wait for condition variable COND to be signaled or broadcast until
+ ABSTIME measured by the specified clock. MUTEX is assumed to be
+ locked before. CLOCK is the clock to use. ABSTIME is an absolute
+ time specification against CLOCK's epoch.
+
+ This function is a cancellation point and therefore not marked with
+ __THROW. */
+extern int pthread_cond_timedwaitonclock_np (pthread_cond_t *__restrict __cond,
+ pthread_mutex_t *__restrict __mutex,
+ __clockid_t __clock_id,
+ const struct timespec *__restrict __abstime)
+ __nonnull ((1, 2, 4));
+#endif
+
/* Functions for handling condition variable attributes. */
/* Initialize condition variable attribute ATTR. */
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index d2a206a8df..b0ecb71d3b 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -101,6 +101,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/arm/libpthread.abilist b/sysdeps/unix/sysv/linux/arm/libpthread.abilist
index 91545c1542..32b3331c19 100644
--- a/sysdeps/unix/sysv/linux/arm/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libpthread.abilist
@@ -9,6 +9,7 @@ GLIBC_2.12 pthread_setname_np F
GLIBC_2.18 GLIBC_2.18 A
GLIBC_2.18 pthread_getattr_default_np F
GLIBC_2.18 pthread_setattr_default_np F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.4 GLIBC_2.4 A
GLIBC_2.4 _IO_flockfile F
GLIBC_2.4 _IO_ftrylockfile F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 2ff1998ac9..1bcf512fa1 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2023,6 +2023,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/i386/libpthread.abilist b/sysdeps/unix/sysv/linux/i386/libpthread.abilist
index 8f9c3254be..3ae0e37475 100644
--- a/sysdeps/unix/sysv/linux/i386/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libpthread.abilist
@@ -216,6 +216,7 @@ GLIBC_2.2.3 GLIBC_2.2.3 A
GLIBC_2.2.3 pthread_getattr_np F
GLIBC_2.2.6 GLIBC_2.2.6 A
GLIBC_2.2.6 __nanosleep F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.3.2 GLIBC_2.3.2 A
GLIBC_2.3.2 pthread_cond_broadcast F
GLIBC_2.3.2 pthread_cond_destroy F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist
index 91545c1542..32b3331c19 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libpthread.abilist
@@ -9,6 +9,7 @@ GLIBC_2.12 pthread_setname_np F
GLIBC_2.18 GLIBC_2.18 A
GLIBC_2.18 pthread_getattr_default_np F
GLIBC_2.18 pthread_setattr_default_np F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.4 GLIBC_2.4 A
GLIBC_2.4 _IO_flockfile F
GLIBC_2.4 _IO_ftrylockfile F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist
index 8f9c3254be..3ae0e37475 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libpthread.abilist
@@ -216,6 +216,7 @@ GLIBC_2.2.3 GLIBC_2.2.3 A
GLIBC_2.2.3 pthread_getattr_np F
GLIBC_2.2.6 GLIBC_2.2.6 A
GLIBC_2.2.6 __nanosleep F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.3.2 GLIBC_2.3.2 A
GLIBC_2.3.2 pthread_cond_broadcast F
GLIBC_2.3.2 pthread_cond_destroy F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 513524d932..13f13e04db 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1864,6 +1864,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
index 85365c057c..accb1d8d6f 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libpthread.abilist
@@ -203,6 +203,8 @@ GLIBC_2.2.5 waitpid F
GLIBC_2.2.5 write F
GLIBC_2.2.6 GLIBC_2.2.6 A
GLIBC_2.2.6 __nanosleep F
+GLIBC_2.26 GLIBC_2.26 A
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.3.2 GLIBC_2.3.2 A
GLIBC_2.3.2 pthread_cond_broadcast F
GLIBC_2.3.2 pthread_cond_destroy F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0c557e9f43..db930f6307 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2107,6 +2107,7 @@ GLIBC_2.25 strfroml F
GLIBC_2.26 GLIBC_2.26 A
GLIBC_2.26 preadv2 F
GLIBC_2.26 preadv64v2 F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
GLIBC_2.26 pwritev2 F
GLIBC_2.26 pwritev64v2 F
GLIBC_2.26 reallocarray F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist
index 6cd0fc3487..d89762bd33 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libpthread.abilist
@@ -224,3 +224,4 @@ GLIBC_2.16 write F
GLIBC_2.18 GLIBC_2.18 A
GLIBC_2.18 pthread_getattr_default_np F
GLIBC_2.18 pthread_setattr_default_np F
+GLIBC_2.26 pthread_cond_timedwaitonclock_np F
--
2.11.0
2.11.0