diff options
Diffstat (limited to 'boost/thread/win32/condition_variable.hpp')
-rw-r--r-- | boost/thread/win32/condition_variable.hpp | 341 |
1 files changed, 239 insertions, 102 deletions
diff --git a/boost/thread/win32/condition_variable.hpp b/boost/thread/win32/condition_variable.hpp index 5ff342f1fa..5cf975a534 100644 --- a/boost/thread/win32/condition_variable.hpp +++ b/boost/thread/win32/condition_variable.hpp @@ -18,6 +18,7 @@ #include <boost/thread/thread_time.hpp> #include <boost/thread/lock_guard.hpp> #include <boost/thread/lock_types.hpp> +#include <boost/thread/detail/platform_time.hpp> #include <boost/assert.hpp> #include <boost/intrusive_ptr.hpp> @@ -76,7 +77,7 @@ namespace boost void release(unsigned count_to_release) { notified=true; - detail::winapi::ReleaseSemaphore(semaphore,count_to_release,0); + winapi::ReleaseSemaphore(semaphore,count_to_release,0); } void release_waiters() @@ -89,14 +90,14 @@ namespace boost return notified; } - bool wait(timeout abs_time) + bool interruptible_wait(detail::internal_platform_timepoint const &timeout) { - return this_thread::interruptible_wait(semaphore,abs_time); + return this_thread::interruptible_wait(semaphore, timeout); } bool woken() { - unsigned long const woken_result=detail::winapi::WaitForSingleObjectEx(wake_sem,0,0); + unsigned long const woken_result=winapi::WaitForSingleObjectEx(wake_sem,0,0); BOOST_ASSERT((woken_result==detail::win32::timeout) || (woken_result==0)); return woken_result==0; } @@ -135,7 +136,7 @@ namespace boost void wake_waiters(long count_to_wake) { detail::interlocked_write_release(&total_count,total_count-count_to_wake); - detail::winapi::ReleaseSemaphore(wake_sem,count_to_wake,0); + winapi::ReleaseSemaphore(wake_sem,count_to_wake,0); } template<typename lock_type> @@ -230,10 +231,29 @@ namespace boost } }; - protected: + basic_condition_variable(const basic_condition_variable& other); + basic_condition_variable& operator=(const basic_condition_variable& other); + + public: + basic_condition_variable(): + total_count(0),active_generation_count(0),wake_sem(0) + {} + + ~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<typename lock_type> - bool do_wait(lock_type& lock,timeout abs_time) + bool do_wait_until(lock_type& lock, detail::internal_platform_timepoint const &timeout) { relocker<lock_type> locker(lock); entry_manager entry(get_wait_entry(), internal_mutex); @@ -242,7 +262,7 @@ namespace boost bool woken=false; while(!woken) { - if(!entry->wait(abs_time)) + if(!entry->interruptible_wait(timeout)) { return false; } @@ -252,31 +272,9 @@ namespace boost // do it here to avoid throwing on the destructor entry.remove_waiter_and_reset(); locker.lock(); - return woken; - } - - template<typename lock_type,typename predicate_type> - bool do_wait(lock_type& m,timeout const& abs_time,predicate_type pred) - { - while (!pred()) - { - if(!do_wait(m, abs_time)) - return pred(); - } - return true; + return true; } - basic_condition_variable(const basic_condition_variable& other); - basic_condition_variable& operator=(const basic_condition_variable& other); - - public: - basic_condition_variable(): - total_count(0),active_generation_count(0),wake_sem(0) - {} - - ~basic_condition_variable() - {} - void notify_one() BOOST_NOEXCEPT { if(detail::interlocked_read_acquire(&total_count)) @@ -330,75 +328,115 @@ namespace boost condition_variable() {} + using detail::basic_condition_variable::do_wait_until; using detail::basic_condition_variable::notify_one; using detail::basic_condition_variable::notify_all; void wait(unique_lock<mutex>& m) { - do_wait(m,detail::timeout::sentinel()); + do_wait_until(m, detail::internal_platform_timepoint::getMax()); } template<typename predicate_type> void wait(unique_lock<mutex>& m,predicate_type pred) { - while(!pred()) wait(m); + while (!pred()) + { + wait(m); + } } - #if defined BOOST_THREAD_USES_DATETIME bool timed_wait(unique_lock<mutex>& m,boost::system_time const& abs_time) { - return do_wait(m,abs_time); + // 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); + 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<mutex>& m,boost::xtime const& abs_time) { - return do_wait(m,system_time(abs_time)); + return timed_wait(m, system_time(abs_time)); } template<typename duration_type> bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration) { - if (wait_duration.is_pos_infinity()) - { - wait(m); // or do_wait(m,detail::timeout::sentinel()); - return true; - } - if (wait_duration.is_special()) - { - return true; - } - return do_wait(m,wait_duration.total_milliseconds()); + if (wait_duration.is_pos_infinity()) + { + wait(m); + return true; + } + if (wait_duration.is_special()) + { + return true; + } + const detail::platform_duration d(wait_duration); + return do_wait_until(m, detail::internal_platform_clock::now() + d); } template<typename predicate_type> bool timed_wait(unique_lock<mutex>& m,boost::system_time const& abs_time,predicate_type pred) { - return do_wait(m,abs_time,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()) + { + 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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + do_wait_until(m, detail::internal_platform_clock::now() + d); + } + return pred(); } template<typename predicate_type> bool timed_wait(unique_lock<mutex>& m,boost::xtime const& abs_time,predicate_type pred) { - return do_wait(m,system_time(abs_time),pred); + return timed_wait(m, system_time(abs_time), pred); } template<typename duration_type,typename predicate_type> bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration,predicate_type pred) { if (wait_duration.is_pos_infinity()) { - while (!pred()) - { - wait(m); // or do_wait(m,detail::timeout::sentinel()); - } - return true; + while (!pred()) + { + wait(m); + } + return true; } if (wait_duration.is_special()) { - return pred(); + return pred(); } - return do_wait(m,wait_duration.total_milliseconds(),pred); + 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 + template <class Duration> + cv_status + wait_until( + unique_lock<mutex>& lock, + const chrono::time_point<detail::internal_chrono_clock, Duration>& t) + { + const detail::internal_platform_timepoint ts(t); + if (do_wait_until(lock, ts)) return cv_status::no_timeout; + else return cv_status::timeout; + } template <class Clock, class Duration> cv_status @@ -406,14 +444,18 @@ namespace boost unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& t) { - using namespace chrono; - chrono::time_point<Clock, Duration> now = Clock::now(); - if (t<=now) { - return cv_status::timeout; - } - do_wait(lock, ceil<milliseconds>(t-now).count()); - return Clock::now() < t ? cv_status::no_timeout : - cv_status::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<Duration, typename Clock::duration>::type common_duration; + common_duration 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 <class Rep, class Period> @@ -422,15 +464,22 @@ namespace boost unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& d) { - using namespace chrono; - if (d<=chrono::duration<Rep, Period>::zero()) { - return cv_status::timeout; - } + return wait_until(lock, chrono::steady_clock::now() + d); + } - steady_clock::time_point c_now = steady_clock::now(); - do_wait(lock, ceil<milliseconds>(d).count()); - return steady_clock::now() - c_now < d ? cv_status::no_timeout : - cv_status::timeout; + template <class Duration, class Predicate> + bool + wait_until( + unique_lock<mutex>& lock, + const chrono::time_point<detail::internal_chrono_clock, Duration>& t, + Predicate pred) + { + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } template <class Clock, class Duration, class Predicate> @@ -440,13 +489,20 @@ namespace boost const chrono::time_point<Clock, Duration>& 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<Duration, typename Clock::duration>::type common_duration; while (!pred()) { - if (wait_until(lock, t) == cv_status::timeout) - return pred(); + common_duration d(t - Clock::now()); + if (d <= common_duration::zero()) break; // timeout occurred + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - return true; + return pred(); } + template <class Rep, class Period, class Predicate> bool wait_for( @@ -467,59 +523,122 @@ namespace boost condition_variable_any() {} + using detail::basic_condition_variable::do_wait_until; using detail::basic_condition_variable::notify_one; using detail::basic_condition_variable::notify_all; template<typename lock_type> void wait(lock_type& m) { - do_wait(m,detail::timeout::sentinel()); + do_wait_until(m, detail::internal_platform_timepoint::getMax()); } template<typename lock_type,typename predicate_type> void wait(lock_type& m,predicate_type pred) { - while(!pred()) wait(m); + while (!pred()) + { + wait(m); + } } #if defined BOOST_THREAD_USES_DATETIME template<typename lock_type> bool timed_wait(lock_type& m,boost::system_time const& abs_time) { - return do_wait(m,abs_time); + // 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); + 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<typename lock_type> bool timed_wait(lock_type& m,boost::xtime const& abs_time) { - return do_wait(m,system_time(abs_time)); + return timed_wait(m, system_time(abs_time)); } template<typename lock_type,typename duration_type> bool timed_wait(lock_type& m,duration_type const& wait_duration) { - return do_wait(m,wait_duration.total_milliseconds()); + if (wait_duration.is_pos_infinity()) + { + wait(m); + return true; + } + if (wait_duration.is_special()) + { + return true; + } + const detail::platform_duration d(wait_duration); + return do_wait_until(m, detail::internal_platform_clock::now() + d); } template<typename lock_type,typename predicate_type> bool timed_wait(lock_type& m,boost::system_time const& abs_time,predicate_type pred) { - return do_wait(m,abs_time,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()) + { + 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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + do_wait_until(m, detail::internal_platform_clock::now() + d); + } + return pred(); } template<typename lock_type,typename predicate_type> bool timed_wait(lock_type& m,boost::xtime const& abs_time,predicate_type pred) { - return do_wait(m,system_time(abs_time),pred); + return timed_wait(m, system_time(abs_time), pred); } template<typename lock_type,typename duration_type,typename predicate_type> bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) { - return do_wait(m,wait_duration.total_milliseconds(),pred); + if (wait_duration.is_pos_infinity()) + { + while (!pred()) + { + wait(m); + } + return true; + } + if (wait_duration.is_special()) + { + return pred(); + } + 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 + template <class lock_type,class Duration> + cv_status + wait_until( + lock_type& lock, + const chrono::time_point<detail::internal_chrono_clock, Duration>& t) + { + const detail::internal_platform_timepoint ts(t); + if (do_wait_until(lock, ts)) return cv_status::no_timeout; + else return cv_status::timeout; + } template <class lock_type, class Clock, class Duration> cv_status @@ -527,14 +646,18 @@ namespace boost lock_type& lock, const chrono::time_point<Clock, Duration>& t) { - using namespace chrono; - chrono::time_point<Clock, Duration> now = Clock::now(); - if (t<=now) { - return cv_status::timeout; - } - do_wait(lock, ceil<milliseconds>(t-now).count()); - return Clock::now() < t ? cv_status::no_timeout : - cv_status::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<Duration, typename Clock::duration>::type common_duration; + common_duration 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 <class lock_type, class Rep, class Period> @@ -543,14 +666,22 @@ namespace boost lock_type& lock, const chrono::duration<Rep, Period>& d) { - using namespace chrono; - if (d<=chrono::duration<Rep, Period>::zero()) { - return cv_status::timeout; - } - steady_clock::time_point c_now = steady_clock::now(); - do_wait(lock, ceil<milliseconds>(d).count()); - return steady_clock::now() - c_now < d ? cv_status::no_timeout : - cv_status::timeout; + return wait_until(lock, chrono::steady_clock::now() + d); + } + + template <class lock_type, class Clock, class Duration, class Predicate> + bool + wait_until( + lock_type& lock, + const chrono::time_point<detail::internal_chrono_clock, Duration>& t, + Predicate pred) + { + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } template <class lock_type, class Clock, class Duration, class Predicate> @@ -560,12 +691,18 @@ namespace boost const chrono::time_point<Clock, Duration>& 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<Duration, typename Clock::duration>::type common_duration; while (!pred()) { - if (wait_until(lock, t) == cv_status::timeout) - return pred(); + common_duration d(t - Clock::now()); + if (d <= common_duration::zero()) break; // timeout occurred + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - return true; + return pred(); } template <class lock_type, class Rep, class Period, class Predicate> |