public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [patch] libstdc++/64847 add autoconf checks for pthread_rwlock_t
@ 2015-03-12 19:17 Jonathan Wakely
  2015-03-13 13:46 ` Jonathan Wakely
  0 siblings, 1 reply; 3+ messages in thread
From: Jonathan Wakely @ 2015-03-12 19:17 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 720 bytes --]

I assumed that Pthreads was enough to ensure pthread_rwlock_t but
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64847 shows that isn't
true for HPUX (seems it was optional prior to POSIX 1003.1-2001).

This adds an autoconf check to decide whether to use pthread_rwlock_t
or the fallback implementation in terms of std::condition_variable and
std::mutex.

This also includes some fixes from Torvald so that we loop and retry
if libc returns EAGAIN, to handle a difference in semantics between
POSIX and C++14.

And as an optimization I've made the _M_rwlock member use the
PTHREAD_RWLOCK_INITIALIZER macro if available.

Tested x86_64-linux, ppc64le-linux and x86_64-dragonfly.

I plan to commit this to trunk tomorrow.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 4821 bytes --]

commit 7212446ada7d741f6fe0fc9d9fca9d5b55322384
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Mar 12 17:29:42 2015 +0000

    2015-03-12  Jonathan Wakely  <jwakely@redhat.com>
    	    Torvald Riegel  <triegel@redhat.com>
    
    	PR libstdc++/64847
    	* acinclude.m4 (GLIBCXX_CHECK_GTHREADS): Check for pthread_rwlock_t.
    	* config.h.in: Regenerate.
    	* configure: Regenerate.
    	* include/std/shared_mutex: Check _GLIBCXX_USE_PTHREADS_RWLOCKS.
    	(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.

diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index 1727140..86628c0 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -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_PTHREADS_RWLOCKS], 1,
+            [Define if POSIX read/write locks are available in <gthr.h>.])],
+            [],
+            [#include "gthr.h"])
   fi
 
   CXXFLAGS="$ac_save_CXXFLAGS"
diff --git a/libstdc++-v3/include/std/shared_mutex b/libstdc++-v3/include/std/shared_mutex
index 5dcc295..61251b0 100644
--- a/libstdc++-v3/include/std/shared_mutex
+++ b/libstdc++-v3/include/std/shared_mutex
@@ -57,10 +57,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// shared_timed_mutex
   class shared_timed_mutex
   {
-#if defined(__GTHREADS_CXX0X)
+#ifdef _GLIBCXX_USE_PTHREADS_RWLOCKS
     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;
+      do
+	__ret = pthread_rwlock_rdlock(&_M_rwlock);
+      // 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.
+      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,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    static_cast<long>(__ns.count())
 	  };
 
-	int __ret = pthread_rwlock_timedrdlock(&_M_rwlock, &__ts);
+	int __ret;
+	do
+	  __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)
+	// deadlock, we just try to acquire the lock again (and will time out
+	// eventually).  Unlike for lock(), we are not allowed to throw an
+	// exception.  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).
+	while (__ret == EAGAIN || __ret == EDEADLK);
+	if (__ret == ETIMEDOUT)
 	  return false;
 	// Errors not handled: EINVAL
 	_GLIBCXX_DEBUG_ASSERT(__ret == 0);
@@ -241,7 +261,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       unlock();
     }
 
-#else // defined(__GTHREADS_CXX0X)
+#else // !_GLIBCXX_USE_PTHREADS_RWLOCKS
 
 #if _GTHREAD_USE_MUTEX_TIMEDLOCK
     struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
@@ -438,7 +458,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    _M_gate1.notify_one();
 	}
     }
-#endif // !defined(__GTHREADS_CXX0X)
+#endif // !_GLIBCXX_USE_PTHREADS_RWLOCKS
   };
 #endif // _GLIBCXX_HAS_GTHREADS
 

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patch] libstdc++/64847 add autoconf checks for pthread_rwlock_t
  2015-03-12 19:17 [patch] libstdc++/64847 add autoconf checks for pthread_rwlock_t Jonathan Wakely
@ 2015-03-13 13:46 ` Jonathan Wakely
  2015-03-18 10:55   ` Jonathan Wakely
  0 siblings, 1 reply; 3+ messages in thread
From: Jonathan Wakely @ 2015-03-13 13:46 UTC (permalink / raw)
  To: libstdc++, gcc-patches

On 12/03/15 18:49 +0000, Jonathan Wakely wrote:
>I assumed that Pthreads was enough to ensure pthread_rwlock_t but
>https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64847 shows that isn't
>true for HPUX (seems it was optional prior to POSIX 1003.1-2001).
>
>This adds an autoconf check to decide whether to use pthread_rwlock_t
>or the fallback implementation in terms of std::condition_variable and
>std::mutex.

Actually the autoconf changes aren't needed, as PR 64847 is already
fixed. Maybe it's better to have an explicit check for
pthread_rwlock_t anyway though ... I'll have a think.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [patch] libstdc++/64847 add autoconf checks for pthread_rwlock_t
  2015-03-13 13:46 ` Jonathan Wakely
@ 2015-03-18 10:55   ` Jonathan Wakely
  0 siblings, 0 replies; 3+ messages in thread
From: Jonathan Wakely @ 2015-03-18 10:55 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 967 bytes --]

On 13/03/15 13:46 +0000, Jonathan Wakely wrote:
>On 12/03/15 18:49 +0000, Jonathan Wakely wrote:
>>I assumed that Pthreads was enough to ensure pthread_rwlock_t but
>>https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64847 shows that isn't
>>true for HPUX (seems it was optional prior to POSIX 1003.1-2001).
>>
>>This adds an autoconf check to decide whether to use pthread_rwlock_t
>>or the fallback implementation in terms of std::condition_variable and
>>std::mutex.
>
>Actually the autoconf changes aren't needed, as PR 64847 is already
>fixed. Maybe it's better to have an explicit check for
>pthread_rwlock_t anyway though ... I'll have a think.

Here's what I've committed, it has Torvald's fixes for retrying on
EAGAIN, lengthy comments explaining why we retry, and also the
autoconf macros (as having a _GLIBCXX_USE_PTHREAD_RWLOCK_T macro that
can be explicitly overriden in a per-target c++config.h is probably
useful).

Tested x86_64-linux, committed to trunk.

[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 5101 bytes --]

commit 9ffe38de54cc4f07c818a6d9119ae9ef6d8bdbda
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Mar 13 13:31:36 2015 +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.

diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index 0b8c0f0..74f5a65 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -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"
diff --git a/libstdc++-v3/include/std/shared_mutex b/libstdc++-v3/include/std/shared_mutex
index 5dcc295..ab1b45b 100644
--- a/libstdc++-v3/include/std/shared_mutex
+++ b/libstdc++-v3/include/std/shared_mutex
@@ -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
 

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-03-18 10:55 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-12 19:17 [patch] libstdc++/64847 add autoconf checks for pthread_rwlock_t Jonathan Wakely
2015-03-13 13:46 ` Jonathan Wakely
2015-03-18 10:55   ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).