diff --git a/include/boost/thread/pthread/condition_variable.hpp b/include/boost/thread/pthread/condition_variable.hpp index 2ee1c800..e64c4e76 100644 --- a/include/boost/thread/pthread/condition_variable.hpp +++ b/include/boost/thread/pthread/condition_variable.hpp @@ -99,6 +99,15 @@ namespace boost } } + // When this function returns true: + // * A notification (or sometimes a spurious OS signal) has been received + // * Do not assume that the timeout has not been reached + // * Do not assume that the predicate has been changed + // + // When this function returns false: + // * The timeout has been reached + // * Do not assume that a notification has not been received + // * Do not assume that the predicate has not been changed inline bool condition_variable::do_wait_until( unique_lock& m, detail::internal_platform_timepoint const &timeout) @@ -209,7 +218,10 @@ namespace boost template void wait(lock_type& m,predicate_type pred) { - while(!pred()) wait(m); + while (!pred()) + { + wait(m); + } } #if defined BOOST_THREAD_USES_DATETIME @@ -217,23 +229,23 @@ namespace boost bool timed_wait(lock_type& m,boost::system_time const& abs_time) { #if defined BOOST_THREAD_WAIT_BUG - boost::system_time const& abs_time_fixed = abs_time + BOOST_THREAD_WAIT_BUG; + const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); #else - boost::system_time const& abs_time_fixed = abs_time; + const detail::real_platform_timepoint ts(abs_time); #endif #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO - const detail::real_platform_timepoint ts(abs_time_fixed); - detail::platform_duration d = ts - detail::real_platform_clock::now(); - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) - { - d = ts - detail::real_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return false; - d = (std::min)(d, detail::platform_milliseconds(100)); - } - return true; + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + const detail::platform_duration d = ts - detail::real_platform_clock::now(); + do_wait_until(m, detail::internal_platform_clock::now() + d); + return ts > detail::real_platform_clock::now(); #else - return do_wait_until(m, detail::internal_platform_timepoint(abs_time_fixed)); + return do_wait_until(m, ts); #endif } template @@ -256,15 +268,16 @@ namespace boost } detail::platform_duration d(wait_duration); #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) - const detail::mono_platform_timepoint& ts = detail::mono_platform_clock::now() + d; - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) - { - d = ts - detail::mono_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return false; - d = (std::min)(d, detail::platform_milliseconds(100)); - } - return true; + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); + do_wait_until(m, detail::internal_platform_clock::now() + d); + return ts > detail::mono_platform_clock::now(); #else return do_wait_until(m, detail::internal_platform_clock::now() + d); #endif @@ -273,12 +286,26 @@ namespace boost template bool timed_wait(lock_type& m,boost::system_time const& abs_time, predicate_type pred) { +#if defined BOOST_THREAD_WAIT_BUG + const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); +#else + const detail::real_platform_timepoint ts(abs_time); +#endif while (!pred()) { - if(!timed_wait(m, abs_time)) - return pred(); +#if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + detail::platform_duration d = ts - detail::real_platform_clock::now(); + if (d <= detail::platform_duration::zero()) break; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(100)); + do_wait_until(m, detail::internal_platform_clock::now() + d); +#else + if (!do_wait_until(m, ts)) break; // timeout occurred +#endif } - return true; + return pred(); } template @@ -304,18 +331,25 @@ namespace boost } detail::platform_duration d(wait_duration); #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) - const detail::mono_platform_timepoint& ts = detail::mono_platform_clock::now() + d; - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! pred() && ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); + while (!pred()) { - d = ts - detail::mono_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return pred(); - d = (std::min)(d, detail::platform_milliseconds(100)); + if (d <= detail::platform_duration::zero()) break; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(100)); + do_wait_until(m, detail::internal_platform_clock::now() + d); + d = ts - detail::mono_platform_clock::now(); } - return pred(); #else - return do_wait_until(m, detail::internal_platform_clock::now() + d, move(pred)); + const detail::internal_platform_timepoint ts(detail::internal_platform_clock::now() + d); + while (!pred()) + { + if (!do_wait_until(m, ts)) break; // timeout occurred + } #endif + return pred(); } #endif @@ -326,9 +360,9 @@ namespace boost lock_type& lock, const chrono::time_point& t) { - const boost::detail::internal_platform_timepoint ts(t); - if (do_wait_until(lock, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + const boost::detail::internal_platform_timepoint ts(t); + if (do_wait_until(lock, ts)) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -337,16 +371,18 @@ namespace boost lock_type& lock, const chrono::time_point& t) { - typedef typename common_type::type CD; - CD d = t - Clock::now(); - d = (std::min)(d, CD(chrono::milliseconds(100))); - while (cv_status::timeout == wait_until(lock, detail::internal_chrono_clock::now() + d)) - { - d = t - Clock::now(); - if ( d <= CD::zero() ) return cv_status::timeout; - d = (std::min)(d, CD(chrono::milliseconds(100))); - } - return cv_status::no_timeout; + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + typedef typename common_type::type CD; + CD d = t - Clock::now(); + do_wait_until(lock, detail::internal_chrono_clock::now() + d); + if (t > Clock::now()) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -355,7 +391,22 @@ namespace boost lock_type& lock, const chrono::duration& d) { - return wait_until(lock, chrono::steady_clock::now() + d); + return wait_until(lock, chrono::steady_clock::now() + d); + } + + template + bool + wait_until( + lock_type& lock, + const chrono::time_point& t, + Predicate pred) + { + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } template @@ -365,12 +416,18 @@ namespace boost const chrono::time_point& t, Predicate pred) { + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + typedef typename common_type::type CD; while (!pred()) { - if (wait_until(lock, t) == cv_status::timeout) - return pred(); + CD d = t - Clock::now(); + if (d <= CD::zero()) break; // timeout occurred + d = (std::min)(d, CD(chrono::milliseconds(100))); + do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - return true; + return pred(); } template @@ -380,7 +437,7 @@ namespace boost const chrono::duration& d, Predicate pred) { - return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred)); + return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred)); } #endif @@ -397,6 +454,15 @@ namespace boost } private: + // When this function returns true: + // * A notification (or sometimes a spurious OS signal) has been received + // * Do not assume that the timeout has not been reached + // * Do not assume that the predicate has been changed + // + // When this function returns false: + // * The timeout has been reached + // * Do not assume that a notification has not been received + // * Do not assume that the predicate has not been changed template bool do_wait_until( lock_type& m, @@ -428,19 +494,6 @@ namespace boost } return true; } - template - bool do_wait_until( - lock_type& lock, - detail::internal_platform_timepoint const& t, - Predicate pred) - { - while (!pred()) - { - if ( ! do_wait_until(lock, t) ) - return pred(); - } - return true; - } }; } diff --git a/include/boost/thread/pthread/condition_variable_fwd.hpp b/include/boost/thread/pthread/condition_variable_fwd.hpp index 4ac94165..104185db 100644 --- a/include/boost/thread/pthread/condition_variable_fwd.hpp +++ b/include/boost/thread/pthread/condition_variable_fwd.hpp @@ -47,19 +47,6 @@ namespace boost inline bool do_wait_until( unique_lock& lock, detail::internal_platform_timepoint const &timeout); - template - bool do_wait_until( - unique_lock& lock, - detail::internal_platform_timepoint const &timeout, - Predicate pred) - { - while (!pred()) - { - if ( ! do_wait_until(lock, timeout) ) - return pred(); - } - return true; - } public: BOOST_THREAD_NO_COPYABLE(condition_variable) @@ -108,7 +95,10 @@ namespace boost template void wait(unique_lock& m,predicate_type pred) { - while(!pred()) wait(m); + while (!pred()) + { + wait(m); + } } #if defined BOOST_THREAD_USES_DATETIME @@ -117,23 +107,23 @@ namespace boost boost::system_time const& abs_time) { #if defined BOOST_THREAD_WAIT_BUG - boost::system_time const& abs_time_fixed = abs_time + BOOST_THREAD_WAIT_BUG; + const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); #else - boost::system_time const& abs_time_fixed = abs_time; + const detail::real_platform_timepoint ts(abs_time); #endif #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO - const detail::real_platform_timepoint ts(abs_time_fixed); - detail::platform_duration d = ts - detail::real_platform_clock::now(); - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) - { - d = ts - detail::real_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return false; - d = (std::min)(d, detail::platform_milliseconds(100)); - } - return true; + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + const detail::platform_duration d = ts - detail::real_platform_clock::now(); + do_wait_until(m, detail::internal_platform_clock::now() + d); + return ts > detail::real_platform_clock::now(); #else - return do_wait_until(m, detail::internal_platform_timepoint(abs_time_fixed)); + return do_wait_until(m, ts); #endif } bool timed_wait( @@ -159,15 +149,16 @@ namespace boost } detail::platform_duration d(wait_duration); #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) - const detail::mono_platform_timepoint& ts = detail::mono_platform_clock::now() + d; - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) - { - d = ts - detail::mono_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return false; - d = (std::min)(d, detail::platform_milliseconds(100)); - } - return true; + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); + do_wait_until(m, detail::internal_platform_clock::now() + d); + return ts > detail::mono_platform_clock::now(); #else return do_wait_until(m, detail::internal_platform_clock::now() + d); #endif @@ -178,12 +169,26 @@ namespace boost unique_lock& m, boost::system_time const& abs_time,predicate_type pred) { +#if defined BOOST_THREAD_WAIT_BUG + const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); +#else + const detail::real_platform_timepoint ts(abs_time); +#endif while (!pred()) { - if(!timed_wait(m, abs_time)) - return pred(); +#if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + detail::platform_duration d = ts - detail::real_platform_clock::now(); + if (d <= detail::platform_duration::zero()) break; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(100)); + do_wait_until(m, detail::internal_platform_clock::now() + d); +#else + if (!do_wait_until(m, ts)) break; // timeout occurred +#endif } - return true; + return pred(); } template @@ -197,13 +202,13 @@ namespace boost template bool timed_wait( unique_lock& m, - duration_type const& wait_duration, predicate_type pred) + duration_type const& wait_duration,predicate_type pred) { if (wait_duration.is_pos_infinity()) { while (!pred()) { - wait(m); + wait(m); } return true; } @@ -213,18 +218,25 @@ namespace boost } detail::platform_duration d(wait_duration); #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) - const detail::mono_platform_timepoint& ts = detail::mono_platform_clock::now() + d; - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! pred() && ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); + while (!pred()) { - d = ts - detail::mono_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return pred(); - d = (std::min)(d, detail::platform_milliseconds(100)); + if (d <= detail::platform_duration::zero()) break; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(100)); + do_wait_until(m, detail::internal_platform_clock::now() + d); + d = ts - detail::mono_platform_clock::now(); } - return pred(); #else - return do_wait_until(m, detail::internal_platform_clock::now() + d, move(pred)); + const detail::internal_platform_timepoint ts(detail::internal_platform_clock::now() + d); + while (!pred()) + { + if (!do_wait_until(m, ts)) break; // timeout occurred + } #endif + return pred(); } #endif @@ -236,9 +248,9 @@ namespace boost unique_lock& lock, const chrono::time_point& t) { - const detail::internal_platform_timepoint ts(t); - if (do_wait_until(lock, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + const detail::internal_platform_timepoint ts(t); + if (do_wait_until(lock, ts)) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -247,16 +259,18 @@ namespace boost unique_lock& lock, const chrono::time_point& t) { - typedef typename common_type::type CD; - CD d = t - Clock::now(); - d = (std::min)(d, CD(chrono::milliseconds(100))); - while (cv_status::timeout == wait_until(lock, detail::internal_chrono_clock::now() + d)) - { - d = t - Clock::now(); - if ( d <= CD::zero() ) return cv_status::timeout; - d = (std::min)(d, CD(chrono::milliseconds(100))); - } - return cv_status::no_timeout; + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + typedef typename common_type::type CD; + CD d = t - Clock::now(); + do_wait_until(lock, detail::internal_chrono_clock::now() + d); + if (t > Clock::now()) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -265,7 +279,22 @@ namespace boost unique_lock& lock, const chrono::duration& d) { - return wait_until(lock, chrono::steady_clock::now() + d); + return wait_until(lock, chrono::steady_clock::now() + d); + } + + template + bool + wait_until( + unique_lock& lock, + const chrono::time_point& t, + Predicate pred) + { + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } template @@ -275,12 +304,18 @@ namespace boost const chrono::time_point& t, Predicate pred) { + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + typedef typename common_type::type CD; while (!pred()) { - if (wait_until(lock, t) == cv_status::timeout) - return pred(); + CD d = t - Clock::now(); + if (d <= CD::zero()) break; // timeout occurred + d = (std::min)(d, CD(chrono::milliseconds(100))); + do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - return true; + return pred(); } template @@ -290,7 +325,7 @@ namespace boost const chrono::duration& d, Predicate pred) { - return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred)); + return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred)); } #endif diff --git a/include/boost/thread/win32/condition_variable.hpp b/include/boost/thread/win32/condition_variable.hpp index bcb9e23a..45310d28 100644 --- a/include/boost/thread/win32/condition_variable.hpp +++ b/include/boost/thread/win32/condition_variable.hpp @@ -90,7 +90,7 @@ namespace boost return notified; } - bool do_wait_until(detail::internal_platform_timepoint const &timeout) + bool interruptible_wait(detail::internal_platform_timepoint const &timeout) { return this_thread::interruptible_wait(semaphore, timeout); } @@ -243,6 +243,15 @@ namespace boost ~basic_condition_variable() {} + // When this function returns true: + // * A notification (or sometimes a spurious OS signal) has been received + // * Do not assume that the timeout has not been reached + // * Do not assume that the predicate has been changed + // + // When this function returns false: + // * The timeout has been reached + // * Do not assume that a notification has not been received + // * Do not assume that the predicate has not been changed template bool do_wait_until(lock_type& lock, detail::internal_platform_timepoint const &timeout) { @@ -253,7 +262,7 @@ namespace boost bool woken=false; while(!woken) { - if(!entry->do_wait_until(timeout)) + if(!entry->interruptible_wait(timeout)) { return false; } @@ -331,31 +340,27 @@ namespace boost template void wait(unique_lock& m,predicate_type pred) { - while(!pred()) wait(m); + while (!pred()) + { + wait(m); + } } - #if defined BOOST_THREAD_USES_DATETIME bool timed_wait(unique_lock& m,boost::system_time const& abs_time) { -#if 1 + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. const detail::real_platform_timepoint ts(abs_time); - detail::platform_duration d = ts - detail::real_platform_clock::now(); - return do_wait_until(m, detail::internal_platform_clock::now() + d); -#else // fixme: this code allows notifications to be missed - const detail::real_platform_timepoint ts(abs_time); - detail::platform_duration d = ts - detail::real_platform_clock::now(); - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) - { - d = ts - detail::real_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return false; - d = (std::min)(d, detail::platform_milliseconds(100)); - } - return true; -#endif + const detail::platform_duration d = ts - detail::real_platform_clock::now(); + do_wait_until(m, detail::internal_platform_clock::now() + d); + return ts > detail::real_platform_clock::now(); } - bool timed_wait(unique_lock& m,boost::xtime const& abs_time) { return timed_wait(m, system_time(abs_time)); @@ -365,27 +370,32 @@ namespace boost { if (wait_duration.is_pos_infinity()) { - wait(m); - return true; + wait(m); + return true; } if (wait_duration.is_special()) { - return true; + return true; } - const detail::internal_platform_timepoint ts = detail::internal_platform_clock::now() - + detail::platform_duration(wait_duration); - return do_wait_until(m, ts); + const detail::platform_duration d(wait_duration); + return do_wait_until(m, detail::internal_platform_clock::now() + d); } template bool timed_wait(unique_lock& m,boost::system_time const& abs_time,predicate_type pred) { + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + const detail::real_platform_timepoint ts(abs_time); while (!pred()) { - if(!timed_wait(m, abs_time)) - return pred(); + detail::platform_duration d = ts - detail::real_platform_clock::now(); + if (d <= detail::platform_duration::zero()) break; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(100)); + do_wait_until(m, detail::internal_platform_clock::now() + d); } - return true; + return pred(); } template bool timed_wait(unique_lock& m,boost::xtime const& abs_time,predicate_type pred) @@ -397,24 +407,23 @@ namespace boost { if (wait_duration.is_pos_infinity()) { - while (!pred()) - { - wait(m); - } - return true; + while (!pred()) + { + wait(m); + } + return true; } if (wait_duration.is_special()) { - return pred(); - } - const detail::internal_platform_timepoint ts = detail::internal_platform_clock::now() - + detail::platform_duration(wait_duration); - while (!pred()) - { - if(!do_wait_until(m, ts)) return pred(); } - return true; + const detail::platform_duration d(wait_duration); + const detail::internal_platform_timepoint ts(detail::internal_platform_clock::now() + d); + while (!pred()) + { + if (!do_wait_until(m, ts)) break; // timeout occurred + } + return pred(); } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -424,9 +433,9 @@ namespace boost unique_lock& lock, const chrono::time_point& t) { - const detail::internal_platform_timepoint ts(t); - if (do_wait_until(lock, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + const detail::internal_platform_timepoint ts(t); + if (do_wait_until(lock, ts)) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -435,21 +444,18 @@ namespace boost unique_lock& lock, const chrono::time_point& t) { -#if 1 - Duration d = t - Clock::now(); - return wait_until(lock, detail::internal_chrono_clock::now() + d); -#else // fixme: this code allows notifications to be missed - typedef typename common_type::type CD; - CD d = t - Clock::now(); - d = (std::min)(d, CD(chrono::milliseconds(100))); - while (cv_status::timeout == wait_until(lock, detail::internal_chrono_clock::now() + d)) - { - d = t - Clock::now(); - if ( d <= CD::zero() ) return cv_status::timeout; - d = (std::min)(d, CD(chrono::milliseconds(100))); - } - return cv_status::no_timeout; -#endif + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + typedef typename common_type::type CD; + CD d = t - Clock::now(); + do_wait_until(lock, detail::internal_chrono_clock::now() + d); + if (t > Clock::now()) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -458,7 +464,22 @@ namespace boost unique_lock& lock, const chrono::duration& d) { - return wait_until(lock, chrono::steady_clock::now() + d); + return wait_until(lock, chrono::steady_clock::now() + d); + } + + template + bool + wait_until( + unique_lock& lock, + const chrono::time_point& t, + Predicate pred) + { + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } template @@ -468,12 +489,18 @@ namespace boost const chrono::time_point& t, Predicate pred) { + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + typedef typename common_type::type CD; while (!pred()) { - if (wait_until(lock, t) == cv_status::timeout) - return pred(); + CD d = t - Clock::now(); + if (d <= CD::zero()) break; // timeout occurred + d = (std::min)(d, CD(chrono::milliseconds(100))); + do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - return true; + return pred(); } template @@ -509,29 +536,27 @@ namespace boost template void wait(lock_type& m,predicate_type pred) { - while(!pred()) wait(m); + while (!pred()) + { + wait(m); + } } #if defined BOOST_THREAD_USES_DATETIME template bool timed_wait(lock_type& m,boost::system_time const& abs_time) { -#if 1 + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. const detail::real_platform_timepoint ts(abs_time); - detail::platform_duration d = ts - detail::real_platform_clock::now(); - return do_wait_until(m, detail::internal_platform_clock::now() + d); -#else // fixme: this code allows notifications to be missed - const detail::real_platform_timepoint ts(abs_time); - detail::platform_duration d = ts - detail::real_platform_clock::now(); - d = (std::min)(d, detail::platform_milliseconds(100)); - while ( ! do_wait_until(m, detail::internal_platform_clock::now() + d) ) - { - d = ts - detail::real_platform_clock::now(); - if ( d <= detail::platform_duration::zero() ) return false; - d = (std::min)(d, detail::platform_milliseconds(100)); - } - return true; -#endif + const detail::platform_duration d = ts - detail::real_platform_clock::now(); + do_wait_until(m, detail::internal_platform_clock::now() + d); + return ts > detail::real_platform_clock::now(); } template @@ -545,27 +570,32 @@ namespace boost { if (wait_duration.is_pos_infinity()) { - wait(m); - return true; + wait(m); + return true; } if (wait_duration.is_special()) { - return true; + return true; } - const detail::internal_platform_timepoint ts = detail::internal_platform_clock::now() - + detail::platform_duration(wait_duration); - return do_wait_until(m, ts); + const detail::platform_duration d(wait_duration); + return do_wait_until(m, detail::internal_platform_clock::now() + d); } template bool timed_wait(lock_type& m,boost::system_time const& abs_time,predicate_type pred) { + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + const detail::real_platform_timepoint ts(abs_time); while (!pred()) { - if(!timed_wait(m, abs_time)) - return pred(); + detail::platform_duration d = ts - detail::real_platform_clock::now(); + if (d <= detail::platform_duration::zero()) break; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(100)); + do_wait_until(m, detail::internal_platform_clock::now() + d); } - return true; + return pred(); } template @@ -579,24 +609,23 @@ namespace boost { if (wait_duration.is_pos_infinity()) { - while (!pred()) - { - wait(m); - } - return true; + while (!pred()) + { + wait(m); + } + return true; } if (wait_duration.is_special()) { - return pred(); - } - const detail::internal_platform_timepoint ts = detail::internal_platform_clock::now() - + detail::platform_duration(wait_duration); - while (!pred()) - { - if(!do_wait_until(m, ts)) return pred(); } - return true; + const detail::platform_duration d(wait_duration); + const detail::internal_platform_timepoint ts(detail::internal_platform_clock::now() + d); + while (!pred()) + { + if (!do_wait_until(m, ts)) break; // timeout occurred + } + return pred(); } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -606,9 +635,9 @@ namespace boost lock_type& lock, const chrono::time_point& t) { - const detail::internal_platform_timepoint ts(t); - if (do_wait_until(lock, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + const detail::internal_platform_timepoint ts(t); + if (do_wait_until(lock, ts)) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -617,21 +646,18 @@ namespace boost lock_type& lock, const chrono::time_point& t) { -#if 1 - Duration d = t - Clock::now(); - return wait_until(lock, detail::internal_chrono_clock::now() + d); -#else // fixme: this code allows notifications to be missed - typedef typename common_type::type CD; - CD d = t - Clock::now(); - d = (std::min)(d, CD(chrono::milliseconds(100))); - while (cv_status::timeout == wait_until(lock, detail::internal_chrono_clock::now() + d)) - { - d = t - Clock::now(); - if ( d <= CD::zero() ) return cv_status::timeout; - d = (std::min)(d, CD(chrono::milliseconds(100))); - } - return cv_status::no_timeout; -#endif + // The system time may jump while this function is waiting. To compensate for this and time + // out near the correct time, we could call do_wait_until() in a loop with a short timeout + // and recheck the time remaining each time through the loop. However, because we can't + // check the predicate each time do_wait_until() completes, this introduces the possibility + // of not exiting the function when a notification occurs, since do_wait_until() may report + // that it timed out even though a notification was received. The best this function can do + // is report correctly whether or not it reached the timeout time. + typedef typename common_type::type CD; + CD d = t - Clock::now(); + do_wait_until(lock, detail::internal_chrono_clock::now() + d); + if (t > Clock::now()) return cv_status::no_timeout; + else return cv_status::timeout; } template @@ -640,7 +666,22 @@ namespace boost lock_type& lock, const chrono::duration& d) { - return wait_until(lock, chrono::steady_clock::now() + d); + return wait_until(lock, chrono::steady_clock::now() + d); + } + + template + bool + wait_until( + lock_type& lock, + const chrono::time_point& t, + Predicate pred) + { + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } template @@ -650,12 +691,18 @@ namespace boost const chrono::time_point& t, Predicate pred) { + // The system time may jump while this function is waiting. To compensate for this + // and time out near the correct time, we call do_wait_until() in a loop with a + // short timeout and recheck the time remaining each time through the loop. + typedef typename common_type::type CD; while (!pred()) { - if (wait_until(lock, t) == cv_status::timeout) - return pred(); + CD d = t - Clock::now(); + if (d <= CD::zero()) break; // timeout occurred + d = (std::min)(d, CD(chrono::milliseconds(100))); + do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - return true; + return pred(); } template diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 78b2d00e..15cef271 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -361,11 +361,12 @@ namespace boost unique_lock lock(local_thread_info->data_mutex); while(!local_thread_info->done) { - if(!local_thread_info->done_condition.do_wait_until(lock,timeout)) - { - res=false; - return true; - } + if(!local_thread_info->done_condition.do_wait_until(lock,timeout)) break; // timeout occurred + } + if(!local_thread_info->done) + { + res=false; + return true; } do_join=!local_thread_info->join_started; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 24f89819..aa51c0db 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -325,7 +325,7 @@ rule thread-compile ( sources : reqs * : name ) [ thread-run2-noit ./sync/conditions/condition_variable/wait_for_pred_pass.cpp : condition_variable__wait_for_pred_p ] [ thread-run2-noit ./sync/conditions/condition_variable/wait_until_pass.cpp : condition_variable__wait_until_p ] [ thread-run2-noit ./sync/conditions/condition_variable/wait_until_pred_pass.cpp : condition_variable__wait_until_pred_p ] - #[ thread-run2-noit ./sync/conditions/condition_variable/lost_notif_pass.cpp : condition_variable__lost_notif_p ] + [ thread-run2-noit ./sync/conditions/condition_variable/lost_notif_pass.cpp : condition_variable__lost_notif_p ] [ thread-compile-fail ./sync/conditions/condition_variable_any/assign_fail.cpp : : condition_variable_any__assign_f ] [ thread-compile-fail ./sync/conditions/condition_variable_any/copy_fail.cpp : : condition_variable_any__copy_f ] @@ -335,7 +335,7 @@ rule thread-compile ( sources : reqs * : name ) [ thread-run2-noit ./sync/conditions/condition_variable_any/wait_for_pred_pass.cpp : condition_variable_any__wait_for_pred_p ] [ thread-run2-noit ./sync/conditions/condition_variable_any/wait_until_pass.cpp : condition_variable_any__wait_until_p ] [ thread-run2-noit ./sync/conditions/condition_variable_any/wait_until_pred_pass.cpp : condition_variable_any__wait_until_pred_p ] - #[ thread-run2-noit ./sync/conditions/condition_variable_any/lost_notif_pass.cpp : condition_variable_any__lost_notif_p ] + [ thread-run2-noit ./sync/conditions/condition_variable_any/lost_notif_pass.cpp : condition_variable_any__lost_notif_p ] [ thread-run2-noit ./sync/conditions/cv_status/cv_status_pass.cpp : cv_status__cv_status_p ] [ thread-run2-noit ./sync/conditions/notify_all_at_thread_exit_pass.cpp : notify_all_at_thread_exit_p ] ;