From 54646e99595ca5c442f52f58f20dbc4e75804a62 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sun, 16 Feb 2014 23:33:03 +0100 Subject: [PATCH] Added sleep_for/until with no interruption point. --- include/boost/thread/pthread/thread_data.hpp | 31 ++++- include/boost/thread/v2/thread.hpp | 59 ++++++++++ include/boost/thread/win32/thread_data.hpp | 27 +++++ src/pthread/thread.cpp | 113 +++++++++++-------- src/win32/thread.cpp | 84 ++++++++++++++ 5 files changed, 261 insertions(+), 53 deletions(-) diff --git a/include/boost/thread/pthread/thread_data.hpp b/include/boost/thread/pthread/thread_data.hpp index 039f8ee3..cc6460c4 100644 --- a/include/boost/thread/pthread/thread_data.hpp +++ b/include/boost/thread/pthread/thread_data.hpp @@ -220,11 +220,11 @@ namespace boost namespace this_thread { - namespace hiden - { - void BOOST_THREAD_DECL sleep_for(const timespec& ts); - void BOOST_THREAD_DECL sleep_until(const timespec& ts); - } + namespace hiden + { + void BOOST_THREAD_DECL sleep_for(const timespec& ts); + void BOOST_THREAD_DECL sleep_until(const timespec& ts); + } #ifdef BOOST_THREAD_USES_CHRONO #ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY @@ -237,6 +237,27 @@ namespace boost #endif #endif // BOOST_THREAD_USES_CHRONO + namespace no_interruption_point + { + namespace hiden + { + void BOOST_THREAD_DECL sleep_for(const timespec& ts); + void BOOST_THREAD_DECL sleep_until(const timespec& ts); + } + + #ifdef BOOST_THREAD_USES_CHRONO + #ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY + + inline + void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns) + { + return boost::this_thread::hiden::sleep_for(boost::detail::to_timespec(ns)); + } + #endif + #endif // BOOST_THREAD_USES_CHRONO + + } // no_interruption_point + void BOOST_THREAD_DECL yield() BOOST_NOEXCEPT; #if defined BOOST_THREAD_USES_DATETIME diff --git a/include/boost/thread/v2/thread.hpp b/include/boost/thread/v2/thread.hpp index 0d5088fc..181661a3 100644 --- a/include/boost/thread/v2/thread.hpp +++ b/include/boost/thread/v2/thread.hpp @@ -18,7 +18,66 @@ namespace boost { namespace this_thread { + namespace no_interruption_point + { +#ifdef BOOST_THREAD_USES_CHRONO + template + void sleep_until(const chrono::time_point& t) + { + using namespace chrono; + mutex mut; + condition_variable cv; + unique_lock lk(mut); + while (Clock::now() < t) + cv.wait_until(lk, t); + } + +#ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY + + template + void sleep_for(const chrono::duration& d) + { + using namespace chrono; + if (d > duration::zero()) + { + duration Max = nanoseconds::max BOOST_PREVENT_MACRO_SUBSTITUTION (); + nanoseconds ns; + if (d < Max) + { + ns = duration_cast(d); + if (ns < d) + ++ns; + } + else + ns = nanoseconds:: max BOOST_PREVENT_MACRO_SUBSTITUTION (); + sleep_for(ns); + } + } + + template + inline BOOST_SYMBOL_VISIBLE + void sleep_until(const chrono::time_point& t) + { + using namespace chrono; + sleep_for(t - steady_clock::now()); + } +#else + template + void sleep_for(const chrono::duration& d) + { + using namespace chrono; + if (d > duration::zero()) + { + steady_clock::time_point c_timeout = steady_clock::now() + ceil(d); + sleep_until(c_timeout); + } + } + +#endif + +#endif + } #ifdef BOOST_THREAD_USES_CHRONO template diff --git a/include/boost/thread/win32/thread_data.hpp b/include/boost/thread/win32/thread_data.hpp index d2db5154..05d2e34c 100644 --- a/include/boost/thread/win32/thread_data.hpp +++ b/include/boost/thread/win32/thread_data.hpp @@ -273,6 +273,33 @@ namespace boost interruptible_wait(chrono::duration_cast(ns).count()); } #endif + namespace no_interruption_point + { + bool BOOST_THREAD_DECL non_interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time); + inline void non_interruptible_wait(uintmax_t milliseconds) + { + non_interruptible_wait(detail::win32::invalid_handle_value,milliseconds); + } + inline BOOST_SYMBOL_VISIBLE void non_interruptible_wait(system_time const& abs_time) + { + non_interruptible_wait(detail::win32::invalid_handle_value,abs_time); + } + template + inline BOOST_SYMBOL_VISIBLE void sleep(TimeDuration const& rel_time) + { + non_interruptible_wait(detail::pin_to_zero(rel_time.total_milliseconds())); + } + inline BOOST_SYMBOL_VISIBLE void sleep(system_time const& abs_time) + { + non_interruptible_wait(abs_time); + } +#ifdef BOOST_THREAD_USES_CHRONO + inline void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns) + { + non_interruptible_wait(chrono::duration_cast(ns).count()); + } +#endif + } } } diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp index 7c3e04d1..b09f015f 100644 --- a/src/pthread/thread.cpp +++ b/src/pthread/thread.cpp @@ -433,6 +433,68 @@ namespace boost namespace this_thread { + namespace no_interruption_point + { + namespace hiden + { + void BOOST_THREAD_DECL sleep_for(const timespec& ts) + { + + if (boost::detail::timespec_ge(ts, boost::detail::timespec_zero())) + { + + # if defined(BOOST_HAS_PTHREAD_DELAY_NP) + # if defined(__IBMCPP__) + BOOST_VERIFY(!pthread_delay_np(const_cast(&ts))); + # else + BOOST_VERIFY(!pthread_delay_np(&ts)); + # endif + # elif defined(BOOST_HAS_NANOSLEEP) + // nanosleep takes a timespec that is an offset, not + // an absolute time. + nanosleep(&ts, 0); + # else + mutex mx; + unique_lock lock(mx); + condition_variable cond; + cond.do_wait_for(lock, ts); + # endif + } + } + + void BOOST_THREAD_DECL sleep_until(const timespec& ts) + { + timespec now = boost::detail::timespec_now(); + if (boost::detail::timespec_gt(ts, now)) + { + for (int foo=0; foo < 5; ++foo) + { + + # if defined(BOOST_HAS_PTHREAD_DELAY_NP) + timespec d = boost::detail::timespec_minus(ts, now); + BOOST_VERIFY(!pthread_delay_np(&d)); + # elif defined(BOOST_HAS_NANOSLEEP) + // nanosleep takes a timespec that is an offset, not + // an absolute time. + timespec d = boost::detail::timespec_minus(ts, now); + nanosleep(&d, 0); + # else + mutex mx; + unique_lock lock(mx); + condition_variable cond; + cond.do_wait_until(lock, ts); + # endif + timespec now2 = boost::detail::timespec_now(); + if (boost::detail::timespec_ge(now2, ts)) + { + return; + } + } + } + } + + } + } namespace hiden { void BOOST_THREAD_DECL sleep_for(const timespec& ts) @@ -446,27 +508,7 @@ namespace boost } else { - - if (boost::detail::timespec_ge(ts, boost::detail::timespec_zero())) - { - - # if defined(BOOST_HAS_PTHREAD_DELAY_NP) - # if defined(__IBMCPP__) - BOOST_VERIFY(!pthread_delay_np(const_cast(&ts))); - # else - BOOST_VERIFY(!pthread_delay_np(&ts)); - # endif - # elif defined(BOOST_HAS_NANOSLEEP) - // nanosleep takes a timespec that is an offset, not - // an absolute time. - nanosleep(&ts, 0); - # else - mutex mx; - unique_lock lock(mx); - condition_variable cond; - cond.do_wait_for(lock, ts); - # endif - } + boost::this_thread::no_interruption_point::hiden::sleep_for(ts); } } @@ -481,37 +523,12 @@ namespace boost } else { - timespec now = boost::detail::timespec_now(); - if (boost::detail::timespec_gt(ts, now)) - { - for (int foo=0; foo < 5; ++foo) - { - - # if defined(BOOST_HAS_PTHREAD_DELAY_NP) - timespec d = boost::detail::timespec_minus(ts, now); - BOOST_VERIFY(!pthread_delay_np(&d)); - # elif defined(BOOST_HAS_NANOSLEEP) - // nanosleep takes a timespec that is an offset, not - // an absolute time. - timespec d = boost::detail::timespec_minus(ts, now); - nanosleep(&d, 0); - # else - mutex mx; - unique_lock lock(mx); - condition_variable cond; - cond.do_wait_until(lock, ts); - # endif - timespec now2 = boost::detail::timespec_now(); - if (boost::detail::timespec_ge(now2, ts)) - { - return; - } - } - } + boost::this_thread::no_interruption_point::hiden::sleep_until(ts); } } } // hiden } // this_thread + namespace this_thread { void yield() BOOST_NOEXCEPT diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp index 787f1763..8141750b 100644 --- a/src/win32/thread.cpp +++ b/src/win32/thread.cpp @@ -607,6 +607,90 @@ namespace boost return false; } + namespace no_interruption_point + { + bool non_interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time) + { + detail::win32::handle handles[3]={0}; + unsigned handle_count=0; + unsigned wait_handle_index=~0U; + unsigned timeout_index=~0U; + if(handle_to_wait_for!=detail::win32::invalid_handle_value) + { + wait_handle_index=handle_count; + handles[handle_count++]=handle_to_wait_for; + } + detail::win32::handle_manager timer_handle; + +#ifndef UNDER_CE + unsigned const min_timer_wait_period=20; + + if(!target_time.is_sentinel()) + { + detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds(); + if(time_left.milliseconds > min_timer_wait_period) + { + // for a long-enough timeout, use a waitable timer (which tracks clock changes) + timer_handle=CreateWaitableTimer(NULL,false,NULL); + if(timer_handle!=0) + { + LARGE_INTEGER due_time=get_due_time(target_time); + + bool const set_time_succeeded=SetWaitableTimer(timer_handle,&due_time,0,0,0,false)!=0; + if(set_time_succeeded) + { + timeout_index=handle_count; + handles[handle_count++]=timer_handle; + } + } + } + else if(!target_time.relative) + { + // convert short absolute-time timeouts into relative ones, so we don't race against clock changes + target_time=detail::timeout(time_left.milliseconds); + } + } +#endif + + bool const using_timer=timeout_index!=~0u; + detail::timeout::remaining_time time_left(0); + + do + { + if(!using_timer) + { + time_left=target_time.remaining_milliseconds(); + } + + if(handle_count) + { + unsigned long const notified_index=detail::win32::WaitForMultipleObjects(handle_count,handles,false,using_timer?INFINITE:time_left.milliseconds); + if(notified_index