acinclude.m4 (GLIBCXX_CHECK_GTHREADS): Check for pthread_rwlock_t.
authorJonathan Wakely <jwakely@redhat.com>
Wed, 18 Mar 2015 10:53:38 +0000 (10:53 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 18 Mar 2015 10:53:38 +0000 (10:53 +0000)
2015-03-18  Jonathan Wakely  <jwakely@redhat.com>
    Torvald Riegel  <triegel@redhat.com>

* acinclude.m4 (GLIBCXX_CHECK_GTHREADS): Check for pthread_rwlock_t.
* config.h.in: Regenerate.
* configure: Regenerate.
* include/std/shared_mutex: Check _GLIBCXX_USE_PTHREAD_RWLOCK_T.
(shared_timed_mutex::_M_rwlock): Use PTHREAD_RWLOCK_INITIALIZER.
(shared_timed_mutex::lock_shared()): Retry on EAGAIN.
(shared_timed_mutex::try_lock_shared_until()): Retry on EAGAIN and
EDEADLK.

Co-Authored-By: Torvald Riegel <triegel@redhat.com>
From-SVN: r221484

libstdc++-v3/ChangeLog
libstdc++-v3/acinclude.m4
libstdc++-v3/config.h.in
libstdc++-v3/configure
libstdc++-v3/include/std/shared_mutex

index 103d7098adba4efe66638db7c4aa13645b9c8ebc..ae6445e3b864dd92f88b2c314e9b09ac6bb33850 100644 (file)
@@ -1,3 +1,15 @@
+2015-03-18  Jonathan Wakely  <jwakely@redhat.com>
+           Torvald Riegel  <triegel@redhat.com>
+
+       * acinclude.m4 (GLIBCXX_CHECK_GTHREADS): Check for pthread_rwlock_t.
+       * config.h.in: Regenerate.
+       * configure: Regenerate.
+       * include/std/shared_mutex: Check _GLIBCXX_USE_PTHREAD_RWLOCK_T.
+       (shared_timed_mutex::_M_rwlock): Use PTHREAD_RWLOCK_INITIALIZER.
+       (shared_timed_mutex::lock_shared()): Retry on EAGAIN.
+       (shared_timed_mutex::try_lock_shared_until()): Retry on EAGAIN and
+       EDEADLK.
+
 2015-03-17  Jonathan Wakely  <jwakely@redhat.com>
 
        * libsupc++/nested_exception.h: Do not try to derive from final
index 0b8c0f0b111ff5b5863b935cf773c2bfa7577ed5..74f5a652fb7f7da51b2c2e46a031da5ecbfcfd66 100644 (file)
@@ -3563,6 +3563,13 @@ AC_DEFUN([GLIBCXX_CHECK_GTHREADS], [
   if test x"$ac_has_gthreads" = x"yes"; then
     AC_DEFINE(_GLIBCXX_HAS_GTHREADS, 1,
              [Define if gthreads library is available.])
+
+    # Also check for pthread_rwlock_t for std::shared_timed_mutex in C++14
+    AC_CHECK_TYPE([pthread_rwlock_t],
+            [AC_DEFINE([_GLIBCXX_USE_PTHREAD_RWLOCK_T], 1,
+            [Define if POSIX read/write locks are available in <gthr.h>.])],
+            [],
+            [#include "gthr.h"])
   fi
 
   CXXFLAGS="$ac_save_CXXFLAGS"
index adc7d44ee4886be44af74c276426e8f0676c6212..26ccd22de2df6b341d254539269a3050d15ae3cf 100644 (file)
 /* Define if pthreads_num_processors_np is available in <pthread.h>. */
 #undef _GLIBCXX_USE_PTHREADS_NUM_PROCESSORS_NP
 
+/* Define if POSIX read/write locks are available in <gthr.h>. */
+#undef _GLIBCXX_USE_PTHREAD_RWLOCK_T
+
 /* Define if /dev/random and /dev/urandom are available for the random_device
    of TR1 (Chapter 5.1). */
 #undef _GLIBCXX_USE_RANDOM_TR1
index bcdb93178b0cc2954d0c0bff07d652f3b4a599e4..2507ff7e490b3e7fbbc7e619d1abb0a63f9631b5 100755 (executable)
@@ -2498,6 +2498,60 @@ $as_echo "$ac_res" >&6; }
   eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
 
 } # ac_fn_c_check_type
+
+# ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES
+# ---------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_cxx_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_cxx_check_type
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -11539,7 +11593,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11542 "configure"
+#line 11596 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11645,7 +11699,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11648 "configure"
+#line 11702 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -15065,7 +15119,7 @@ fi
     #
     # Fake what AC_TRY_COMPILE does.  XXX Look at redoing this new-style.
     cat > conftest.$ac_ext << EOF
-#line 15068 "configure"
+#line 15122 "configure"
 struct S { ~S(); };
 void bar();
 void foo()
@@ -15417,7 +15471,7 @@ $as_echo "$glibcxx_cv_atomic_long_long" >&6; }
   # Fake what AC_TRY_COMPILE does.
 
     cat > conftest.$ac_ext << EOF
-#line 15420 "configure"
+#line 15474 "configure"
 int main()
 {
   typedef bool atomic_type;
@@ -15452,7 +15506,7 @@ $as_echo "$glibcxx_cv_atomic_bool" >&6; }
     rm -f conftest*
 
     cat > conftest.$ac_ext << EOF
-#line 15455 "configure"
+#line 15509 "configure"
 int main()
 {
   typedef short atomic_type;
@@ -15487,7 +15541,7 @@ $as_echo "$glibcxx_cv_atomic_short" >&6; }
     rm -f conftest*
 
     cat > conftest.$ac_ext << EOF
-#line 15490 "configure"
+#line 15544 "configure"
 int main()
 {
   // NB: _Atomic_word not necessarily int.
@@ -15523,7 +15577,7 @@ $as_echo "$glibcxx_cv_atomic_int" >&6; }
     rm -f conftest*
 
     cat > conftest.$ac_ext << EOF
-#line 15526 "configure"
+#line 15580 "configure"
 int main()
 {
   typedef long long atomic_type;
@@ -15602,7 +15656,7 @@ $as_echo "$as_me: WARNING: Performance of certain classes will degrade as a resu
   # unnecessary for this test.
 
     cat > conftest.$ac_ext << EOF
-#line 15605 "configure"
+#line 15659 "configure"
 int main()
 {
   _Decimal32 d1;
@@ -15644,7 +15698,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
   # unnecessary for this test.
 
     cat > conftest.$ac_ext << EOF
-#line 15647 "configure"
+#line 15701 "configure"
 template<typename T1, typename T2>
   struct same
   { typedef T2 type; };
@@ -15678,7 +15732,7 @@ $as_echo "$enable_int128" >&6; }
     rm -f conftest*
 
     cat > conftest.$ac_ext << EOF
-#line 15681 "configure"
+#line 15735 "configure"
 template<typename T1, typename T2>
   struct same
   { typedef T2 type; };
@@ -78786,6 +78840,16 @@ $as_echo "$ac_has_gthreads" >&6; }
 
 $as_echo "#define _GLIBCXX_HAS_GTHREADS 1" >>confdefs.h
 
+
+    # Also check for pthread_rwlock_t for std::shared_timed_mutex in C++14
+    ac_fn_cxx_check_type "$LINENO" "pthread_rwlock_t" "ac_cv_type_pthread_rwlock_t" "#include \"gthr.h\"
+"
+if test "x$ac_cv_type_pthread_rwlock_t" = x""yes; then :
+
+$as_echo "#define _GLIBCXX_USE_PTHREAD_RWLOCK_T 1" >>confdefs.h
+
+fi
+
   fi
 
   CXXFLAGS="$ac_save_CXXFLAGS"
index 5dcc295746b3c7b8eb3ef9c8fc22bda98f574b90..ab1b45b87acd76bdc231992d253511cb91ac8f0a 100644 (file)
@@ -57,10 +57,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// shared_timed_mutex
   class shared_timed_mutex
   {
-#if defined(__GTHREADS_CXX0X)
+#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_T
     typedef chrono::system_clock       __clock_t;
 
-    pthread_rwlock_t                   _M_rwlock;
+#ifdef PTHREAD_RWLOCK_INITIALIZER
+    pthread_rwlock_t   _M_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+  public:
+    shared_timed_mutex() = default;
+    ~shared_timed_mutex() = default;
+#else
+    pthread_rwlock_t   _M_rwlock;
 
   public:
     shared_timed_mutex()
@@ -82,6 +89,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Errors not handled: EBUSY, EINVAL
       _GLIBCXX_DEBUG_ASSERT(__ret == 0);
     }
+#endif
 
     shared_timed_mutex(const shared_timed_mutex&) = delete;
     shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
@@ -165,12 +173,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     void
     lock_shared()
     {
-      int __ret = pthread_rwlock_rdlock(&_M_rwlock);
+      int __ret;
+      // We retry if we exceeded the maximum number of read locks supported by
+      // the POSIX implementation; this can result in busy-waiting, but this
+      // is okay based on the current specification of forward progress
+      // guarantees by the standard.
+      do
+       __ret = pthread_rwlock_rdlock(&_M_rwlock);
+      while (__ret == EAGAIN);
       if (__ret == EDEADLK)
        __throw_system_error(int(errc::resource_deadlock_would_occur));
-      if (__ret == EAGAIN)
-       // Maximum number of read locks has been exceeded.
-       __throw_system_error(int(errc::device_or_resource_busy));
       // Errors not handled: EINVAL
       _GLIBCXX_DEBUG_ASSERT(__ret == 0);
     }
@@ -210,11 +222,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            static_cast<long>(__ns.count())
          };
 
-       int __ret = pthread_rwlock_timedrdlock(&_M_rwlock, &__ts);
-       // If the maximum number of read locks has been exceeded, or we would
-       // deadlock, we just fail to acquire the lock.  Unlike for lock(),
-       // we are not allowed to throw an exception.
-       if (__ret == ETIMEDOUT || __ret == EAGAIN || __ret == EDEADLK)
+       int __ret;
+       // Unlike for lock(), we are not allowed to throw an exception so if
+       // the maximum number of read locks has been exceeded, or we would
+       // deadlock, we just try to acquire the lock again (and will time out
+       // eventually). 
+       // In cases where we would exceed the maximum number of read locks
+       // throughout the whole time until the timeout, we will fail to
+       // acquire the lock even if it would be logically free; however, this
+       // is allowed by the standard, and we made a "strong effort"
+       // (see C++14 30.4.1.4p26).
+       // For cases where the implementation detects a deadlock we
+       // intentionally block and timeout so that an early return isn't
+       // mistaken for a spurious failure, which might help users realise
+       // there is a deadlock.
+       do
+         __ret = pthread_rwlock_timedrdlock(&_M_rwlock, &__ts);
+       while (__ret == EAGAIN || __ret == EDEADLK);
+       if (__ret == ETIMEDOUT)
          return false;
        // Errors not handled: EINVAL
        _GLIBCXX_DEBUG_ASSERT(__ret == 0);
@@ -241,7 +266,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unlock();
     }
 
-#else // defined(__GTHREADS_CXX0X)
+#else // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T
 
 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
     struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
@@ -438,7 +463,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
            _M_gate1.notify_one();
        }
     }
-#endif // !defined(__GTHREADS_CXX0X)
+#endif // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T
   };
 #endif // _GLIBCXX_HAS_GTHREADS