about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
Diffstat (limited to 'nptl')
-rw-r--r--nptl/Makefile1
-rw-r--r--nptl/Versions2
-rw-r--r--nptl/forward.c5
-rw-r--r--nptl/nptl-init.c1
-rw-r--r--nptl/pthreadP.h5
-rw-r--r--nptl/pthread_cond_wait.c44
-rw-r--r--nptl/tst-cond11.c38
-rw-r--r--nptl/tst-cond26.c77
-rw-r--r--nptl/tst-cond27.c66
9 files changed, 221 insertions, 18 deletions
diff --git a/nptl/Makefile b/nptl/Makefile
index fefdeb3be4..76bfd89e23 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -251,6 +251,7 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 	tst-cond8 tst-cond9 tst-cond10 tst-cond11 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-cond26 tst-cond27 \
 	tst-cond-except \
 	tst-robust1 tst-robust2 tst-robust3 tst-robust4 tst-robust5 \
 	tst-robust6 tst-robust7 tst-robust8 tst-robust9 \
diff --git a/nptl/Versions b/nptl/Versions
index 4f0e837507..3ab0ae596d 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -277,7 +277,7 @@ libpthread {
   }
 
   GLIBC_2.30 {
-    sem_clockwait;
+    sem_clockwait; pthread_cond_clockwait;
   }
 
   GLIBC_PRIVATE {
diff --git a/nptl/forward.c b/nptl/forward.c
index ed1e7d0350..50f358f2b1 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_clockwait,
+	 (pthread_cond_t *cond, pthread_mutex_t *mutex, clockid_t clockid,
+	  const struct timespec *abstime), (cond, mutex, clockid, abstime),
+	 0)
+weak_alias (__pthread_cond_clockwait, pthread_cond_clockwait);
 
 
 FORWARD (pthread_equal, (pthread_t thread1, pthread_t thread2),
diff --git a/nptl/nptl-init.c b/nptl/nptl-init.c
index 73935f8be4..9c2a3d735c 100644
--- a/nptl/nptl-init.c
+++ b/nptl/nptl-init.c
@@ -95,6 +95,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_clockwait = __pthread_cond_clockwait,
 # 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 66527d8f2d..d80662ab07 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -449,6 +449,11 @@ 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_clockwait (pthread_cond_t *cond,
+				     pthread_mutex_t *mutex,
+				     clockid_t clockid,
+				     const struct timespec *abstime)
+  __nonnull ((1, 2, 4));
 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 7385562f57..558f930cd2 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;
@@ -386,6 +387,11 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
 
   LIBC_PROBE (cond_wait, 2, cond, mutex);
 
+  /* clockid will already have been checked by
+     __pthread_cond_clockwait or pthread_condattr_setclock, or we
+     don't use it if abstime is NULL, so we don't need to check it
+     here. */
+
   /* Acquire a position (SEQ) in the waiter sequence (WSEQ).  We use an
      atomic operation because signals and broadcasts may update the group
      switch without acquiring the mutex.  We do not need release MO here
@@ -511,10 +517,6 @@ __pthread_cond_wait_common (pthread_cond_t *cond, pthread_mutex_t *mutex,
 	        err = ETIMEDOUT;
 	      else
 		{
-		  const clockid_t clockid =
-		    ((flags & __PTHREAD_COND_CLOCK_MONOTONIC_MASK) != 0) ?
-		    CLOCK_MONOTONIC : CLOCK_REALTIME;
-
 		  err = futex_abstimed_wait_cancelable
                     (cond->__data.__g_signals + g, 0, clockid, abstime,
                      private);
@@ -632,7 +634,8 @@ __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);
+  /* clockid is unused when abstime is NULL. */
+  return __pthread_cond_wait_common (cond, mutex, 0, NULL);
 }
 
 /* See __pthread_cond_wait_common.  */
@@ -644,10 +647,39 @@ __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);
+
+  /* Relaxed MO is suffice because clock ID bit is only modified
+     in condition creation.  */
+  unsigned int flags = atomic_load_relaxed (&cond->__data.__wrefs);
+  clockid_t clockid = (flags & __PTHREAD_COND_CLOCK_MONOTONIC_MASK)
+                    ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+  return __pthread_cond_wait_common (cond, mutex, clockid, abstime);
+}
+
+/* See __pthread_cond_wait_common.  */
+int
+__pthread_cond_clockwait (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;
+
+  if (!futex_abstimed_supported_clockid (clockid))
+    return EINVAL;
+
+  /* If we do not support waiting using CLOCK_MONOTONIC, return an error.  */
+  if (clockid == CLOCK_MONOTONIC
+      && !futex_supports_exact_relative_timeouts ())
+    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_clockwait, pthread_cond_clockwait);
diff --git a/nptl/tst-cond11.c b/nptl/tst-cond11.c
index 3bc4ff404f..6ce95aa13b 100644
--- a/nptl/tst-cond11.c
+++ b/nptl/tst-cond11.c
@@ -22,28 +22,32 @@
 #include <time.h>
 #include <unistd.h>
 #include <support/check.h>
+#include <support/test-driver.h>
 #include <support/timespec.h>
 #include <support/xthread.h>
 #include <support/xtime.h>
 
+/* A bogus clock value that tells run_test to use pthread_cond_timedwait
+   rather than pthread_condclockwait.  */
+#define CLOCK_USE_ATTR_CLOCK (-1)
 
 #if defined _POSIX_CLOCK_SELECTION && _POSIX_CLOCK_SELECTION >= 0
 static int
-run_test (clockid_t cl)
+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 ("clock = %d\n", (int) cl);
+  verbose_printf ("attr_clock = %d\n", (int) attr_clock);
 
   TEST_COMPARE (pthread_condattr_init (&condattr), 0);
-  TEST_COMPARE (pthread_condattr_setclock (&condattr, cl), 0);
+  TEST_COMPARE (pthread_condattr_setclock (&condattr, attr_clock), 0);
 
-  clockid_t cl2;
-  TEST_COMPARE (pthread_condattr_getclock (&condattr, &cl2), 0);
-  TEST_COMPARE (cl, cl2);
+  clockid_t attr_clock_read;
+  TEST_COMPARE (pthread_condattr_getclock (&condattr, &attr_clock_read), 0);
+  TEST_COMPARE (attr_clock, attr_clock_read);
 
   TEST_COMPARE (pthread_cond_init (&cond, &condattr), 0);
   TEST_COMPARE (pthread_condattr_destroy (&condattr), 0);
@@ -57,13 +61,20 @@ run_test (clockid_t cl)
   TEST_COMPARE (pthread_mutex_lock (&mut), EDEADLK);
 
   struct timespec ts_timeout;
-  xclock_gettime (cl, &ts_timeout);
+  xclock_gettime (wait_clock == CLOCK_USE_ATTR_CLOCK ? attr_clock : wait_clock,
+                  &ts_timeout);
 
   /* Wait one second.  */
   ++ts_timeout.tv_sec;
 
-  TEST_COMPARE (pthread_cond_timedwait (&cond, &mut, &ts_timeout), ETIMEDOUT);
-  TEST_TIMESPEC_BEFORE_NOW (ts_timeout, cl);
+  if (wait_clock == CLOCK_USE_ATTR_CLOCK) {
+    TEST_COMPARE (pthread_cond_timedwait (&cond, &mut, &ts_timeout), ETIMEDOUT);
+    TEST_TIMESPEC_BEFORE_NOW (ts_timeout, attr_clock);
+  } else {
+    TEST_COMPARE (pthread_cond_clockwait (&cond, &mut, wait_clock, &ts_timeout),
+                  ETIMEDOUT);
+    TEST_TIMESPEC_BEFORE_NOW (ts_timeout, wait_clock);
+  }
 
   xpthread_mutex_unlock (&mut);
   xpthread_mutex_destroy (&mut);
@@ -83,7 +94,7 @@ do_test (void)
 
 #else
 
-  run_test (CLOCK_REALTIME);
+  run_test (CLOCK_REALTIME, CLOCK_USE_ATTR_CLOCK);
 
 # if defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0
 #  if _POSIX_MONOTONIC_CLOCK == 0
@@ -93,8 +104,13 @@ do_test (void)
   else if (e == 0)
       FAIL_RET ("sysconf (_SC_MONOTONIC_CLOCK) must not return 0");
   else
+    {
 #  endif
-    run_test (CLOCK_MONOTONIC);
+      run_test (CLOCK_MONOTONIC, CLOCK_USE_ATTR_CLOCK);
+      run_test (CLOCK_REALTIME, CLOCK_MONOTONIC);
+      run_test (CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+      run_test (CLOCK_MONOTONIC, CLOCK_REALTIME);
+    }
 # else
   puts ("_POSIX_MONOTONIC_CLOCK not defined");
 # endif
diff --git a/nptl/tst-cond26.c b/nptl/tst-cond26.c
new file mode 100644
index 0000000000..bb4d6c9bbb
--- /dev/null
+++ b/nptl/tst-cond26.c
@@ -0,0 +1,77 @@
+/* Test unsupported/bad clocks passed to pthread_cond_clockwait.
+
+   Copyright (C) 2019 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/>.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <support/check.h>
+#include <support/timespec.h>
+#include <support/xthread.h>
+
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+
+#define NOT_A_VALID_CLOCK 123456
+
+static int
+do_test (void)
+{
+  xpthread_mutex_lock (&mut);
+
+  const struct timespec ts = make_timespec (0, 0);
+
+  /* These clocks are meaningless to sem_clockwait.  */
+#if defined(CLOCK_PROCESS_CPUTIME_ID)
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        CLOCK_PROCESS_CPUTIME_ID, &ts), EINVAL);
+#endif
+#if defined(CLOCK_THREAD_CPUTIME_ID)
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        CLOCK_THREAD_CPUTIME_ID, &ts), EINVAL);
+#endif
+
+  /* These clocks might be meaningful, but are currently unsupported
+     by pthread_cond_clockwait.  */
+#if defined(CLOCK_REALTIME_COARSE)
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        CLOCK_REALTIME_COARSE, &ts), EINVAL);
+#endif
+#if defined(CLOCK_MONOTONIC_RAW)
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        CLOCK_MONOTONIC_RAW, &ts), EINVAL);
+#endif
+#if defined(CLOCK_MONOTONIC_COARSE)
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        CLOCK_MONOTONIC_COARSE, &ts), EINVAL);
+#endif
+#if defined(CLOCK_BOOTTIME)
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        CLOCK_BOOTTIME, &ts), EINVAL);
+#endif
+
+  /* This is a completely invalid clock.  */
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut,
+                                        NOT_A_VALID_CLOCK, &ts), EINVAL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nptl/tst-cond27.c b/nptl/tst-cond27.c
new file mode 100644
index 0000000000..ad9658af4f
--- /dev/null
+++ b/nptl/tst-cond27.c
@@ -0,0 +1,66 @@
+/* Test pthread_cond_clockwait timeout.
+
+   Copyright (C) 2019 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/>.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <support/check.h>
+#include <support/timespec.h>
+#include <support/xthread.h>
+
+
+static pthread_mutex_t mut = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+
+static int
+do_test_clock (clockid_t clockid)
+{
+  /* Get the mutex.  */
+  xpthread_mutex_lock (&mut);
+
+  /* Waiting for the condition will fail.  But we want the timeout here.  */
+  const struct timespec ts_now = xclock_now (clockid);
+  const struct timespec ts_timeout =
+    timespec_add (ts_now, make_timespec (0, 500000000));
+
+  /* In theory pthread_cond_clockwait could return zero here due to
+     spurious wakeup. However that can't happen without a signal or an
+     additional waiter.  */
+  TEST_COMPARE (pthread_cond_clockwait (&cond, &mut, clockid, &ts_timeout),
+                ETIMEDOUT);
+
+  xpthread_mutex_unlock (&mut);
+
+  return 0;
+}
+
+static int
+do_test (void)
+{
+  do_test_clock (CLOCK_MONOTONIC);
+  do_test_clock (CLOCK_REALTIME);
+  return 0;
+}
+
+#include <support/test-driver.c>