Subject: Re: no pthread_spinlock_t on Mac OS 10.6.4

From: Paul Eggert <[email-masked]>
Date: Tue, 21 Sep 2010 00:49:37 -0700

On 09/20/2010 05:43 PM, Gary V. Vaughan wrote:

> Hmm... looking at gnulib/pthread.h, am I right in assuming that
> threads are now effectively disabled by gnulib on the mac in favour
> of a selection of stub functions?  In which case, I suppose those
> warnings indicate that glthread/lock.c may attempt to call unstubbed
> functions along some codepath and will then crash.

Yes, that's right.  The problem is that the gnulib i18n code includes
pthread.h for its own purposes, and wants primitives that our trivial
pthread replacement doesn't supply.

Could you try the following patch instead?  It tries an idea discussed
earlier today, namely, fall back on mutexes if spinlocks aren't there.
You'll need not only to update lib/pthread.in.h and m4/pthread.m4, but
also to get that code from modules/pthread into your makefile; I did
that by manually editing the "begin gnulib module pthread" section of
lib/gnulib.mk.

The patch below has a chance of working only because the gnulib i18n
code doesn't need spinlocks: if it did, we'd need something fancier.
Perhaps someday there should be a way to tell the i18n code "Hey! This
app doesn't use threads so don't try to include <pthread.h> or link to
the thread library."

Sorry, I can't easily test this as I don't use MacOS.

diff --git a/lib/pthread.in.h b/lib/pthread.in.h
index 0fdf9c3..f8e358b 100644
--- a/lib/pthread.in.h
+++ b/lib/pthread.in.h
@@ -19,6 +19,17 @@
 /* Written by Paul Eggert and Glen Lenker.  */
 
 #ifndef _GL_PTHREAD_H_
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+
+/* The include_next requires a split double-inclusion guard.  */
+#if @HAVE_PTHREAD_H@
+# @INCLUDE_NEXT@ @NEXT_PTHREAD_H@
+#endif
+
+#ifndef _GL_PTHREAD_H_
 #define _GL_PTHREAD_H_
 
 #include <errno.h>
@@ -27,7 +38,7 @@
 #include <sys/types.h>
 #include <time.h>
 
-#ifndef HAVE_PTHREAD_T
+#if ! @HAVE_PTHREAD_T@
  typedef int pthread_t;
  typedef int pthread_attr_t;
  typedef int pthread_barrier_t;
@@ -40,9 +51,9 @@
  typedef int pthread_once_t;
  typedef int pthread_rwlock_t;
  typedef int pthread_rwlockattr_t;
- typedef int pthread_spinlock_t;
 #endif
 
+#ifndef PTHREAD_COND_INITIALIZER
 #define PTHREAD_COND_INITIALIZER { 0 }
 #define PTHREAD_MUTEX_INITIALIZER { 0 }
 #define PTHREAD_ONCE_INIT { 0 }
@@ -81,6 +92,9 @@
 
 #define PTHREAD_SCOPE_SYSTEM 0
 #define PTHREAD_SCOPE_PROCESS 1
+#endif
+
+#if ! @HAVE_PTHREAD_T@
 
 /* Provide substitutes for the thread functions that should work
    adequately on a single-threaded implementation, where
@@ -147,6 +161,24 @@ pthread_join (pthread_t thread, void **pvalue)
 }
 
 static inline int
+pthread_mutexattr_destroy (pthread_mutexattr_t *attr)
+{
+  return 0;
+}
+
+static inline int
+pthread_mutexattr_init (pthread_mutexattr_t *attr)
+{
+  return 0;
+}
+
+static inline int
+pthread_mutexattr_settype (pthread_mutexattr_t *attr, int attr_type)
+{
+  return 0;
+}
+
+static inline int
 pthread_mutex_destroy (pthread_mutex_t *mutex)
 {
   /* MUTEX is never seriously used.  */
@@ -170,6 +202,12 @@ pthread_mutex_lock (pthread_mutex_t *mutex)
 }
 
 static inline int
+pthread_mutex_trylock (pthread_mutex_t *mutex)
+{
+  return pthread_mutex_lock (mutex);
+}
+
+static inline int
 pthread_mutex_unlock (pthread_mutex_t *mutex)
 {
   /* There is only one thread, so it always unlocks successfully.
@@ -178,40 +216,44 @@ pthread_mutex_unlock (pthread_mutex_t *mutex)
   return 0;
 }
 
+#endif
+
+#if ! @HAVE_PTHREAD_SPINLOCK_T@
+
+/* Approximate spinlocks with mutexes.  */
+
+typedef pthread_mutex_t pthread_spinlock_t;
+
 static inline int
 pthread_spin_init (pthread_spinlock_t *lock, int pshared)
 {
-  /* LOCK is never seriously used.  */
-  return 0;
+  return pthread_mutex_init (lock, NULL);
 }
 
 static inline int
 pthread_spin_destroy (pthread_spinlock_t *lock)
 {
-  /* LOCK is never seriously used.  */
-  return 0;
+  return pthread_mutex_destroy (lock);
 }
 
 static inline int
 pthread_spin_lock (pthread_spinlock_t *lock)
 {
-  /* Only one thread, so it always gets the lock.  */
-  return 0;
+  return pthread_mutex_lock (lock);
 }
 
 static inline int
 pthread_spin_trylock (pthread_spinlock_t *lock)
 {
-  /* Only one thread, so it always gets the lock.  Assume that a
-     thread never tries a lock that it already holds.  */
-  return 0;
+  return pthread_mutex_trylock (lock);
 }
 
 static inline int
 pthread_spin_unlock (pthread_spinlock_t *lock)
 {
-  /* Only one thread, so spin locks are no-ops.  */
-  return 0;
+  return pthread_mutex_unlock (lock);
 }
+#endif
 
 #endif /* _GL_PTHREAD_H_ */
+#endif /* _GL_PTHREAD_H_ */
diff --git a/m4/pthread.m4 b/m4/pthread.m4
index 69866cb..2b00944 100644
--- a/m4/pthread.m4
+++ b/m4/pthread.m4
@@ -5,25 +5,50 @@ dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
 
 AC_DEFUN([gl_PTHREAD_CHECK],
-  [AC_CHECK_HEADERS_ONCE([pthread.h])
+[
+   AC_REQUIRE([gl_PTHREAD_DEFAULTS])
+   AC_CHECK_HEADERS_ONCE([pthread.h])
+   gl_CHECK_NEXT_HEADERS([pthread.h])
+   if test $ac_cv_header_pthread_h = yes; then
+     HAVE_PTHREAD_H=1
+   else
+     HAVE_PTHREAD_H=0
+   fi
+
+   AC_CHECK_TYPES([pthread_t, pthread_spinlock_t])
+   if test $ac_cv_type_pthread_t != yes; then
+     HAVE_PTHREAD_T=0
+   fi
+   if test $ac_cv_type_pthread_spinlock_t != yes; then
+     HAVE_PTHREAD_SPINLOCK_T=0
+   fi
+
+   if test $ac_cv_header_pthread_h != yes ||
+      test $ac_cv_type_pthread_t != yes ||
+      test $ac_cv_type_pthread_spinlock_t != yes; then
+     PTHREAD_H='pthread.h'
+   fi
 
    LIB_PTHREAD=
-   PTHREAD_H=
-   if test "$ac_cv_header_pthread_h" = yes; then
+   if test $ac_cv_header_pthread_h = yes; then
      gl_saved_libs=$LIBS
      AC_SEARCH_LIBS([pthread_create], [pthread],
        [if test "$ac_cv_search_pthread_create" != "none required"; then
           LIB_PTHREAD="$ac_cv_search_pthread_create"
         fi])
      LIBS="$gl_saved_libs"
-   else
-     AC_CHECK_TYPES([pthread_t])
-     PTHREAD_H='pthread.h'
    fi
-
    AC_SUBST([LIB_PTHREAD])
-   AC_SUBST([PTHREAD_H])
 
    AC_REQUIRE([AC_C_INLINE])
    AC_REQUIRE([AC_C_RESTRICT])
 ])
+
+AC_DEFUN([gl_PTHREAD_DEFAULTS],
+[
+  dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_PTHREAD_H=1;              AC_SUBST([HAVE_PTHREAD_H])
+  HAVE_PTHREAD_T=1;              AC_SUBST([HAVE_PTHREAD_T])
+  HAVE_PTHREAD_SPINLOCK_T=1;     AC_SUBST([HAVE_PTHREAD_SPINLOCK_T])
+  PTHREAD_H='';                  AC_SUBST([PTHREAD_H])
+])
diff --git a/modules/pthread b/modules/pthread
index 51d5dbb..6d06464 100644
--- a/modules/pthread
+++ b/modules/pthread
@@ -18,9 +18,18 @@ BUILT_SOURCES += $(PTHREAD_H)
 # We need the following in order to create <pthread.h> when the system
 # doesn't have one that works with the given compiler.
 pthread.h: pthread.in.h
-	$(AM_V_GEN)ln -f $(srcdir)/pthread.in.h $@ \
-          || cp $(srcdir)/pthread.in.h $@
-MOSTLYCLEANFILES += pthread.h
+	$(AM_V_GEN)rm -f $@-t $@ && \
+	{ echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \
+	  sed -e 's|@''HAVE_PTHREAD_H''@|$(HAVE_PTHREAD_H)|g' \
+	      -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+	      -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+	      -e 's|@''NEXT_PTHREAD_H''@|$(NEXT_PTHREAD_H)|g' \
+	      -e 's|@''HAVE_PTHREAD_T''@|$(HAVE_PTHREAD_T)|g' \
+	      -e 's|@''HAVE_PTHREAD_SPINLOCK_T''@|$(HAVE_PTHREAD_SPINLOCK_T)|g' \
+	      < $(srcdir)/pthread.in.h; \
+	} > $@-t && \
+	mv $@-t $@
+MOSTLYCLEANFILES += pthread.h pthread.h-t
 
 Include:
 <pthread.h>