about summary refs log tree commit diff
path: root/sysdeps/sparc/sparc32/sem_post.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/sparc/sparc32/sem_post.c')
-rw-r--r--sysdeps/sparc/sparc32/sem_post.c87
1 files changed, 54 insertions, 33 deletions
diff --git a/sysdeps/sparc/sparc32/sem_post.c b/sysdeps/sparc/sparc32/sem_post.c
index 7ca189810e..64cd851ffa 100644
--- a/sysdeps/sparc/sparc32/sem_post.c
+++ b/sysdeps/sparc/sparc32/sem_post.c
@@ -1,4 +1,4 @@
-/* sem_post -- post to a POSIX semaphore.  SPARC version.
+/* sem_post -- post to a POSIX semaphore.  Generic futex-using version.
    Copyright (C) 2003-2015 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
@@ -17,6 +17,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <atomic.h>
 #include <errno.h>
 #include <sysdep.h>
 #include <lowlevellock.h>
@@ -24,33 +25,58 @@
 #include <semaphore.h>
 
 #include <shlib-compat.h>
-#include <sparc-nptl.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
 __new_sem_post (sem_t *sem)
 {
-  struct sparc_new_sem *isem = (struct sparc_new_sem *) sem;
-  int nr;
+  struct new_sem *isem = (struct new_sem *) sem;
+  int private = isem->private;
+  unsigned int v;
 
-  if (__atomic_is_v9)
-    nr = atomic_increment_val (&isem->value);
-  else
-    {
-      __sparc32_atomic_do_lock24 (&isem->lock);
-      nr = ++(isem->value);
-      __sparc32_atomic_do_unlock24 (&isem->lock);
-    }
-  atomic_full_barrier ();
-  if (isem->nwaiters > 0)
+  __sparc32_atomic_do_lock24(&isem->pad);
+
+  v = isem->value;
+  if ((v << SEM_VALUE_SHIFT) == SEM_VALUE_MAX)
     {
-      int err = lll_futex_wake (&isem->value, 1,
-				isem->private ^ FUTEX_PRIVATE_FLAG);
-      if (__builtin_expect (err, 0) < 0)
-	{
-	  __set_errno (-err);
-	  return -1;
-	}
+      __sparc32_atomic_do_unlock24(&isem->pad);
+
+      __set_errno (EOVERFLOW);
+      return -1;
     }
+  isem->value = v + (1 << SEM_VALUE_SHIFT);
+
+  __sparc32_atomic_do_unlock24(&isem->pad);
+
+  if ((v & SEM_NWAITERS_MASK) != 0)
+    futex_wake (&isem->value, 1, private);
+
   return 0;
 }
 versioned_symbol (libpthread, __new_sem_post, sem_post, GLIBC_2_1);
@@ -61,19 +87,14 @@ int
 attribute_compat_text_section
 __old_sem_post (sem_t *sem)
 {
-  struct sparc_old_sem *isem = (struct sparc_old_sem *) sem;
-  int nr;
+  int *futex = (int *) sem;
 
-  if (__atomic_is_v9)
-    nr = atomic_increment_val (&isem->value);
-  else
-    {
-      __sparc32_atomic_do_lock24 (&isem->lock);
-      nr = ++(isem->value);
-      __sparc32_atomic_do_unlock24 (&isem->lock);
-    }
-  int err = lll_futex_wake (&isem->value, 1,
-			    isem->private ^ FUTEX_PRIVATE_FLAG);
+  /* We must need to synchronize with consumers of this token, so the atomic
+     increment must have release MO semantics.  */
+  atomic_write_barrier ();
+  (void) atomic_increment_val (futex);
+  /* We always have to assume it is a shared semaphore.  */
+  int err = lll_futex_wake (futex, 1, LLL_SHARED);
   if (__builtin_expect (err, 0) < 0)
     {
       __set_errno (-err);