From 6bf513db3802388e3fc8c40a506e23c85a8abf21 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Wed, 1 Jul 2020 22:13:27 +0200
Subject: [PATCH] pthread-spin: Add error checking.

* lib/pthread-spin.c: Include <stdbool.h>.
(pthread_spin_init, pthread_spin_lock, pthread_spin_trylock,
pthread_spin_unlock) [GCC>=4.7]: Prefer an implementation that verifies
the unlocks.
* modules/pthread-spin (Depends-on): Add stdbool.
---
 ChangeLog            |  9 ++++++++
 lib/pthread-spin.c   | 53 ++++++++++++++++++++++++++++++++++++++++++++
 modules/pthread-spin |  1 +
 3 files changed, 63 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 86c32ed6c7..b1d391b4ab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2020-07-01  Bruno Haible  <bruno@clisp.org>
+
+	pthread-spin: Add error checking.
+	* lib/pthread-spin.c: Include <stdbool.h>.
+	(pthread_spin_init, pthread_spin_lock, pthread_spin_trylock,
+	pthread_spin_unlock) [GCC>=4.7]: Prefer an implementation that verifies
+	the unlocks.
+	* modules/pthread-spin (Depends-on): Add stdbool.
+
 2020-07-01  Bruno Haible  <bruno@clisp.org>
 
 	pthread-spin: Add tests.
diff --git a/lib/pthread-spin.c b/lib/pthread-spin.c
index 93e8b370ca..73eb9f31f6 100644
--- a/lib/pthread-spin.c
+++ b/lib/pthread-spin.c
@@ -21,6 +21,8 @@
 /* Specification.  */
 #include <pthread.h>
 
+#include <stdbool.h>
+
 #if (defined _WIN32 && ! defined __CYGWIN__) && USE_WINDOWS_THREADS
 # include "windows-spin.h"
 #endif
@@ -72,6 +74,55 @@ pthread_spin_destroy (pthread_spinlock_t *lock)
    Documentation:
    <https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/_005f_005fatomic-Builtins.html>  */
 
+#  if 1
+/* An implementation that verifies the unlocks.  */
+
+int
+pthread_spin_init (pthread_spinlock_t *lock,
+                   int shared_across_processes _GL_UNUSED)
+{
+  __atomic_store_n ((char *) lock, 0, __ATOMIC_SEQ_CST);
+  return 0;
+}
+
+int
+pthread_spin_lock (pthread_spinlock_t *lock)
+{
+  /* Wait until *lock becomes 0, then replace it with 1.  */
+  asyncsafe_spinlock_t zero;
+  while (!(zero = 0,
+           __atomic_compare_exchange_n ((char *) lock, &zero, 1, false,
+                                        __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
+    ;
+  return 0;
+}
+
+int
+pthread_spin_trylock (pthread_spinlock_t *lock)
+{
+  asyncsafe_spinlock_t zero;
+  if (!(zero = 0,
+        __atomic_compare_exchange_n ((char *) lock, &zero, 1, false,
+                                     __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)))
+    return EBUSY;
+  return 0;
+}
+
+int
+pthread_spin_unlock (pthread_spinlock_t *lock)
+{
+  /* If *lock is 1, then replace it with 0.  */
+  asyncsafe_spinlock_t one = 1;
+  if (!__atomic_compare_exchange_n ((char *) lock, &one, 0, false,
+                                    __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+    abort ();
+  return 0;
+}
+
+#  else
+/* An implementation that is a little bit more optimized, but does not verify
+   the unlocks.  */
+
 int
 pthread_spin_init (pthread_spinlock_t *lock,
                    int shared_across_processes _GL_UNUSED)
@@ -103,6 +154,8 @@ pthread_spin_unlock (pthread_spinlock_t *lock)
   return 0;
 }
 
+#  endif
+
 int
 pthread_spin_destroy (pthread_spinlock_t *lock)
 {
diff --git a/modules/pthread-spin b/modules/pthread-spin
index 53e0051c34..ce69944021 100644
--- a/modules/pthread-spin
+++ b/modules/pthread-spin
@@ -7,6 +7,7 @@ m4/pthread-spin.m4
 
 Depends-on:
 pthread-h
+stdbool         [test $HAVE_PTHREAD_SPIN_INIT = 0 || test $REPLACE_PTHREAD_SPIN_INIT = 1]
 windows-spin    [test $gl_threads_api = windows]
 
 configure.ac:
-- 
2.39.5