Skip to content

Commit

Permalink
PR libstdc++/41861 Add full steady_clock support to condition_variable
Browse files Browse the repository at this point in the history
The pthread_cond_clockwait function is available in glibc since the 2.30
release. If this function is available in the C library it can be used
to fix PR libstdc++/41861 by supporting std::chrono::steady_clock
properly with std::condition_variable.

This means that code using std::condition_variable::wait_for or
std::condition_variable::wait_until with std::chrono::steady_clock is no
longer subject to timing out early or potentially waiting for much
longer if the system clock is warped at an inopportune moment.

If pthread_cond_clockwait is available then std::chrono::steady_clock is
deemed to be the "best" clock available which means that it is used for
the relative wait_for calls and absolute wait_until calls using
user-defined clocks. Calls explicitly using std::chrono::system_clock
continue to use CLOCK_REALTIME via __gthread_cond_timedwait.

If pthread_cond_clockwait is not available then
std::chrono::system_clock is deemed to be the "best" clock available
which means that the previous suboptimal behaviour remains.

2019-09-04  Mike Crowe  <mac@mcrowe.com>

	PR libstdc++/41861
	* acinclude.m4 (GLIBCXX_CHECK_PTHREAD_COND_CLOCKWAIT): Check for new
	pthread_cond_clockwait function.
	* configure.ac: Use GLIBCXX_CHECK_PTHREAD_COND_CLOCKWAIT.
	* configure: Regenerate.
	* config.h.in: Regenerate.
	* include/std/condition_variable: (condition_variable): Rename
	__steady_clock_t typedef and add system_clock. Change __clock_t to be
	a typedef for the preferred clock to convert arbitrary other clocks to.
	[_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT] (wait_until): Add a steady_clock
	overload.
	(wait_until): Change __clock_t overload to use system_clock.
	[_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT] (__wait_until_impl): Add
	steady_clock overload that calls pthread_cond_clockwait.
	(__wait_until_impl): Change __clock_t overload to use system_clock.
	(condition_variable_any) [_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT]: Use
	steady_clock for __clock_t if pthread_cond_clockwait is available.

From-SVN: r275390
  • Loading branch information
mikecrowe authored and jwakely committed Sep 4, 2019
1 parent 76e0dd6 commit ad4d1d2
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 11 deletions.
20 changes: 20 additions & 0 deletions libstdc++-v3/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
2019-09-04 Mike Crowe <mac@mcrowe.com>

PR libstdc++/41861
* acinclude.m4 (GLIBCXX_CHECK_PTHREAD_COND_CLOCKWAIT): Check for new
pthread_cond_clockwait function.
* configure.ac: Use GLIBCXX_CHECK_PTHREAD_COND_CLOCKWAIT.
* configure: Regenerate.
* config.h.in: Regenerate.
* include/std/condition_variable: (condition_variable): Rename
__steady_clock_t typedef and add system_clock. Change __clock_t to be
a typedef for the preferred clock to convert arbitrary other clocks to.
[_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT] (wait_until): Add a steady_clock
overload.
(wait_until): Change __clock_t overload to use system_clock.
[_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT] (__wait_until_impl): Add
steady_clock overload that calls pthread_cond_clockwait.
(__wait_until_impl): Change __clock_t overload to use system_clock.
(condition_variable_any) [_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT]: Use
steady_clock for __clock_t if pthread_cond_clockwait is available.

2019-09-04 Mike Crowe <mac@mcrowe.com>

* testsuite/30_threads/condition_variable/members/2.cc (test01):
Expand Down
31 changes: 31 additions & 0 deletions libstdc++-v3/acinclude.m4
Original file line number Diff line number Diff line change
Expand Up @@ -4193,6 +4193,37 @@ AC_DEFUN([GLIBCXX_CHECK_PTHREADS_NUM_PROCESSORS_NP], [
AC_LANG_RESTORE
])

dnl
dnl Check whether pthread_cond_clockwait is available in <pthread.h> for std::condition_variable to use,
dnl and define _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT.
dnl
AC_DEFUN([GLIBCXX_CHECK_PTHREAD_COND_CLOCKWAIT], [
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
ac_save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS -fno-exceptions"
ac_save_LIBS="$LIBS"
LIBS="$LIBS -lpthread"
AC_MSG_CHECKING([for pthread_cond_clockwait])
AC_CACHE_VAL(glibcxx_cv_PTHREAD_COND_CLOCKWAIT, [
GCC_TRY_COMPILE_OR_LINK(
[#include <pthread.h>],
[pthread_mutex_t mutex; pthread_cond_t cond; struct timespec ts; int n = pthread_cond_clockwait(&cond, &mutex, 0, &ts);],
[glibcxx_cv_PTHREAD_COND_CLOCKWAIT=yes],
[glibcxx_cv_PTHREAD_COND_CLOCKWAIT=no])
])
if test $glibcxx_cv_PTHREAD_COND_CLOCKWAIT = yes; then
AC_DEFINE(_GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT, 1, [Define if pthread_cond_clockwait is available in <pthread.h>.])
fi
AC_MSG_RESULT($glibcxx_cv_PTHREAD_COND_CLOCKWAIT)
CXXFLAGS="$ac_save_CXXFLAGS"
LIBS="$ac_save_LIBS"
AC_LANG_RESTORE
])

dnl
dnl Check whether sysctl is available in <pthread.h>, and define _GLIBCXX_USE_SYSCTL_HW_NCPU.
dnl
Expand Down
3 changes: 3 additions & 0 deletions libstdc++-v3/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,9 @@
/* Define if pthreads_num_processors_np is available in <pthread.h>. */
#undef _GLIBCXX_USE_PTHREADS_NUM_PROCESSORS_NP

/* Define if pthread_cond_clockwait is available in <pthread.h>. */
#undef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT

/* Define if POSIX read/write locks are available in <gthr.h>. */
#undef _GLIBCXX_USE_PTHREAD_RWLOCK_T

Expand Down
83 changes: 83 additions & 0 deletions libstdc++-v3/configure
Original file line number Diff line number Diff line change
Expand Up @@ -21581,6 +21581,89 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu



# For pthread_cond_clockwait



ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu

ac_save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS -fno-exceptions"
ac_save_LIBS="$LIBS"
LIBS="$LIBS -lpthread"

{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_cond_clockwait" >&5
$as_echo_n "checking for pthread_cond_clockwait... " >&6; }
if ${glibcxx_cv_PTHREAD_COND_CLOCKWAIT+:} false; then :
$as_echo_n "(cached) " >&6
else

if test x$gcc_no_link = xyes; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
main ()
{
pthread_mutex_t mutex; pthread_cond_t cond; struct timespec ts; int n = pthread_cond_clockwait(&cond, &mutex, 0, &ts);
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
glibcxx_cv_PTHREAD_COND_CLOCKWAIT=yes
else
glibcxx_cv_PTHREAD_COND_CLOCKWAIT=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
else
if test x$gcc_no_link = xyes; then
as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5
fi
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
main ()
{
pthread_mutex_t mutex; pthread_cond_t cond; struct timespec ts; int n = pthread_cond_clockwait(&cond, &mutex, 0, &ts);
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
glibcxx_cv_PTHREAD_COND_CLOCKWAIT=yes
else
glibcxx_cv_PTHREAD_COND_CLOCKWAIT=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi

fi

if test $glibcxx_cv_PTHREAD_COND_CLOCKWAIT = yes; then

$as_echo "#define _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT 1" >>confdefs.h

fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $glibcxx_cv_PTHREAD_COND_CLOCKWAIT" >&5
$as_echo "$glibcxx_cv_PTHREAD_COND_CLOCKWAIT" >&6; }

CXXFLAGS="$ac_save_CXXFLAGS"
LIBS="$ac_save_LIBS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu




ac_fn_c_check_header_mongrel "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default"
if test "x$ac_cv_header_locale_h" = xyes; then :
Expand Down
3 changes: 3 additions & 0 deletions libstdc++-v3/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ GLIBCXX_ENABLE_LIBSTDCXX_TIME
# Check for tmpnam which is obsolescent in POSIX.1-2008
GLIBCXX_CHECK_TMPNAM

# For pthread_cond_clockwait
GLIBCXX_CHECK_PTHREAD_COND_CLOCKWAIT

AC_LC_MESSAGES

# For hardware_concurrency
Expand Down
62 changes: 51 additions & 11 deletions libstdc++-v3/include/std/condition_variable
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// condition_variable
class condition_variable
{
typedef chrono::system_clock __clock_t;
typedef chrono::steady_clock __steady_clock_t;
using steady_clock = chrono::steady_clock;
using system_clock = chrono::system_clock;
#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
using __clock_t = steady_clock;
#else
using __clock_t = system_clock;
#endif
typedef __gthread_cond_t __native_type;

#ifdef __GTHREAD_COND_INIT
Expand Down Expand Up @@ -101,18 +106,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
wait(__lock);
}

#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
template<typename _Duration>
cv_status
wait_until(unique_lock<mutex>& __lock,
const chrono::time_point<steady_clock, _Duration>& __atime)
{ return __wait_until_impl(__lock, __atime); }
#endif

template<typename _Duration>
cv_status
wait_until(unique_lock<mutex>& __lock,
const chrono::time_point<__clock_t, _Duration>& __atime)
const chrono::time_point<system_clock, _Duration>& __atime)
{ return __wait_until_impl(__lock, __atime); }

template<typename _Clock, typename _Duration>
cv_status
wait_until(unique_lock<mutex>& __lock,
const chrono::time_point<_Clock, _Duration>& __atime)
{
// DR 887 - Sync unknown clock to known clock.
const typename _Clock::time_point __c_entry = _Clock::now();
const __clock_t::time_point __s_entry = __clock_t::now();
const auto __delta = __atime - __c_entry;
Expand Down Expand Up @@ -145,11 +157,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
wait_for(unique_lock<mutex>& __lock,
const chrono::duration<_Rep, _Period>& __rtime)
{
using __dur = typename __steady_clock_t::duration;
using __dur = typename steady_clock::duration;
auto __reltime = chrono::duration_cast<__dur>(__rtime);
if (__reltime < __rtime)
++__reltime;
return wait_until(__lock, __steady_clock_t::now() + __reltime);
return wait_until(__lock, steady_clock::now() + __reltime);
}

template<typename _Rep, typename _Period, typename _Predicate>
Expand All @@ -158,11 +170,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
const chrono::duration<_Rep, _Period>& __rtime,
_Predicate __p)
{
using __dur = typename __steady_clock_t::duration;
using __dur = typename steady_clock::duration;
auto __reltime = chrono::duration_cast<__dur>(__rtime);
if (__reltime < __rtime)
++__reltime;
return wait_until(__lock, __steady_clock_t::now() + __reltime,
return wait_until(__lock, steady_clock::now() + __reltime,
std::move(__p));
}

Expand All @@ -171,10 +183,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return &_M_cond; }

private:
#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
template<typename _Dur>
cv_status
__wait_until_impl(unique_lock<mutex>& __lock,
const chrono::time_point<steady_clock, _Dur>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);

__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.time_since_epoch().count()),
static_cast<long>(__ns.count())
};

pthread_cond_clockwait(&_M_cond, __lock.mutex()->native_handle(),
CLOCK_MONOTONIC,
&__ts);

return (steady_clock::now() < __atime
? cv_status::no_timeout : cv_status::timeout);
}
#endif

template<typename _Dur>
cv_status
__wait_until_impl(unique_lock<mutex>& __lock,
const chrono::time_point<__clock_t, _Dur>& __atime)
const chrono::time_point<system_clock, _Dur>& __atime)
{
auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
Expand All @@ -188,7 +224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__gthread_cond_timedwait(&_M_cond, __lock.mutex()->native_handle(),
&__ts);

return (__clock_t::now() < __atime
return (system_clock::now() < __atime
? cv_status::no_timeout : cv_status::timeout);
}
};
Expand All @@ -208,7 +244,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Like above, but mutex is not required to have try_lock.
class condition_variable_any
{
typedef chrono::system_clock __clock_t;
#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
using __clock_t = chrono::steady_clock;
#else
using __clock_t = chrono::system_clock;
#endif
condition_variable _M_cond;
shared_ptr<mutex> _M_mutex;

Expand Down

0 comments on commit ad4d1d2

Please sign in to comment.