about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
Diffstat (limited to 'nptl')
-rw-r--r--nptl/Makefile5
-rw-r--r--nptl/Versions4
-rw-r--r--nptl/sem_clockwait.c45
-rw-r--r--nptl/sem_timedwait.c3
-rw-r--r--nptl/sem_wait.c3
-rw-r--r--nptl/sem_waitcommon.c15
-rw-r--r--nptl/tst-sem13.c39
-rw-r--r--nptl/tst-sem17.c76
-rw-r--r--nptl/tst-sem5.c23
9 files changed, 193 insertions, 20 deletions
diff --git a/nptl/Makefile b/nptl/Makefile
index d23a235581..fefdeb3be4 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -113,7 +113,7 @@ libpthread-routines = nptl-init nptlfreeres vars events version pt-interp \
 		      sem_init sem_destroy \
 		      sem_open sem_close sem_unlink \
 		      sem_getvalue \
-		      sem_wait sem_timedwait sem_post \
+		      sem_wait sem_timedwait sem_clockwait sem_post \
 		      cleanup cleanup_defer cleanup_compat \
 		      cleanup_defer_compat unwind \
 		      pt-longjmp pt-cleanup\
@@ -194,6 +194,7 @@ CFLAGS-pthread_once.c += $(uses-callbacks) -fexceptions \
 CFLAGS-pthread_cond_wait.c += -fexceptions -fasynchronous-unwind-tables
 CFLAGS-sem_wait.c += -fexceptions -fasynchronous-unwind-tables
 CFLAGS-sem_timedwait.c += -fexceptions -fasynchronous-unwind-tables
+CFLAGS-sem_clockwait.c = -fexceptions -fasynchronous-unwind-tables
 
 # These are the function wrappers we have to duplicate here.
 CFLAGS-fcntl.c += -fexceptions -fasynchronous-unwind-tables
@@ -263,7 +264,7 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 	tst-key1 tst-key2 tst-key3 tst-key4 \
 	tst-sem1 tst-sem2 tst-sem3 tst-sem4 tst-sem5 tst-sem6 tst-sem7 \
 	tst-sem8 tst-sem9 tst-sem10 tst-sem14 \
-	tst-sem15 tst-sem16 \
+	tst-sem15 tst-sem16 tst-sem17 \
 	tst-barrier1 tst-barrier2 tst-barrier3 tst-barrier4 \
 	tst-align tst-align3 \
 	tst-basic1 tst-basic2 tst-basic3 tst-basic4 tst-basic5 tst-basic6 \
diff --git a/nptl/Versions b/nptl/Versions
index 6007fd03e7..4f0e837507 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -276,6 +276,10 @@ libpthread {
     cnd_timedwait; cnd_wait; tss_create; tss_delete; tss_get; tss_set;
   }
 
+  GLIBC_2.30 {
+    sem_clockwait;
+  }
+
   GLIBC_PRIVATE {
     __pthread_initialize_minimal;
     __pthread_clock_gettime; __pthread_clock_settime;
diff --git a/nptl/sem_clockwait.c b/nptl/sem_clockwait.c
new file mode 100644
index 0000000000..23a6c08677
--- /dev/null
+++ b/nptl/sem_clockwait.c
@@ -0,0 +1,45 @@
+/* sem_clockwait -- wait on a semaphore with timeout using the specified
+   clock.
+
+   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 "sem_waitcommon.c"
+
+int
+sem_clockwait (sem_t *sem, clockid_t clockid,
+	       const struct timespec *abstime)
+{
+  /* Check that supplied clockid is one we support, even if we don't end up
+     waiting.  */
+  if (!futex_abstimed_supported_clockid (clockid))
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
+    return 0;
+  else
+    return __new_sem_wait_slow ((struct new_sem *) sem, clockid, abstime);
+}
diff --git a/nptl/sem_timedwait.c b/nptl/sem_timedwait.c
index 3dd71ab2fa..0918d8bd10 100644
--- a/nptl/sem_timedwait.c
+++ b/nptl/sem_timedwait.c
@@ -36,5 +36,6 @@ sem_timedwait (sem_t *sem, const struct timespec *abstime)
   if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
     return 0;
   else
-    return __new_sem_wait_slow((struct new_sem *) sem, abstime);
+    return __new_sem_wait_slow ((struct new_sem *) sem,
+				CLOCK_REALTIME, abstime);
 }
diff --git a/nptl/sem_wait.c b/nptl/sem_wait.c
index 6a2d26bd1a..20a8b9d377 100644
--- a/nptl/sem_wait.c
+++ b/nptl/sem_wait.c
@@ -39,7 +39,8 @@ __new_sem_wait (sem_t *sem)
   if (__new_sem_wait_fast ((struct new_sem *) sem, 0) == 0)
     return 0;
   else
-    return __new_sem_wait_slow((struct new_sem *) sem, NULL);
+    return __new_sem_wait_slow ((struct new_sem *) sem,
+				CLOCK_REALTIME, NULL);
 }
 versioned_symbol (libpthread, __new_sem_wait, sem_wait, GLIBC_2_1);
 
diff --git a/nptl/sem_waitcommon.c b/nptl/sem_waitcommon.c
index 425d040dd4..cad56e996d 100644
--- a/nptl/sem_waitcommon.c
+++ b/nptl/sem_waitcommon.c
@@ -103,19 +103,19 @@ __sem_wait_cleanup (void *arg)
    users don't seem to need it.  */
 static int
 __attribute__ ((noinline))
-do_futex_wait (struct new_sem *sem, const struct timespec *abstime)
+do_futex_wait (struct new_sem *sem, clockid_t clockid,
+	       const struct timespec *abstime)
 {
   int err;
 
 #if __HAVE_64B_ATOMICS
   err = futex_abstimed_wait_cancelable (
       (unsigned int *) &sem->data + SEM_VALUE_OFFSET, 0,
-      CLOCK_REALTIME, abstime,
+      clockid, abstime,
       sem->private);
 #else
   err = futex_abstimed_wait_cancelable (&sem->value, SEM_NWAITERS_MASK,
-					CLOCK_REALTIME, abstime,
-					sem->private);
+					clockid, abstime, sem->private);
 #endif
 
   return err;
@@ -162,7 +162,8 @@ __new_sem_wait_fast (struct new_sem *sem, int definitive_result)
 /* Slow path that blocks.  */
 static int
 __attribute__ ((noinline))
-__new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime)
+__new_sem_wait_slow (struct new_sem *sem, clockid_t clockid,
+		     const struct timespec *abstime)
 {
   int err = 0;
 
@@ -180,7 +181,7 @@ __new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime)
       /* If there is no token available, sleep until there is.  */
       if ((d & SEM_VALUE_MASK) == 0)
 	{
-	  err = do_futex_wait (sem, abstime);
+	  err = do_futex_wait (sem, clockid, abstime);
 	  /* A futex return value of 0 or EAGAIN is due to a real or spurious
 	     wake-up, or due to a change in the number of tokens.  We retry in
 	     these cases.
@@ -281,7 +282,7 @@ __new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime)
 	  if ((v >> SEM_VALUE_SHIFT) == 0)
 	    {
 	      /* See __HAVE_64B_ATOMICS variant.  */
-	      err = do_futex_wait(sem, abstime);
+	      err = do_futex_wait (sem, clockid, abstime);
 	      if (err == ETIMEDOUT || err == EINTR)
 		{
 		  __set_errno (err);
diff --git a/nptl/tst-sem13.c b/nptl/tst-sem13.c
index 28d37ed0cb..d7baa2ae58 100644
--- a/nptl/tst-sem13.c
+++ b/nptl/tst-sem13.c
@@ -6,9 +6,14 @@
 #include <internaltypes.h>
 #include <support/check.h>
 
+/* A bogus clock value that tells run_test to use sem_timedwait rather than
+   sem_clockwait.  */
+#define CLOCK_USE_TIMEDWAIT (-1)
 
-static int
-do_test (void)
+typedef int (*waitfn_t)(sem_t *, struct timespec *);
+
+static void
+do_test_wait (waitfn_t waitfn, const char *fnname)
 {
   union
   {
@@ -16,11 +21,13 @@ do_test (void)
     struct new_sem ns;
   } u;
 
+  printf ("do_test_wait: %s\n", fnname);
+
   TEST_COMPARE (sem_init (&u.s, 0, 0), 0);
 
   struct timespec ts = { 0, 1000000001 };	/* Invalid.  */
   errno = 0;
-  TEST_VERIFY_EXIT (sem_timedwait (&u.s, &ts) < 0);
+  TEST_VERIFY_EXIT (waitfn (&u.s, &ts) < 0);
   TEST_COMPARE (errno, EINVAL);
 
 #if __HAVE_64B_ATOMICS
@@ -33,7 +40,7 @@ do_test (void)
   ts.tv_sec = /* Invalid.  */ -2;
   ts.tv_nsec = 0;
   errno = 0;
-  TEST_VERIFY_EXIT (sem_timedwait (&u.s, &ts) < 0);
+  TEST_VERIFY_EXIT (waitfn (&u.s, &ts) < 0);
   TEST_COMPARE (errno, ETIMEDOUT);
 #if __HAVE_64B_ATOMICS
   nwaiters = (u.ns.data >> SEM_NWAITERS_SHIFT);
@@ -41,7 +48,31 @@ do_test (void)
   nwaiters = u.ns.nwaiters;
 #endif
   TEST_COMPARE (nwaiters, 0);
+}
 
+int test_sem_timedwait (sem_t *sem, struct timespec *ts)
+{
+  return sem_timedwait (sem, ts);
+}
+
+int test_sem_clockwait_monotonic (sem_t *sem, struct timespec *ts)
+{
+  return sem_clockwait (sem, CLOCK_MONOTONIC, ts);
+}
+
+int test_sem_clockwait_realtime (sem_t *sem, struct timespec *ts)
+{
+  return sem_clockwait (sem, CLOCK_REALTIME, ts);
+}
+
+static int do_test (void)
+{
+  do_test_wait (&test_sem_timedwait,
+                "sem_timedwait");
+  do_test_wait (&test_sem_clockwait_monotonic,
+                "sem_clockwait(monotonic)");
+  do_test_wait (&test_sem_clockwait_realtime,
+                "sem_clockwait(realtime)");
   return 0;
 }
 
diff --git a/nptl/tst-sem17.c b/nptl/tst-sem17.c
new file mode 100644
index 0000000000..69e2387bdc
--- /dev/null
+++ b/nptl/tst-sem17.c
@@ -0,0 +1,76 @@
+/* Test unsupported/bad clocks passed to sem_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 <semaphore.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <support/check.h>
+#include <support/timespec.h>
+
+
+#define NOT_A_VALID_CLOCK 123456
+
+static int
+do_test (void)
+{
+  sem_t s;
+  TEST_COMPARE (sem_init (&s, 0, 1), 0);
+
+  const struct timespec ts = make_timespec (0, 0);
+
+  /* These clocks are meaningless to sem_clockwait.  */
+#if defined(CLOCK_PROCESS_CPUTIME_ID)
+  TEST_COMPARE (sem_clockwait (&s, CLOCK_PROCESS_CPUTIME_ID, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+#endif
+#if defined(CLOCK_THREAD_CPUTIME_ID)
+  TEST_COMPARE (sem_clockwait (&s, CLOCK_THREAD_CPUTIME_ID, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+#endif
+
+  /* These clocks might be meaningful, but are currently unsupported
+     by pthread_cond_clockwait.  */
+#if defined(CLOCK_REALTIME_COARSE)
+  TEST_COMPARE (sem_clockwait (&s, CLOCK_REALTIME_COARSE, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+#endif
+#if defined(CLOCK_MONOTONIC_RAW)
+  TEST_COMPARE (sem_clockwait (&s, CLOCK_MONOTONIC_RAW, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+#endif
+#if defined(CLOCK_MONOTONIC_COARSE)
+  TEST_COMPARE (sem_clockwait (&s, CLOCK_MONOTONIC_COARSE, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+#endif
+#if defined(CLOCK_BOOTTIME)
+  TEST_COMPARE (sem_clockwait (&s, CLOCK_BOOTTIME, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+#endif
+
+  /* This is a completely invalid clock.  */
+  TEST_COMPARE (sem_clockwait (&s, NOT_A_VALID_CLOCK, &ts), -1);
+  TEST_COMPARE (errno, EINVAL);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nptl/tst-sem5.c b/nptl/tst-sem5.c
index 396222b85f..63b13552b0 100644
--- a/nptl/tst-sem5.c
+++ b/nptl/tst-sem5.c
@@ -25,10 +25,15 @@
 #include <support/timespec.h>
 #include <support/xtime.h>
 
+/* A bogus clock value that tells run_test to use sem_timedwait rather than
+   sem_clockwait.  */
+#define CLOCK_USE_TIMEDWAIT (-1)
 
-static int
-do_test (void)
+static void
+do_test_clock (clockid_t clockid)
 {
+  const clockid_t clockid_for_get =
+    clockid == CLOCK_USE_TIMEDWAIT ? CLOCK_REALTIME : clockid;
   sem_t s;
   struct timespec ts;
 
@@ -36,14 +41,22 @@ do_test (void)
   TEST_COMPARE (TEMP_FAILURE_RETRY (sem_wait (&s)), 0);
 
   /* We wait for half a second.  */
-  xclock_gettime (CLOCK_REALTIME, &ts);
+  xclock_gettime (clockid_for_get, &ts);
   ts = timespec_add (ts, make_timespec (0, TIMESPEC_HZ/2));
 
   errno = 0;
-  TEST_COMPARE (TEMP_FAILURE_RETRY (sem_timedwait (&s, &ts)), -1);
+  TEST_COMPARE (TEMP_FAILURE_RETRY ((clockid == CLOCK_USE_TIMEDWAIT)
+                                    ? sem_timedwait (&s, &ts)
+                                    : sem_clockwait (&s, clockid, &ts)), -1);
   TEST_COMPARE (errno, ETIMEDOUT);
-  TEST_TIMESPEC_NOW_OR_AFTER (CLOCK_REALTIME, ts);
+  TEST_TIMESPEC_NOW_OR_AFTER (clockid_for_get, ts);
+}
 
+static int do_test (void)
+{
+  do_test_clock (CLOCK_USE_TIMEDWAIT);
+  do_test_clock (CLOCK_REALTIME);
+  do_test_clock (CLOCK_MONOTONIC);
   return 0;
 }