From cf115d2cc6bb658db58f4b0b7a2da078a18a62ef Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Thu, 30 Aug 2012 19:14:33 +0000 Subject: [PATCH] Change sleep_for, sleep_until, and the condition_variable timed wait functions to protect against duration and time_point overflow. Since we're about to wait anyway, we can afford to spend a few more cycles on this checking. I purposefully did not treat the timed try_locks with overflow checking. This fixes http://llvm.org/bugs/show_bug.cgi?id=13721 . I'm unsure if the standard needs clarification in this area, or if this is simply QOI. The facilities were never intended to overflow check, but just to not overflow if durations stayed within +/- 292 years. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@162925 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/__mutex_base | 32 ++++++++++---------------------- include/thread | 18 ++++++++++++++---- src/condition_variable.cpp | 16 ++++++++++++++-- src/thread.cpp | 18 +++++++++++++++--- 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/include/__mutex_base b/include/__mutex_base index 4fdcb617..1782e7c0 100644 --- a/include/__mutex_base +++ b/include/__mutex_base @@ -323,11 +323,6 @@ public: template void wait(unique_lock& __lk, _Predicate __pred); - template - cv_status - wait_until(unique_lock& __lk, - const chrono::time_point& __t); - template cv_status wait_until(unique_lock& __lk, @@ -382,28 +377,13 @@ condition_variable::wait(unique_lock& __lk, _Predicate __pred) wait(__lk); } -template -cv_status -condition_variable::wait_until(unique_lock& __lk, - const chrono::time_point& __t) -{ - using namespace chrono; - typedef time_point __nano_sys_tmpt; - __do_timed_wait(__lk, - __nano_sys_tmpt(__ceil(__t.time_since_epoch()))); - return system_clock::now() < __t ? cv_status::no_timeout : - cv_status::timeout; -} - template cv_status condition_variable::wait_until(unique_lock& __lk, const chrono::time_point<_Clock, _Duration>& __t) { using namespace chrono; - system_clock::time_point __s_now = system_clock::now(); - typename _Clock::time_point __c_now = _Clock::now(); - __do_timed_wait(__lk, __s_now + __ceil(__t - __c_now)); + wait_for(__lk, __t - _Clock::now()); return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout; } @@ -427,9 +407,17 @@ condition_variable::wait_for(unique_lock& __lk, const chrono::duration<_Rep, _Period>& __d) { using namespace chrono; + if (__d <= __d.zero()) + return cv_status::timeout; + typedef time_point > __sys_tpf; + typedef time_point __sys_tpi; + __sys_tpf _Max = __sys_tpi::max(); system_clock::time_point __s_now = system_clock::now(); steady_clock::time_point __c_now = steady_clock::now(); - __do_timed_wait(__lk, __s_now + __ceil(__d)); + if (_Max - __d > __s_now) + __do_timed_wait(__lk, __s_now + __ceil(__d)); + else + __do_timed_wait(__lk, __sys_tpi::max()); return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout; } diff --git a/include/thread b/include/thread index d81e8537..94e7ab6c 100644 --- a/include/thread +++ b/include/thread @@ -410,10 +410,20 @@ void sleep_for(const chrono::duration<_Rep, _Period>& __d) { using namespace chrono; - nanoseconds __ns = duration_cast(__d); - if (__ns < __d) - ++__ns; - sleep_for(__ns); + if (__d > duration<_Rep, _Period>::zero()) + { + _LIBCPP_CONSTEXPR duration _Max = nanoseconds::max(); + nanoseconds __ns; + if (__d < _Max) + { + __ns = duration_cast(__d); + if (__ns < __d) + ++__ns; + } + else + __ns = nanoseconds::max(); + sleep_for(__ns); + } } template diff --git a/src/condition_variable.cpp b/src/condition_variable.cpp index 552bce35..de0f6f47 100644 --- a/src/condition_variable.cpp +++ b/src/condition_variable.cpp @@ -51,10 +51,22 @@ condition_variable::__do_timed_wait(unique_lock& lk, __throw_system_error(EPERM, "condition_variable::timed wait: mutex not locked"); nanoseconds d = tp.time_since_epoch(); + if (d > nanoseconds(0x59682F000000E941)) + d = nanoseconds(0x59682F000000E941); timespec ts; seconds s = duration_cast(d); - ts.tv_sec = static_cast(s.count()); - ts.tv_nsec = static_cast((d - s).count()); + typedef decltype(ts.tv_sec) ts_sec; + _LIBCPP_CONSTEXPR ts_sec ts_sec_max = numeric_limits::max(); + if (s.count() < ts_sec_max) + { + ts.tv_sec = static_cast(s.count()); + ts.tv_nsec = static_cast((d - s).count()); + } + else + { + ts.tv_sec = ts_sec_max; + ts.tv_nsec = giga::num - 1; + } int ec = pthread_cond_timedwait(&__cv_, lk.mutex()->native_handle(), &ts); if (ec != 0 && ec != ETIMEDOUT) __throw_system_error(ec, "condition_variable timed_wait failed"); diff --git a/src/thread.cpp b/src/thread.cpp index 4445b8db..8747adf0 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -11,6 +11,7 @@ #include "exception" #include "vector" #include "future" +#include "limits" #include #if !_WIN32 #if !__sun__ && !__linux__ @@ -83,11 +84,22 @@ void sleep_for(const chrono::nanoseconds& ns) { using namespace chrono; - if (ns >= nanoseconds::zero()) + if (ns > nanoseconds::zero()) { + seconds s = duration_cast(ns); timespec ts; - ts.tv_sec = static_cast(duration_cast(ns).count()); - ts.tv_nsec = static_cast((ns - seconds(ts.tv_sec)).count()); + typedef decltype(ts.tv_sec) ts_sec; + _LIBCPP_CONSTEXPR ts_sec ts_sec_max = numeric_limits::max(); + if (s.count() < ts_sec_max) + { + ts.tv_sec = static_cast(s.count()); + ts.tv_nsec = static_cast((ns-s).count()); + } + else + { + ts.tv_sec = ts_sec_max; + ts.tv_nsec = giga::num - 1; + } nanosleep(&ts, 0); } }