+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
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"
/* 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
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.
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
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
#
# 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()
# Fake what AC_TRY_COMPILE does.
cat > conftest.$ac_ext << EOF
-#line 15420 "configure"
+#line 15474 "configure"
int main()
{
typedef bool atomic_type;
rm -f conftest*
cat > conftest.$ac_ext << EOF
-#line 15455 "configure"
+#line 15509 "configure"
int main()
{
typedef short atomic_type;
rm -f conftest*
cat > conftest.$ac_ext << EOF
-#line 15490 "configure"
+#line 15544 "configure"
int main()
{
// NB: _Atomic_word not necessarily int.
rm -f conftest*
cat > conftest.$ac_ext << EOF
-#line 15526 "configure"
+#line 15580 "configure"
int main()
{
typedef long long atomic_type;
# unnecessary for this test.
cat > conftest.$ac_ext << EOF
-#line 15605 "configure"
+#line 15659 "configure"
int main()
{
_Decimal32 d1;
# 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; };
rm -f conftest*
cat > conftest.$ac_ext << EOF
-#line 15681 "configure"
+#line 15735 "configure"
template<typename T1, typename T2>
struct same
{ typedef T2 type; };
$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"
/// 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()
// 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;
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);
}
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);
unlock();
}
-#else // defined(__GTHREADS_CXX0X)
+#else // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
struct _Mutex : mutex, __timed_mutex_impl<_Mutex>
_M_gate1.notify_one();
}
}
-#endif // !defined(__GTHREADS_CXX0X)
+#endif // ! _GLIBCXX_USE_PTHREAD_RWLOCK_T
};
#endif // _GLIBCXX_HAS_GTHREADS