diff options
Diffstat (limited to 'boost/thread')
32 files changed, 2794 insertions, 3218 deletions
diff --git a/boost/thread/concurrent_queues/detail/sync_deque_base.hpp b/boost/thread/concurrent_queues/detail/sync_deque_base.hpp index beffaf73b1..e3ecad15de 100644 --- a/boost/thread/concurrent_queues/detail/sync_deque_base.hpp +++ b/boost/thread/concurrent_queues/detail/sync_deque_base.hpp @@ -11,6 +11,8 @@ // ////////////////////////////////////////////////////////////////////////////// +#include <boost/bind.hpp> + #include <boost/thread/detail/config.hpp> #include <boost/thread/condition_variable.hpp> #include <boost/thread/detail/move.hpp> @@ -84,10 +86,13 @@ namespace detail inline void throw_if_closed(unique_lock<mutex>&); inline void throw_if_closed(lock_guard<mutex>&); - inline void wait_until_not_empty(unique_lock<mutex>& lk); + inline bool not_empty_or_closed(unique_lock<mutex>& ) const; + inline bool wait_until_not_empty_or_closed(unique_lock<mutex>& lk); template <class WClock, class Duration> - queue_op_status wait_until_not_empty_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&); + queue_op_status wait_until_not_empty_or_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp); + template <class WClock, class Duration> + queue_op_status wait_until_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp); inline void notify_not_empty_if_needed(unique_lock<mutex>& ) { @@ -176,39 +181,38 @@ namespace detail } template <class ValueType, class Queue> - void sync_deque_base<ValueType, Queue>::wait_until_not_empty(unique_lock<mutex>& lk) + bool sync_deque_base<ValueType, Queue>::not_empty_or_closed(unique_lock<mutex>& ) const { - for (;;) - { - if (! empty(lk)) break; - throw_if_closed(lk); - not_empty_.wait(lk); - } + return ! data_.empty() || closed_; } + template <class ValueType, class Queue> bool sync_deque_base<ValueType, Queue>::wait_until_not_empty_or_closed(unique_lock<mutex>& lk) { - for (;;) - { - if (! empty(lk)) break; - if (closed(lk)) return true; - not_empty_.wait(lk); - } - return false; + not_empty_.wait(lk, boost::bind(&sync_deque_base<ValueType, Queue>::not_empty_or_closed, boost::ref(*this), boost::ref(lk))); + if (! empty(lk)) return false; // success + return true; // closed } template <class ValueType, class Queue> template <class WClock, class Duration> - queue_op_status sync_deque_base<ValueType, Queue>::wait_until_not_empty_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp) + queue_op_status sync_deque_base<ValueType, Queue>::wait_until_not_empty_or_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp) { - for (;;) - { - if (! empty(lk)) return queue_op_status::success; - throw_if_closed(lk); - if (not_empty_.wait_until(lk, tp) == cv_status::timeout ) return queue_op_status::timeout; - } + if (! not_empty_.wait_until(lk, tp, boost::bind(&sync_deque_base<ValueType, Queue>::not_empty_or_closed, boost::ref(*this), boost::ref(lk)))) + return queue_op_status::timeout; + if (! empty(lk)) return queue_op_status::success; + return queue_op_status::closed; } + template <class ValueType, class Queue> + template <class WClock, class Duration> + queue_op_status sync_deque_base<ValueType, Queue>::wait_until_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp) + { + bool (sync_queue_base<ValueType, Queue>::*closed_function_ptr)(unique_lock<mutex>&) const = &sync_queue_base<ValueType, Queue>::closed; + if (! not_empty_.wait_until(lk, tp, boost::bind(closed_function_ptr, boost::ref(*this), boost::ref(lk)))) + return queue_op_status::timeout; + return queue_op_status::closed; + } } // detail } // concurrent diff --git a/boost/thread/concurrent_queues/detail/sync_queue_base.hpp b/boost/thread/concurrent_queues/detail/sync_queue_base.hpp index 6ecb8211e3..c570da9505 100644 --- a/boost/thread/concurrent_queues/detail/sync_queue_base.hpp +++ b/boost/thread/concurrent_queues/detail/sync_queue_base.hpp @@ -11,6 +11,8 @@ // ////////////////////////////////////////////////////////////////////////////// +#include <boost/bind.hpp> + #include <boost/thread/detail/config.hpp> #include <boost/thread/condition_variable.hpp> #include <boost/thread/detail/move.hpp> @@ -84,10 +86,13 @@ namespace detail inline void throw_if_closed(unique_lock<mutex>&); inline void throw_if_closed(lock_guard<mutex>&); - inline void wait_until_not_empty(unique_lock<mutex>& lk); + inline bool not_empty_or_closed(unique_lock<mutex>& ) const; + inline bool wait_until_not_empty_or_closed(unique_lock<mutex>& lk); template <class WClock, class Duration> - queue_op_status wait_until_not_empty_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&); + queue_op_status wait_until_not_empty_or_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp); + template <class WClock, class Duration> + queue_op_status wait_until_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp); inline void notify_not_empty_if_needed(unique_lock<mutex>& ) { @@ -176,39 +181,38 @@ namespace detail } template <class ValueType, class Queue> - void sync_queue_base<ValueType, Queue>::wait_until_not_empty(unique_lock<mutex>& lk) + bool sync_queue_base<ValueType, Queue>::not_empty_or_closed(unique_lock<mutex>& ) const { - for (;;) - { - if (! empty(lk)) break; - throw_if_closed(lk); - not_empty_.wait(lk); - } + return ! data_.empty() || closed_; } + template <class ValueType, class Queue> bool sync_queue_base<ValueType, Queue>::wait_until_not_empty_or_closed(unique_lock<mutex>& lk) { - for (;;) - { - if (! empty(lk)) break; - if (closed(lk)) return true; - not_empty_.wait(lk); - } - return false; + not_empty_.wait(lk, boost::bind(&sync_queue_base<ValueType, Queue>::not_empty_or_closed, boost::ref(*this), boost::ref(lk))); + if (! empty(lk)) return false; // success + return true; // closed } template <class ValueType, class Queue> template <class WClock, class Duration> - queue_op_status sync_queue_base<ValueType, Queue>::wait_until_not_empty_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp) + queue_op_status sync_queue_base<ValueType, Queue>::wait_until_not_empty_or_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp) { - for (;;) - { - if (! empty(lk)) return queue_op_status::success; - throw_if_closed(lk); - if (not_empty_.wait_until(lk, tp) == cv_status::timeout ) return queue_op_status::timeout; - } + if (! not_empty_.wait_until(lk, tp, boost::bind(&sync_queue_base<ValueType, Queue>::not_empty_or_closed, boost::ref(*this), boost::ref(lk)))) + return queue_op_status::timeout; + if (! empty(lk)) return queue_op_status::success; + return queue_op_status::closed; } + template <class ValueType, class Queue> + template <class WClock, class Duration> + queue_op_status sync_queue_base<ValueType, Queue>::wait_until_closed_until(unique_lock<mutex>& lk, chrono::time_point<WClock,Duration> const&tp) + { + bool (sync_queue_base<ValueType, Queue>::*closed_function_ptr)(unique_lock<mutex>&) const = &sync_queue_base<ValueType, Queue>::closed; + if (! not_empty_.wait_until(lk, tp, boost::bind(closed_function_ptr, boost::ref(*this), boost::ref(lk)))) + return queue_op_status::timeout; + return queue_op_status::closed; + } } // detail } // concurrent diff --git a/boost/thread/concurrent_queues/sync_deque.hpp b/boost/thread/concurrent_queues/sync_deque.hpp index c84dae022a..c6159c4be0 100644 --- a/boost/thread/concurrent_queues/sync_deque.hpp +++ b/boost/thread/concurrent_queues/sync_deque.hpp @@ -149,11 +149,7 @@ namespace concurrent template <class ValueType, class Container> queue_op_status sync_deque<ValueType, Container>::wait_pull_front(ValueType& elem, unique_lock<mutex>& lk) { - if (super::empty(lk)) - { - if (super::closed(lk)) return queue_op_status::closed; - } - bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); if (has_been_closed) return queue_op_status::closed; pull_front(elem, lk); return queue_op_status::success; @@ -188,7 +184,8 @@ namespace concurrent void sync_deque<ValueType, Container>::pull_front(ValueType& elem) { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + if (has_been_closed) super::throw_if_closed(lk); pull_front(elem, lk); } @@ -197,7 +194,8 @@ namespace concurrent ValueType sync_deque<ValueType, Container>::pull_front() { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + if (has_been_closed) super::throw_if_closed(lk); return pull_front(lk); } diff --git a/boost/thread/concurrent_queues/sync_priority_queue.hpp b/boost/thread/concurrent_queues/sync_priority_queue.hpp index a556910cd6..2c6881f90f 100644 --- a/boost/thread/concurrent_queues/sync_priority_queue.hpp +++ b/boost/thread/concurrent_queues/sync_priority_queue.hpp @@ -82,7 +82,7 @@ namespace detail { return boost::move(result); } - Type const& top() + Type const& top() const { return _elements.front(); } @@ -249,7 +249,8 @@ namespace concurrent T sync_priority_queue<T,Container,Cmp>::pull() { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + if (has_been_closed) super::throw_if_closed(lk); return pull(lk); } @@ -269,7 +270,8 @@ namespace concurrent void sync_priority_queue<T,Container,Cmp>::pull(T& elem) { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + if (has_been_closed) super::throw_if_closed(lk); pull(lk, elem); } @@ -280,10 +282,9 @@ namespace concurrent sync_priority_queue<T,Cont,Cmp>::pull_until(const chrono::time_point<WClock,Duration>& tp, T& elem) { unique_lock<mutex> lk(super::mtx_); - if (queue_op_status::timeout == super::wait_until_not_empty_until(lk, tp)) - return queue_op_status::timeout; - pull(lk, elem); - return queue_op_status::success; + const queue_op_status rc = super::wait_until_not_empty_or_closed_until(lk, tp); + if (rc == queue_op_status::success) pull(lk, elem); + return rc; } ////////////////////// @@ -292,7 +293,7 @@ namespace concurrent queue_op_status sync_priority_queue<T,Cont,Cmp>::pull_for(const chrono::duration<Rep,Period>& dura, T& elem) { - return pull_until(clock::now() + dura, elem); + return pull_until(chrono::steady_clock::now() + dura, elem); } ////////////////////// @@ -334,11 +335,7 @@ namespace concurrent template <class T,class Container, class Cmp> queue_op_status sync_priority_queue<T,Container,Cmp>::wait_pull(unique_lock<mutex>& lk, T& elem) { - if (super::empty(lk)) - { - if (super::closed(lk)) return queue_op_status::closed; - } - bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); if (has_been_closed) return queue_op_status::closed; pull(lk, elem); return queue_op_status::success; @@ -352,7 +349,6 @@ namespace concurrent } ////////////////////// - template <class T,class Container, class Cmp> queue_op_status sync_priority_queue<T,Container,Cmp>::nonblocking_pull(T& elem) { diff --git a/boost/thread/concurrent_queues/sync_queue.hpp b/boost/thread/concurrent_queues/sync_queue.hpp index 1dbbef05dd..b36b57e607 100644 --- a/boost/thread/concurrent_queues/sync_queue.hpp +++ b/boost/thread/concurrent_queues/sync_queue.hpp @@ -10,7 +10,6 @@ // See http://www.boost.org/libs/thread for documentation. // ////////////////////////////////////////////////////////////////////////////// -#include <iostream> #include <boost/thread/detail/config.hpp> #include <boost/thread/concurrent_queues/detail/sync_queue_base.hpp> @@ -51,7 +50,6 @@ namespace concurrent inline ~sync_queue(); // Modifiers - inline void push(const value_type& x); inline queue_op_status try_push(const value_type& x); inline queue_op_status nonblocking_push(const value_type& x); @@ -151,19 +149,9 @@ namespace concurrent template <class ValueType, class Container> queue_op_status sync_queue<ValueType, Container>::wait_pull(ValueType& elem, unique_lock<mutex>& lk) { - //std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; - if (super::empty(lk)) - { - //std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; - if (super::closed(lk)) return queue_op_status::closed; - } - //std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; - bool has_been_closed = super::wait_until_not_empty_or_closed(lk); - //std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); if (has_been_closed) return queue_op_status::closed; - //std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; pull(elem, lk); - //std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; return queue_op_status::success; } @@ -196,7 +184,8 @@ namespace concurrent void sync_queue<ValueType, Container>::pull(ValueType& elem) { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + if (has_been_closed) super::throw_if_closed(lk); pull(elem, lk); } @@ -205,7 +194,8 @@ namespace concurrent ValueType sync_queue<ValueType, Container>::pull() { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); + const bool has_been_closed = super::wait_until_not_empty_or_closed(lk); + if (has_been_closed) super::throw_if_closed(lk); return pull(lk); } diff --git a/boost/thread/concurrent_queues/sync_timed_queue.hpp b/boost/thread/concurrent_queues/sync_timed_queue.hpp index 596a625ef5..a4394c0729 100644 --- a/boost/thread/concurrent_queues/sync_timed_queue.hpp +++ b/boost/thread/concurrent_queues/sync_timed_queue.hpp @@ -53,11 +53,6 @@ namespace detail return *this; } - bool time_not_reached() const - { - return time > clock::now(); - } - bool operator <(const scheduled_type & other) const { return this->time > other.time; @@ -123,6 +118,13 @@ namespace detail queue_op_status try_push(BOOST_THREAD_RV_REF(T) elem, chrono::duration<Rep,Period> const& dura); private: + inline bool not_empty_and_time_reached(unique_lock<mutex>& lk) const; + inline bool not_empty_and_time_reached(lock_guard<mutex>& lk) const; + + bool wait_to_pull(unique_lock<mutex>&); + template <class WClock, class Duration> + queue_op_status wait_to_pull_until(unique_lock<mutex>&, chrono::time_point<WClock, Duration> const& tp); + T pull(unique_lock<mutex>&); T pull(lock_guard<mutex>&); @@ -134,15 +136,6 @@ namespace detail queue_op_status wait_pull(unique_lock<mutex>& lk, T& elem); - bool wait_until_not_empty_time_reached_or_closed(unique_lock<mutex>&); - T pull_when_time_reached(unique_lock<mutex>&); - template <class WClock, class Duration> - queue_op_status pull_when_time_reached_until(unique_lock<mutex>&, chrono::time_point<WClock,Duration> const& tp, T& elem); - bool time_not_reached(unique_lock<mutex>&); - bool time_not_reached(lock_guard<mutex>&); - bool empty_or_time_not_reached(unique_lock<mutex>&); - bool empty_or_time_not_reached(lock_guard<mutex>&); - sync_timed_queue(const sync_timed_queue&); sync_timed_queue& operator=(const sync_timed_queue&); sync_timed_queue(BOOST_THREAD_RV_REF(sync_timed_queue)); @@ -210,82 +203,55 @@ namespace detail /////////////////////////// template <class T, class Clock, class TimePoint> - bool sync_timed_queue<T, Clock, TimePoint>::time_not_reached(unique_lock<mutex>&) + bool sync_timed_queue<T, Clock, TimePoint>::not_empty_and_time_reached(unique_lock<mutex>& lk) const { - return super::data_.top().time_not_reached(); + return ! super::empty(lk) && clock::now() >= super::data_.top().time; } template <class T, class Clock, class TimePoint> - bool sync_timed_queue<T, Clock, TimePoint>::time_not_reached(lock_guard<mutex>&) + bool sync_timed_queue<T, Clock, TimePoint>::not_empty_and_time_reached(lock_guard<mutex>& lk) const { - return super::data_.top().time_not_reached(); + return ! super::empty(lk) && clock::now() >= super::data_.top().time; } /////////////////////////// template <class T, class Clock, class TimePoint> - bool sync_timed_queue<T, Clock, TimePoint>::wait_until_not_empty_time_reached_or_closed(unique_lock<mutex>& lk) + bool sync_timed_queue<T, Clock, TimePoint>::wait_to_pull(unique_lock<mutex>& lk) { for (;;) { - if (super::closed(lk)) return true; - while (! super::empty(lk)) { - if (! time_not_reached(lk)) return false; - time_point tp = super::data_.top().time; - super::not_empty_.wait_until(lk, tp); - if (super::closed(lk)) return true; - } - if (super::closed(lk)) return true; - super::not_empty_.wait(lk); - } - //return false; - } + if (not_empty_and_time_reached(lk)) return false; // success + if (super::closed(lk)) return true; // closed - /////////////////////////// - template <class T, class Clock, class TimePoint> - T sync_timed_queue<T, Clock, TimePoint>::pull_when_time_reached(unique_lock<mutex>& lk) - { - while (time_not_reached(lk)) - { - super::throw_if_closed(lk); - time_point tp = super::data_.top().time; - super::not_empty_.wait_until(lk,tp); - super::wait_until_not_empty(lk); + super::wait_until_not_empty_or_closed(lk); + + if (not_empty_and_time_reached(lk)) return false; // success + if (super::closed(lk)) return true; // closed + + const time_point tp(super::data_.top().time); + super::wait_until_closed_until(lk, tp); } - return pull(lk); } template <class T, class Clock, class TimePoint> template <class WClock, class Duration> - queue_op_status - sync_timed_queue<T, Clock, TimePoint>::pull_when_time_reached_until(unique_lock<mutex>& lk, chrono::time_point<WClock, Duration> const& tp, T& elem) + queue_op_status sync_timed_queue<T, Clock, TimePoint>::wait_to_pull_until(unique_lock<mutex>& lk, chrono::time_point<WClock, Duration> const& tp) { - chrono::time_point<WClock, Duration> tpmin = (tp < super::data_.top().time) ? tp : super::data_.top().time; - while (time_not_reached(lk)) + for (;;) { - super::throw_if_closed(lk); - if (cv_status::timeout == super::not_empty_.wait_until(lk, tpmin)) { - if (time_not_reached(lk)) return queue_op_status::not_ready; - return queue_op_status::timeout; - } - } - pull(lk, elem); - return queue_op_status::success; - } + if (not_empty_and_time_reached(lk)) return queue_op_status::success; + if (super::closed(lk)) return queue_op_status::closed; + if (clock::now() >= tp) return super::empty(lk) ? queue_op_status::timeout : queue_op_status::not_ready; - /////////////////////////// - template <class T, class Clock, class TimePoint> - bool sync_timed_queue<T, Clock, TimePoint>::empty_or_time_not_reached(unique_lock<mutex>& lk) - { - if ( super::empty(lk) ) return true; - if ( time_not_reached(lk) ) return true; - return false; - } - template <class T, class Clock, class TimePoint> - bool sync_timed_queue<T, Clock, TimePoint>::empty_or_time_not_reached(lock_guard<mutex>& lk) - { - if ( super::empty(lk) ) return true; - if ( time_not_reached(lk) ) return true; - return false; + super::wait_until_not_empty_or_closed_until(lk, tp); + + if (not_empty_and_time_reached(lk)) return queue_op_status::success; + if (super::closed(lk)) return queue_op_status::closed; + if (clock::now() >= tp) return super::empty(lk) ? queue_op_status::timeout : queue_op_status::not_ready; + + const time_point tpmin(tp < super::data_.top().time ? tp : super::data_.top().time); + super::wait_until_closed_until(lk, tpmin); + } } /////////////////////////// @@ -312,8 +278,9 @@ namespace detail T sync_timed_queue<T, Clock, TimePoint>::pull() { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); - return pull_when_time_reached(lk); + const bool has_been_closed = wait_to_pull(lk); + if (has_been_closed) super::throw_if_closed(lk); + return pull(lk); } /////////////////////////// @@ -341,8 +308,9 @@ namespace detail void sync_timed_queue<T, Clock, TimePoint>::pull(T& elem) { unique_lock<mutex> lk(super::mtx_); - super::wait_until_not_empty(lk); - elem = pull_when_time_reached(lk); + const bool has_been_closed = wait_to_pull(lk); + if (has_been_closed) super::throw_if_closed(lk); + pull(lk, elem); } ////////////////////// @@ -352,10 +320,9 @@ namespace detail sync_timed_queue<T, Clock, TimePoint>::pull_until(chrono::time_point<WClock, Duration> const& tp, T& elem) { unique_lock<mutex> lk(super::mtx_); - - if (queue_op_status::timeout == super::wait_until_not_empty_until(lk, tp)) - return queue_op_status::timeout; - return pull_when_time_reached_until(lk, tp, elem); + const queue_op_status rc = wait_to_pull_until(lk, tp); + if (rc == queue_op_status::success) pull(lk, elem); + return rc; } ////////////////////// @@ -371,35 +338,26 @@ namespace detail template <class T, class Clock, class TimePoint> queue_op_status sync_timed_queue<T, Clock, TimePoint>::try_pull(unique_lock<mutex>& lk, T& elem) { - if ( super::empty(lk) ) + if (not_empty_and_time_reached(lk)) { - if (super::closed(lk)) return queue_op_status::closed; - return queue_op_status::empty; - } - if ( time_not_reached(lk) ) - { - if (super::closed(lk)) return queue_op_status::closed; - return queue_op_status::not_ready; + pull(lk, elem); + return queue_op_status::success; } - - pull(lk, elem); - return queue_op_status::success; + if (super::closed(lk)) return queue_op_status::closed; + if (super::empty(lk)) return queue_op_status::empty; + return queue_op_status::not_ready; } template <class T, class Clock, class TimePoint> queue_op_status sync_timed_queue<T, Clock, TimePoint>::try_pull(lock_guard<mutex>& lk, T& elem) { - if ( super::empty(lk) ) + if (not_empty_and_time_reached(lk)) { - if (super::closed(lk)) return queue_op_status::closed; - return queue_op_status::empty; - } - if ( time_not_reached(lk) ) - { - if (super::closed(lk)) return queue_op_status::closed; - return queue_op_status::not_ready; + pull(lk, elem); + return queue_op_status::success; } - pull(lk, elem); - return queue_op_status::success; + if (super::closed(lk)) return queue_op_status::closed; + if (super::empty(lk)) return queue_op_status::empty; + return queue_op_status::not_ready; } template <class T, class Clock, class TimePoint> @@ -413,11 +371,7 @@ namespace detail template <class T, class Clock, class TimePoint> queue_op_status sync_timed_queue<T, Clock, TimePoint>::wait_pull(unique_lock<mutex>& lk, T& elem) { - if (super::empty(lk)) - { - if (super::closed(lk)) return queue_op_status::closed; - } - bool has_been_closed = wait_until_not_empty_time_reached_or_closed(lk); + const bool has_been_closed = wait_to_pull(lk); if (has_been_closed) return queue_op_status::closed; pull(lk, elem); return queue_op_status::success; @@ -430,26 +384,6 @@ namespace detail return wait_pull(lk, elem); } -// /////////////////////////// -// template <class T, class Clock, class TimePoint> -// queue_op_status sync_timed_queue<T, Clock, TimePoint>::wait_pull(unique_lock<mutex> &lk, T& elem) -// { -// if (super::empty(lk)) -// { -// if (super::closed(lk)) return queue_op_status::closed; -// } -// bool has_been_closed = super::wait_until_not_empty_or_closed(lk); -// if (has_been_closed) return queue_op_status::closed; -// pull(lk, elem); -// return queue_op_status::success; -// } -// template <class T, class Clock, class TimePoint> -// queue_op_status sync_timed_queue<T, Clock, TimePoint>::wait_pull(T& elem) -// { -// unique_lock<mutex> lk(super::mtx_); -// return wait_pull(lk, elem); -// } - /////////////////////////// template <class T, class Clock, class TimePoint> queue_op_status sync_timed_queue<T, Clock, TimePoint>::nonblocking_pull(T& elem) diff --git a/boost/thread/detail/config.hpp b/boost/thread/detail/config.hpp index ebd73a7e92..70c74687f6 100644 --- a/boost/thread/detail/config.hpp +++ b/boost/thread/detail/config.hpp @@ -12,7 +12,6 @@ #include <boost/detail/workaround.hpp> #include <boost/thread/detail/platform.hpp> -//#define BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC //#define BOOST_THREAD_DONT_PROVIDE_INTERRUPTIONS // ATTRIBUTE_MAY_ALIAS @@ -30,6 +29,34 @@ #define BOOST_THREAD_ATTRIBUTE_MAY_ALIAS #endif +#if defined(BOOST_THREAD_CHRONO_WINDOWS_API) +# warning Boost.Thread will use the Windows API for time +#elif defined(BOOST_THREAD_CHRONO_MAC_API) +# warning Boost.Thread will use the Mac API for time +#elif defined(BOOST_THREAD_CHRONO_POSIX_API) +# warning Boost.Thread will use the POSIX API for time +#endif + +# if defined( BOOST_THREAD_CHRONO_WINDOWS_API ) && defined( BOOST_THREAD_CHRONO_POSIX_API ) +# error both BOOST_THREAD_CHRONO_WINDOWS_API and BOOST_THREAD_CHRONO_POSIX_API are defined +# elif defined( BOOST_THREAD_CHRONO_WINDOWS_API ) && defined( BOOST_THREAD_CHRONO_MAC_API ) +# error both BOOST_THREAD_CHRONO_WINDOWS_API and BOOST_THREAD_CHRONO_MAC_API are defined +# elif defined( BOOST_THREAD_CHRONO_MAC_API ) && defined( BOOST_THREAD_CHRONO_POSIX_API ) +# error both BOOST_THREAD_CHRONO_MAC_API and BOOST_THREAD_CHRONO_POSIX_API are defined +# elif !defined( BOOST_THREAD_CHRONO_WINDOWS_API ) && !defined( BOOST_THREAD_CHRONO_MAC_API ) && !defined( BOOST_THREAD_CHRONO_POSIX_API ) +# if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) +# define BOOST_THREAD_CHRONO_WINDOWS_API +# elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +# define BOOST_THREAD_CHRONO_MAC_API +# else +# define BOOST_THREAD_CHRONO_POSIX_API +# endif +# endif + +#if !defined(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS) +#define BOOST_THREAD_POLL_INTERVAL_MILLISECONDS 100 +#endif + #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED #define BOOST_THREAD_ASSERT_PRECONDITION(EXPR, EX) \ if (EXPR) {} else boost::throw_exception(EX) @@ -96,7 +123,7 @@ /// RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR //#if defined BOOST_NO_CXX11_RVALUE_REFERENCES || defined BOOST_MSVC -#define BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#define BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR //#endif // Default version @@ -385,6 +412,29 @@ # endif #endif +#if defined(BOOST_THREAD_CHRONO_WINDOWS_API) + #define BOOST_THREAD_HAS_MONO_CLOCK + #define BOOST_THREAD_INTERNAL_CLOCK_IS_MONO +#elif defined(BOOST_THREAD_CHRONO_MAC_API) + #define BOOST_THREAD_HAS_MONO_CLOCK +#else + #include <time.h> // check for CLOCK_MONOTONIC + #if defined(CLOCK_MONOTONIC) + #define BOOST_THREAD_HAS_MONO_CLOCK + #define BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + #endif +#endif + +#if defined(BOOST_THREAD_PLATFORM_WIN32) +#elif ! defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO +#if defined BOOST_PTHREAD_HAS_TIMEDLOCK +#define BOOST_THREAD_USES_PTHREAD_TIMEDLOCK +#elif (defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS-0)>=200112L) \ + || (defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21) +#define BOOST_THREAD_USES_PTHREAD_TIMEDLOCK +#endif +#endif + // provided for backwards compatibility, since this // macro was used for several releases by mistake. #if defined(BOOST_THREAD_DYN_DLL) && ! defined(BOOST_THREAD_DYN_LINK) diff --git a/boost/thread/detail/platform.hpp b/boost/thread/detail/platform.hpp index 1f33b1a67a..172a601a02 100644 --- a/boost/thread/detail/platform.hpp +++ b/boost/thread/detail/platform.hpp @@ -31,7 +31,9 @@ #elif defined(__CYGWIN__) # define BOOST_THREAD_CYGWIN #elif (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && !defined(BOOST_DISABLE_WIN32) +#if ! defined BOOST_THREAD_WIN32 # define BOOST_THREAD_WIN32 +#endif #elif defined(__BEOS__) # define BOOST_THREAD_BEOS #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) diff --git a/boost/thread/detail/platform_time.hpp b/boost/thread/detail/platform_time.hpp new file mode 100644 index 0000000000..2180f13c05 --- /dev/null +++ b/boost/thread/detail/platform_time.hpp @@ -0,0 +1,478 @@ +#ifndef BOOST_THREAD_DETAIL_PLATFORM_TIME_HPP +#define BOOST_THREAD_DETAIL_PLATFORM_TIME_HPP +// (C) Copyright 2007-8 Anthony Williams +// (C) Copyright 2012 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/thread/detail/config.hpp> +#include <boost/thread/thread_time.hpp> +#if defined BOOST_THREAD_USES_DATETIME +#include <boost/date_time/posix_time/conversion.hpp> +#endif +#ifndef _WIN32 +#include <unistd.h> +#endif +#ifdef BOOST_THREAD_USES_CHRONO +#include <boost/chrono/duration.hpp> +#include <boost/chrono/system_clocks.hpp> +#include <boost/chrono/ceil.hpp> +#endif + +#if defined(BOOST_THREAD_CHRONO_WINDOWS_API) +#include <boost/detail/winapi/time.hpp> +#include <boost/detail/winapi/timers.hpp> +#include <boost/thread/win32/thread_primitives.hpp> +#elif defined(BOOST_THREAD_CHRONO_MAC_API) +#include <sys/time.h> //for gettimeofday and timeval +#include <mach/mach_time.h> // mach_absolute_time, mach_timebase_info_data_t + +#else +#include <time.h> // for clock_gettime +#endif + +#include <limits> + +#include <boost/config/abi_prefix.hpp> + +namespace boost +{ +//typedef boost::int_least64_t time_max_t; +typedef boost::intmax_t time_max_t; + +#if defined BOOST_THREAD_CHRONO_MAC_API +namespace threads +{ + +namespace chrono_details +{ + +// steady_clock + +// Note, in this implementation steady_clock and high_resolution_clock +// are the same clock. They are both based on mach_absolute_time(). +// mach_absolute_time() * MachInfo.numer / MachInfo.denom is the number of +// nanoseconds since the computer booted up. MachInfo.numer and MachInfo.denom +// are run time constants supplied by the OS. This clock has no relationship +// to the Gregorian calendar. It's main use is as a high resolution timer. + +// MachInfo.numer / MachInfo.denom is often 1 on the latest equipment. Specialize +// for that case as an optimization. + +inline time_max_t +steady_simplified() +{ + return mach_absolute_time(); +} + +inline double compute_steady_factor(kern_return_t& err) +{ + mach_timebase_info_data_t MachInfo; + err = mach_timebase_info(&MachInfo); + if ( err != 0 ) { + return 0; + } + return static_cast<double>(MachInfo.numer) / MachInfo.denom; +} + +inline time_max_t steady_full() +{ + kern_return_t err; + const double factor = chrono_details::compute_steady_factor(err); + if (err != 0) + { + BOOST_ASSERT(0 && "Boost::Chrono - Internal Error"); + } + return static_cast<time_max_t>(mach_absolute_time() * factor); +} + + +typedef time_max_t (*FP)(); + +inline FP init_steady_clock(kern_return_t & err) +{ + mach_timebase_info_data_t MachInfo; + err = mach_timebase_info(&MachInfo); + if ( err != 0 ) + { + return 0; + } + + if (MachInfo.numer == MachInfo.denom) + { + return &chrono_details::steady_simplified; + } + return &chrono_details::steady_full; +} + +} +} +#endif + + namespace detail + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + inline timespec ns_to_timespec(boost::time_max_t const& ns) + { + boost::time_max_t s = ns / 1000000000l; + timespec ts; + ts.tv_sec = static_cast<long> (s); + ts.tv_nsec = static_cast<long> (ns - s * 1000000000l); + return ts; + } + inline boost::time_max_t timespec_to_ns(timespec const& ts) + { + return static_cast<boost::time_max_t>(ts.tv_sec) * 1000000000l + ts.tv_nsec; + } +#endif + + struct platform_duration + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + explicit platform_duration(timespec const& v) : ts_val(v) {} + timespec const& getTs() const { return ts_val; } + + explicit platform_duration(boost::time_max_t const& ns = 0) : ts_val(ns_to_timespec(ns)) {} + boost::time_max_t getNs() const { return timespec_to_ns(ts_val); } +#else + explicit platform_duration(boost::time_max_t const& ns = 0) : ns_val(ns) {} + boost::time_max_t getNs() const { return ns_val; } +#endif + +#if defined BOOST_THREAD_USES_DATETIME + platform_duration(boost::posix_time::time_duration const& rel_time) + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + ts_val.tv_sec = rel_time.total_seconds(); + ts_val.tv_nsec = static_cast<long>(rel_time.fractional_seconds() * (1000000000l / rel_time.ticks_per_second())); +#else + ns_val = static_cast<boost::time_max_t>(rel_time.total_seconds()) * 1000000000l; + ns_val += rel_time.fractional_seconds() * (1000000000l / rel_time.ticks_per_second()); +#endif + } +#endif + +#if defined BOOST_THREAD_USES_CHRONO + template <class Rep, class Period> + platform_duration(chrono::duration<Rep, Period> const& d) + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + ts_val = ns_to_timespec(chrono::ceil<chrono::nanoseconds>(d).count()); +#else + ns_val = chrono::ceil<chrono::nanoseconds>(d).count(); +#endif + } +#endif + + boost::time_max_t getMs() const + { + const boost::time_max_t ns = getNs(); + // ceil/floor away from zero + if (ns >= 0) + { + // return ceiling of positive numbers + return (ns + 999999) / 1000000; + } + else + { + // return floor of negative numbers + return (ns - 999999) / 1000000; + } + } + + static platform_duration zero() + { + return platform_duration(0); + } + + private: +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + timespec ts_val; +#else + boost::time_max_t ns_val; +#endif + }; + + inline bool operator==(platform_duration const& lhs, platform_duration const& rhs) + { + return lhs.getNs() == rhs.getNs(); + } + inline bool operator!=(platform_duration const& lhs, platform_duration const& rhs) + { + return lhs.getNs() != rhs.getNs(); + } + inline bool operator<(platform_duration const& lhs, platform_duration const& rhs) + { + return lhs.getNs() < rhs.getNs(); + } + inline bool operator<=(platform_duration const& lhs, platform_duration const& rhs) + { + return lhs.getNs() <= rhs.getNs(); + } + inline bool operator>(platform_duration const& lhs, platform_duration const& rhs) + { + return lhs.getNs() > rhs.getNs(); + } + inline bool operator>=(platform_duration const& lhs, platform_duration const& rhs) + { + return lhs.getNs() >= rhs.getNs(); + } + + static inline platform_duration platform_milliseconds(long const& ms) + { + return platform_duration(ms * 1000000l); + } + + struct real_platform_timepoint + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + explicit real_platform_timepoint(timespec const& v) : dur(v) {} + timespec const& getTs() const { return dur.getTs(); } +#endif + + explicit real_platform_timepoint(boost::time_max_t const& ns) : dur(ns) {} + boost::time_max_t getNs() const { return dur.getNs(); } + +#if defined BOOST_THREAD_USES_DATETIME + real_platform_timepoint(boost::system_time const& abs_time) + : dur(abs_time - boost::posix_time::from_time_t(0)) {} +#endif + +#if defined BOOST_THREAD_USES_CHRONO + template <class Duration> + real_platform_timepoint(chrono::time_point<chrono::system_clock, Duration> const& abs_time) + : dur(abs_time.time_since_epoch()) {} +#endif + + private: + platform_duration dur; + }; + + inline bool operator==(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return lhs.getNs() == rhs.getNs(); + } + inline bool operator!=(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return lhs.getNs() != rhs.getNs(); + } + inline bool operator<(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return lhs.getNs() < rhs.getNs(); + } + inline bool operator<=(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return lhs.getNs() <= rhs.getNs(); + } + inline bool operator>(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return lhs.getNs() > rhs.getNs(); + } + inline bool operator>=(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return lhs.getNs() >= rhs.getNs(); + } + + inline real_platform_timepoint operator+(real_platform_timepoint const& lhs, platform_duration const& rhs) + { + return real_platform_timepoint(lhs.getNs() + rhs.getNs()); + } + inline real_platform_timepoint operator+(platform_duration const& lhs, real_platform_timepoint const& rhs) + { + return real_platform_timepoint(lhs.getNs() + rhs.getNs()); + } + inline platform_duration operator-(real_platform_timepoint const& lhs, real_platform_timepoint const& rhs) + { + return platform_duration(lhs.getNs() - rhs.getNs()); + } + + struct real_platform_clock + { + static real_platform_timepoint now() + { +#if defined(BOOST_THREAD_CHRONO_WINDOWS_API) + boost::detail::winapi::FILETIME_ ft; + boost::detail::winapi::GetSystemTimeAsFileTime(&ft); // never fails + boost::time_max_t ns = ((((static_cast<boost::time_max_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime) - 116444736000000000LL) * 100LL); + return real_platform_timepoint(ns); +#elif defined(BOOST_THREAD_CHRONO_MAC_API) + timeval tv; + ::gettimeofday(&tv, 0); + timespec ts; + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + return real_platform_timepoint(ts); +#else + timespec ts; + if ( ::clock_gettime( CLOCK_REALTIME, &ts ) ) + { + BOOST_ASSERT(0 && "Boost::Thread - clock_gettime(CLOCK_REALTIME) Internal Error"); + return real_platform_timepoint(0); + } + return real_platform_timepoint(ts); +#endif + } + }; + +#if defined(BOOST_THREAD_HAS_MONO_CLOCK) + + struct mono_platform_timepoint + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + + explicit mono_platform_timepoint(timespec const& v) : dur(v) {} + timespec const& getTs() const { return dur.getTs(); } +#endif + + explicit mono_platform_timepoint(boost::time_max_t const& ns) : dur(ns) {} + boost::time_max_t getNs() const { return dur.getNs(); } + +#if defined BOOST_THREAD_USES_CHRONO + // This conversion assumes that chrono::steady_clock::time_point and mono_platform_timepoint share the same epoch. + template <class Duration> + mono_platform_timepoint(chrono::time_point<chrono::steady_clock, Duration> const& abs_time) + : dur(abs_time.time_since_epoch()) {} +#endif + + // can't name this max() since that is a macro on some Windows systems + static mono_platform_timepoint getMax() + { +#if defined BOOST_THREAD_CHRONO_POSIX_API || defined BOOST_THREAD_CHRONO_MAC_API + timespec ts; + ts.tv_sec = (std::numeric_limits<time_t>::max)(); + ts.tv_nsec = 999999999; + return mono_platform_timepoint(ts); +#else + boost::time_max_t ns = (std::numeric_limits<boost::time_max_t>::max)(); + return mono_platform_timepoint(ns); +#endif + } + + private: + platform_duration dur; + }; + + inline bool operator==(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return lhs.getNs() == rhs.getNs(); + } + inline bool operator!=(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return lhs.getNs() != rhs.getNs(); + } + inline bool operator<(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return lhs.getNs() < rhs.getNs(); + } + inline bool operator<=(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return lhs.getNs() <= rhs.getNs(); + } + inline bool operator>(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return lhs.getNs() > rhs.getNs(); + } + inline bool operator>=(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return lhs.getNs() >= rhs.getNs(); + } + + inline mono_platform_timepoint operator+(mono_platform_timepoint const& lhs, platform_duration const& rhs) + { + return mono_platform_timepoint(lhs.getNs() + rhs.getNs()); + } + inline mono_platform_timepoint operator+(platform_duration const& lhs, mono_platform_timepoint const& rhs) + { + return mono_platform_timepoint(lhs.getNs() + rhs.getNs()); + } + inline platform_duration operator-(mono_platform_timepoint const& lhs, mono_platform_timepoint const& rhs) + { + return platform_duration(lhs.getNs() - rhs.getNs()); + } + + struct mono_platform_clock + { + static mono_platform_timepoint now() + { +#if defined(BOOST_THREAD_CHRONO_WINDOWS_API) +#if defined(BOOST_THREAD_USES_CHRONO) + // Use QueryPerformanceCounter() to match the implementation in Boost + // Chrono so that chrono::steady_clock::now() and this function share the + // same epoch and so can be converted between each other. + boost::detail::winapi::LARGE_INTEGER_ freq; + if ( !boost::detail::winapi::QueryPerformanceFrequency( &freq ) ) + { + BOOST_ASSERT(0 && "Boost::Thread - QueryPerformanceFrequency Internal Error"); + return mono_platform_timepoint(0); + } + if ( freq.QuadPart <= 0 ) + { + BOOST_ASSERT(0 && "Boost::Thread - QueryPerformanceFrequency Internal Error"); + return mono_platform_timepoint(0); + } + + boost::detail::winapi::LARGE_INTEGER_ pcount; + unsigned times=0; + while ( ! boost::detail::winapi::QueryPerformanceCounter( &pcount ) ) + { + if ( ++times > 3 ) + { + BOOST_ASSERT(0 && "Boost::Thread - QueryPerformanceCounter Internal Error"); + return mono_platform_timepoint(0); + } + } + + long double ns = 1000000000.0L * pcount.QuadPart / freq.QuadPart; + return mono_platform_timepoint(static_cast<boost::time_max_t>(ns)); +#else + // Use GetTickCount64() because it's more reliable on older + // systems like Windows XP and Windows Server 2003. + win32::ticks_type msec = win32::gettickcount64(); + return mono_platform_timepoint(msec * 1000000); +#endif +#elif defined(BOOST_THREAD_CHRONO_MAC_API) + kern_return_t err; + threads::chrono_details::FP fp = threads::chrono_details::init_steady_clock(err); + if ( err != 0 ) + { + BOOST_ASSERT(0 && "Boost::Chrono - Internal Error"); + } + return mono_platform_timepoint(fp()); +#else + timespec ts; + if ( ::clock_gettime( CLOCK_MONOTONIC, &ts ) ) + { + BOOST_ASSERT(0 && "Boost::Thread - clock_gettime(CLOCK_MONOTONIC) Internal Error"); + return mono_platform_timepoint(0); + } + return mono_platform_timepoint(ts); +#endif + } + }; + +#endif + +#if defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) + typedef mono_platform_clock internal_platform_clock; + typedef mono_platform_timepoint internal_platform_timepoint; +#else + typedef real_platform_clock internal_platform_clock; + typedef real_platform_timepoint internal_platform_timepoint; +#endif + +#ifdef BOOST_THREAD_USES_CHRONO +#ifdef BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + typedef chrono::steady_clock internal_chrono_clock; +#else + typedef chrono::system_clock internal_chrono_clock; +#endif +#endif + + } +} + +#include <boost/config/abi_suffix.hpp> + +#endif diff --git a/boost/thread/detail/thread.hpp b/boost/thread/detail/thread.hpp index b91adee0e9..52af1ba210 100644 --- a/boost/thread/detail/thread.hpp +++ b/boost/thread/detail/thread.hpp @@ -36,6 +36,7 @@ #include <boost/type_traits/is_same.hpp> #include <boost/type_traits/decay.hpp> #include <boost/functional/hash.hpp> +#include <boost/thread/detail/platform_time.hpp> #ifdef BOOST_THREAD_USES_CHRONO #include <boost/chrono/system_clocks.hpp> #include <boost/chrono/ceil.hpp> @@ -155,15 +156,7 @@ namespace boost }; #endif } -namespace thread_detail { -#ifdef BOOST_THREAD_USES_CHRONO -#if defined(BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC) - typedef chrono::steady_clock internal_clock_t; -#else - typedef chrono::system_clock internal_clock_t; -#endif -#endif -} + class BOOST_THREAD_DECL thread { public: @@ -462,105 +455,80 @@ namespace thread_detail { } class id; -#ifdef BOOST_THREAD_PLATFORM_PTHREAD - inline id get_id() const BOOST_NOEXCEPT; -#else id get_id() const BOOST_NOEXCEPT; -#endif - bool joinable() const BOOST_NOEXCEPT; private: bool join_noexcept(); + bool do_try_join_until_noexcept(detail::internal_platform_timepoint const &timeout, bool& res); + bool do_try_join_until(detail::internal_platform_timepoint const &timeout); public: - inline void join(); + void join(); #ifdef BOOST_THREAD_USES_CHRONO -#if defined(BOOST_THREAD_PLATFORM_WIN32) - template <class Rep, class Period> - bool try_join_for(const chrono::duration<Rep, Period>& rel_time) - { - chrono::milliseconds rel_time2= chrono::ceil<chrono::milliseconds>(rel_time); - return do_try_join_until(rel_time2.count()); - } -#else - template <class Rep, class Period> - bool try_join_for(const chrono::duration<Rep, Period>& rel_time) + template <class Duration> + bool try_join_until(const chrono::time_point<detail::internal_chrono_clock, Duration>& t) { - return try_join_until(chrono::steady_clock::now() + rel_time); + return do_try_join_until(boost::detail::internal_platform_timepoint(t)); } -#endif template <class Clock, class Duration> bool try_join_until(const chrono::time_point<Clock, Duration>& t) { - using namespace chrono; - bool joined= false; - do { - thread_detail::internal_clock_t::time_point s_now = thread_detail::internal_clock_t::now(); - typename Clock::duration d = ceil<nanoseconds>(t-Clock::now()); - if (d <= Clock::duration::zero()) return false; // in case the Clock::time_point t is already reached - joined = try_join_until(s_now + d); - } while (! joined); + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + common_duration d(t - Clock::now()); + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + while ( ! try_join_until(detail::internal_chrono_clock::now() + d) ) + { + d = t - Clock::now(); + if ( d <= common_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + } return true; } - template <class Duration> - bool try_join_until(const chrono::time_point<thread_detail::internal_clock_t, Duration>& t) - { - using namespace chrono; - typedef time_point<thread_detail::internal_clock_t, nanoseconds> nano_sys_tmpt; - return try_join_until(nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - } -#endif -#if defined(BOOST_THREAD_PLATFORM_WIN32) - private: - bool do_try_join_until_noexcept(uintmax_t milli, bool& res); - inline bool do_try_join_until(uintmax_t milli); - public: - bool timed_join(const system_time& abs_time); - //{ - // return do_try_join_until(get_milliseconds_until(wait_until)); - //} -#ifdef BOOST_THREAD_USES_CHRONO - bool try_join_until(const chrono::time_point<thread_detail::internal_clock_t, chrono::nanoseconds>& tp) + template <class Rep, class Period> + bool try_join_for(const chrono::duration<Rep, Period>& rel_time) { - chrono::milliseconds rel_time= chrono::ceil<chrono::milliseconds>(tp-chrono::system_clock::now()); - return do_try_join_until(rel_time.count()); + return try_join_until(chrono::steady_clock::now() + rel_time); } #endif - - -#else - private: - bool do_try_join_until_noexcept(struct timespec const &timeout, bool& res); - inline bool do_try_join_until(struct timespec const &timeout); - public: #if defined BOOST_THREAD_USES_DATETIME bool timed_join(const system_time& abs_time) { - struct timespec const ts=detail::to_timespec(abs_time); + const detail::real_platform_timepoint ts(abs_time); +#if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + detail::platform_duration d(ts - detail::real_platform_clock::now()); + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + while ( ! do_try_join_until(detail::internal_platform_clock::now() + d) ) + { + d = ts - detail::real_platform_clock::now(); + if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + return true; +#else return do_try_join_until(ts); - } #endif -#ifdef BOOST_THREAD_USES_CHRONO - bool try_join_until(const chrono::time_point<thread_detail::internal_clock_t, chrono::nanoseconds>& tp) - { - using namespace chrono; - nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); - return do_try_join_until(ts); } -#endif - -#endif - public: -#if defined BOOST_THREAD_USES_DATETIME template<typename TimeDuration> - inline bool timed_join(TimeDuration const& rel_time) + bool timed_join(TimeDuration const& rel_time) { - return timed_join(get_system_time()+rel_time); + detail::platform_duration d(rel_time); +#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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + while ( ! do_try_join_until(detail::internal_platform_clock::now() + d) ) + { + d = ts - detail::mono_platform_clock::now(); + if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + return true; +#else + return do_try_join_until(detail::internal_platform_clock::now() + d); +#endif } #endif void detach(); @@ -614,7 +582,7 @@ namespace thread_detail { namespace this_thread { #ifdef BOOST_THREAD_PLATFORM_PTHREAD - inline thread::id get_id() BOOST_NOEXCEPT; + thread::id get_id() BOOST_NOEXCEPT; #else thread::id BOOST_THREAD_DECL get_id() BOOST_NOEXCEPT; #endif @@ -745,7 +713,7 @@ namespace thread_detail { }; #ifdef BOOST_THREAD_PLATFORM_PTHREAD - thread::id thread::get_id() const BOOST_NOEXCEPT + inline thread::id thread::get_id() const BOOST_NOEXCEPT { #if defined BOOST_THREAD_PROVIDES_BASIC_THREAD_ID return const_cast<thread*>(this)->native_handle(); @@ -768,7 +736,7 @@ namespace thread_detail { } } #endif - void thread::join() { + inline void thread::join() { if (this_thread::get_id() == get_id()) boost::throw_exception(thread_resource_error(static_cast<int>(system::errc::resource_deadlock_would_occur), "boost thread: trying joining itself")); @@ -777,11 +745,7 @@ namespace thread_detail { ); } -#ifdef BOOST_THREAD_PLATFORM_PTHREAD - bool thread::do_try_join_until(struct timespec const &timeout) -#else - bool thread::do_try_join_until(uintmax_t timeout) -#endif + inline bool thread::do_try_join_until(detail::internal_platform_timepoint const &timeout) { if (this_thread::get_id() == get_id()) boost::throw_exception(thread_resource_error(static_cast<int>(system::errc::resource_deadlock_would_occur), "boost thread: trying joining itself")); diff --git a/boost/thread/future.hpp b/boost/thread/future.hpp index 1234cb85a7..e5fd1921b0 100644 --- a/boost/thread/future.hpp +++ b/boost/thread/future.hpp @@ -159,7 +159,7 @@ namespace boost boost::function<void()> callback; // This declaration should be only included conditionally, but is included to maintain the same layout. continuations_type continuations; - executor_ptr_type ex; + executor_ptr_type ex_; // This declaration should be only included conditionally, but is included to maintain the same layout. virtual void launch_continuation() @@ -173,43 +173,49 @@ namespace boost is_constructed(false), policy_(launch::none), continuations(), - ex() + ex_() {} - shared_state_base(exceptional_ptr const& ex_): - exception(ex_.ptr_), + shared_state_base(exceptional_ptr const& ex): + exception(ex.ptr_), done(true), is_valid_(true), is_deferred_(false), is_constructed(false), policy_(launch::none), continuations(), - ex() + ex_() {} virtual ~shared_state_base() { } + + bool is_done() + { + return done; + } + executor_ptr_type get_executor() { - return ex; + return ex_; } void set_executor_policy(executor_ptr_type aex) { set_executor(); - ex = aex; + ex_ = aex; } void set_executor_policy(executor_ptr_type aex, boost::lock_guard<boost::mutex>&) { set_executor(); - ex = aex; + ex_ = aex; } void set_executor_policy(executor_ptr_type aex, boost::unique_lock<boost::mutex>&) { set_executor(); - ex = aex; + ex_ = aex; } bool valid(boost::unique_lock<boost::mutex>&) { return is_valid_; } @@ -262,6 +268,10 @@ namespace boost external_waiters.erase(it); } +#if 0 + // this inline definition results in ODR. See https://github.com/boostorg/thread/issues/193 + // to avoid it, we define the function on the derived templates using the macro BOOST_THREAD_DO_CONTINUATION +#define BOOST_THREAD_DO_CONTINUATION #if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION void do_continuation(boost::unique_lock<boost::mutex>& lock) { @@ -279,6 +289,31 @@ namespace boost { } #endif + +#else +#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION +#define BOOST_THREAD_DO_CONTINUATION \ + void do_continuation(boost::unique_lock<boost::mutex>& lock) \ + { \ + if (! this->continuations.empty()) { \ + continuations_type the_continuations = this->continuations; \ + this->continuations.clear(); \ + relocker rlk(lock); \ + for (continuations_type::iterator it = the_continuations.begin(); it != the_continuations.end(); ++it) { \ + (*it)->launch_continuation(); \ + } \ + } \ + } +#else +#define BOOST_THREAD_DO_CONTINUATION \ + void do_continuation(boost::unique_lock<boost::mutex>&) \ + { \ + } +#endif + + virtual void do_continuation(boost::unique_lock<boost::mutex>&) = 0; +#endif + #if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION virtual void set_continuation_ptr(continuation_ptr_type continuation, boost::unique_lock<boost::mutex>& lock) { @@ -348,10 +383,7 @@ namespace boost is_deferred_=false; execute(lk); } - while(!done) - { - waiters.wait(lk); - } + waiters.wait(lk, boost::bind(&shared_state_base::is_done, boost::ref(*this))); if(rethrow && exception) { boost::rethrow_exception(exception); @@ -370,6 +402,17 @@ namespace boost } #if defined BOOST_THREAD_USES_DATETIME + template<typename Duration> + bool timed_wait(Duration const& rel_time) + { + boost::unique_lock<boost::mutex> lock(this->mutex); + if (is_deferred_) + return false; + + do_callback(lock); + return waiters.timed_wait(lock, rel_time, boost::bind(&shared_state_base::is_done, boost::ref(*this))); + } + bool timed_wait_until(boost::system_time const& target_time) { boost::unique_lock<boost::mutex> lock(this->mutex); @@ -377,15 +420,7 @@ namespace boost return false; do_callback(lock); - while(!done) - { - bool const success=waiters.timed_wait(lock,target_time); - if(!success && !done) - { - return false; - } - } - return true; + return waiters.timed_wait(lock, target_time, boost::bind(&shared_state_base::is_done, boost::ref(*this))); } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -398,13 +433,9 @@ namespace boost if (is_deferred_) return future_status::deferred; do_callback(lock); - while(!done) + if(!waiters.wait_until(lock, abs_time, boost::bind(&shared_state_base::is_done, boost::ref(*this)))) { - cv_status const st=waiters.wait_until(lock,abs_time); - if(st==cv_status::timeout && !done) - { - return future_status::timeout; - } + return future_status::timeout; } return future_status::ready; } @@ -546,10 +577,8 @@ namespace boost detail::shared_state_base(ex), result() {} - - ~shared_state() - { - } + // locating this definition on the template avoid the ODR issue. See https://github.com/boostorg/thread/issues/193 + BOOST_THREAD_DO_CONTINUATION void mark_finished_with_result_internal(source_reference_type result_, boost::unique_lock<boost::mutex>& lock) { @@ -732,9 +761,8 @@ namespace boost detail::shared_state_base(ex), result(0) {} - ~shared_state() - { - } + // locating this definition on the template avoid the ODR issue. See https://github.com/boostorg/thread/issues/193 + BOOST_THREAD_DO_CONTINUATION void mark_finished_with_result_internal(source_reference_type result_, boost::unique_lock<boost::mutex>& lock) { @@ -811,6 +839,9 @@ namespace boost detail::shared_state_base(ex) {} + // locating this definition on the template avoid the ODR issue. See https://github.com/boostorg/thread/issues/193 + BOOST_THREAD_DO_CONTINUATION + void mark_finished_with_result_internal(boost::unique_lock<boost::mutex>& lock) { mark_finished_internal(lock); @@ -899,10 +930,7 @@ namespace boost join(); #elif defined BOOST_THREAD_ASYNC_FUTURE_WAITS unique_lock<boost::mutex> lk(this->mutex); - while(!this->done) - { - this->waiters.wait(lk); - } + this->waiters.wait(lk, boost::bind(&shared_state_base::is_done, boost::ref(*this))); #endif } @@ -1007,10 +1035,8 @@ namespace boost template<typename Rp, typename Fp> struct future_deferred_shared_state: shared_state<Rp> { - typedef shared_state<Rp> base_type; Fp func_; - public: explicit future_deferred_shared_state(BOOST_THREAD_FWD_REF(Fp) f) : func_(boost::move(f)) { @@ -1035,10 +1061,8 @@ namespace boost template<typename Rp, typename Fp> struct future_deferred_shared_state<Rp&,Fp>: shared_state<Rp&> { - typedef shared_state<Rp&> base_type; Fp func_; - public: explicit future_deferred_shared_state(BOOST_THREAD_FWD_REF(Fp) f) : func_(boost::move(f)) { @@ -1060,10 +1084,8 @@ namespace boost template<typename Fp> struct future_deferred_shared_state<void,Fp>: shared_state<void> { - typedef shared_state<void> base_type; Fp func_; - public: explicit future_deferred_shared_state(BOOST_THREAD_FWD_REF(Fp) f) : func_(boost::move(f)) { @@ -1091,7 +1113,6 @@ namespace boost public: typedef std::vector<int>::size_type count_type; private: - struct registered_waiter; struct registered_waiter { boost::shared_ptr<detail::shared_state_base> future_; @@ -1452,7 +1473,11 @@ namespace boost template<typename Duration> bool timed_wait(Duration const& rel_time) const { - return timed_wait_until(boost::get_system_time()+rel_time); + if(!future_) + { + boost::throw_exception(future_uninitialized()); + } + return future_->timed_wait(rel_time); } bool timed_wait_until(boost::system_time const& abs_time) const @@ -3155,7 +3180,7 @@ namespace boost } }; -#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR) +#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR) #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK #if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) @@ -3497,7 +3522,7 @@ namespace boost {} // construction and destruction -#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR) +#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR) #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK #if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) @@ -3587,7 +3612,7 @@ namespace boost #endif #if defined BOOST_THREAD_PROVIDES_FUTURE_CTOR_ALLOCATORS -#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR) +#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR) template <class Allocator> packaged_task(boost::allocator_arg_t, Allocator a, R(*f)()) { @@ -3608,7 +3633,7 @@ namespace boost task = task_ptr(::new(a2.allocate(1)) task_shared_state_type(f), D(a2, 1) ); future_obtained = false; } -#endif // BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#endif // BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR #if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES template <class F, class Allocator> @@ -3825,7 +3850,7 @@ namespace detail // future<R> async(launch policy, F&&, ArgTypes&&...); //////////////////////////////// -#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR #if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) template <class R, class... ArgTypes> @@ -3884,7 +3909,7 @@ namespace detail } } #endif -#endif // defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR) +#endif // defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR) #if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) @@ -4113,7 +4138,7 @@ namespace detail { //#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) #if defined(BOOST_THREAD_PROVIDES_INVOKE) && ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && ! defined(BOOST_NO_CXX11_HDR_TUPLE) -#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR template <class Executor, class R, class... ArgTypes> BOOST_THREAD_FUTURE<R> @@ -4129,7 +4154,7 @@ namespace detail { ) )); } -#endif // defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#endif // defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR template <class Executor, class F, class ...ArgTypes> BOOST_THREAD_FUTURE<typename boost::result_of<typename decay<F>::type( @@ -4148,7 +4173,7 @@ namespace detail { } #else // ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR template <class Executor, class R> BOOST_THREAD_FUTURE<R> @@ -4178,7 +4203,7 @@ namespace detail { ) )); } -#endif // defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#endif // defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR template <class Executor, class F> BOOST_THREAD_FUTURE<typename boost::result_of<typename decay<F>::type()>::type> @@ -4234,7 +4259,7 @@ namespace detail { // future<R> async(F&&, ArgTypes&&...); //////////////////////////////// -#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR +#if defined BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR #if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) template <class R, class... ArgTypes> BOOST_THREAD_FUTURE<R> @@ -4956,6 +4981,10 @@ namespace detail { return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_deferred_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type>( lock, boost::move(*this), boost::forward<F>(func) ))); + } else if (underlying_cast<int>(policy) & int(launch::sync)) { + return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_sync_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type>( + lock, boost::move(*this), boost::forward<F>(func) + ))); #ifdef BOOST_THREAD_PROVIDES_EXECUTORS } else if (underlying_cast<int>(policy) & int(launch::executor)) { assert(this->future_->get_executor()); @@ -4976,6 +5005,10 @@ namespace detail { return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_deferred_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type>( lock, boost::move(*this), boost::forward<F>(func) ))); + } else if (underlying_cast<int>(policy_) & int(launch::sync)) { + return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_sync_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type>( + lock, boost::move(*this), boost::forward<F>(func) + ))); #ifdef BOOST_THREAD_PROVIDES_EXECUTORS } else if (underlying_cast<int>(policy_) & int(launch::executor)) { assert(this->future_->get_executor()); diff --git a/boost/thread/pthread/condition_variable.hpp b/boost/thread/pthread/condition_variable.hpp index b66b710a24..285785ff59 100644 --- a/boost/thread/pthread/condition_variable.hpp +++ b/boost/thread/pthread/condition_variable.hpp @@ -6,8 +6,10 @@ // (C) Copyright 2007-10 Anthony Williams // (C) Copyright 2011-2012 Vicente J. Botet Escriba -#include <boost/thread/pthread/timespec.hpp> +#include <boost/thread/detail/platform_time.hpp> #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp> +#include <boost/thread/pthread/pthread_helpers.hpp> + #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS #include <boost/thread/pthread/thread_data.hpp> #endif @@ -18,6 +20,8 @@ #endif #include <boost/thread/detail/delete.hpp> +#include <algorithm> + #include <boost/config/abi_prefix.hpp> namespace boost @@ -95,9 +99,18 @@ 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<mutex>& m, - struct timespec const &timeout) + detail::internal_platform_timepoint const &timeout) { #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED if (!m.owns_lock()) @@ -112,12 +125,12 @@ namespace boost detail::interruption_checker check_for_interruption(&internal_mutex,&cond); pthread_mutex_t* the_mutex = &internal_mutex; guard.activate(m); - cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout); + cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout.getTs()); check_for_interruption.unlock_if_locked(); guard.deactivate(); #else pthread_mutex_t* the_mutex = m.mutex()->native_handle(); - cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout); + cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout.getTs()); #endif } #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS @@ -164,11 +177,11 @@ namespace boost { boost::throw_exception(thread_resource_error(res, "boost::condition_variable_any::condition_variable_any() failed in pthread_mutex_init")); } - int const res2 = detail::monotonic_pthread_cond_init(cond); + int const res2 = pthread::cond_init(cond); if(res2) { BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); - boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_any::condition_variable_any() failed in detail::monotonic_pthread_cond_init")); + boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_any::condition_variable_any() failed in pthread::cond_init")); } } ~condition_variable_any() @@ -205,15 +218,35 @@ namespace boost 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) { - struct timespec const timeout=detail::to_timespec(abs_time); - return do_wait_until(m, timeout); +#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 +#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 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, ts); +#endif } template<typename lock_type> bool timed_wait(lock_type& m,xtime const& abs_time) @@ -224,18 +257,55 @@ namespace boost template<typename lock_type,typename duration_type> bool timed_wait(lock_type& m,duration_type const& wait_duration) { - return timed_wait(m,get_system_time()+wait_duration); + if (wait_duration.is_pos_infinity()) + { + wait(m); + return true; + } + if (wait_duration.is_special()) + { + return true; + } + detail::platform_duration d(wait_duration); +#if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !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 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 } template<typename lock_type,typename predicate_type> 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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + 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<typename lock_type,typename predicate_type> @@ -247,24 +317,52 @@ namespace boost 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 timed_wait(m,get_system_time()+wait_duration,pred); + if (wait_duration.is_pos_infinity()) + { + while (!pred()) + { + wait(m); + } + return true; + } + if (wait_duration.is_special()) + { + return pred(); + } + detail::platform_duration d(wait_duration); +#if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !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. + const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); + while (!pred()) + { + 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); + d = ts - detail::mono_platform_clock::now(); + } +#else + 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 -#ifndef BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC #ifdef BOOST_THREAD_USES_CHRONO template <class lock_type,class Duration> cv_status wait_until( lock_type& lock, - const chrono::time_point<chrono::system_clock, Duration>& t) + const chrono::time_point<detail::internal_chrono_clock, Duration>& t) { - using namespace chrono; - typedef time_point<system_clock, nanoseconds> nano_sys_tmpt; - wait_until(lock, - nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - return system_clock::now() < t ? cv_status::no_timeout : - 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 <class lock_type, class Clock, class Duration> @@ -273,11 +371,18 @@ namespace boost lock_type& lock, 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(); - wait_until(lock, s_now + ceil<nanoseconds>(t - c_now)); - 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> @@ -286,86 +391,24 @@ namespace boost lock_type& lock, const chrono::duration<Rep, Period>& d) { - using namespace chrono; - system_clock::time_point s_now = system_clock::now(); - steady_clock::time_point c_now = steady_clock::now(); - wait_until(lock, s_now + ceil<nanoseconds>(d)); - return steady_clock::now() - c_now < d ? cv_status::no_timeout : - cv_status::timeout; - - } - - template <class lock_type> - cv_status wait_until( - lock_type& lk, - chrono::time_point<chrono::system_clock, chrono::nanoseconds> tp) - { - using namespace chrono; - nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); - if (do_wait_until(lk, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + return wait_until(lock, chrono::steady_clock::now() + d); } -#endif -#else // defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC -#ifdef BOOST_THREAD_USES_CHRONO - template <class lock_type, class Duration> - cv_status - wait_until( - lock_type& lock, - const chrono::time_point<chrono::steady_clock, Duration>& t) - { - using namespace chrono; - typedef time_point<steady_clock, nanoseconds> nano_sys_tmpt; - wait_until(lock, - nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - return steady_clock::now() < t ? cv_status::no_timeout : - cv_status::timeout; - } - - template <class lock_type, class Clock, class Duration> - cv_status + template <class lock_type, class Duration, class Predicate> + bool wait_until( - lock_type& lock, - const chrono::time_point<Clock, Duration>& t) - { - using namespace chrono; - steady_clock::time_point s_now = steady_clock::now(); - typename Clock::time_point c_now = Clock::now(); - wait_until(lock, s_now + ceil<nanoseconds>(t - c_now)); - return Clock::now() < t ? cv_status::no_timeout : cv_status::timeout; - } - - template <class lock_type, class Rep, class Period> - cv_status - wait_for( - lock_type& lock, - const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - steady_clock::time_point c_now = steady_clock::now(); - wait_until(lock, c_now + ceil<nanoseconds>(d)); - return steady_clock::now() - c_now < d ? cv_status::no_timeout : - cv_status::timeout; - } - - template <class lock_type> - inline cv_status wait_until( - lock_type& lock, - chrono::time_point<chrono::steady_clock, chrono::nanoseconds> tp) + lock_type& lock, + const chrono::time_point<detail::internal_chrono_clock, Duration>& t, + Predicate pred) { - using namespace chrono; - nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); - if (do_wait_until(lock, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } -#endif -#endif // defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC - -#ifdef BOOST_THREAD_USES_CHRONO template <class lock_type, class Clock, class Duration, class Predicate> bool wait_until( @@ -373,12 +416,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> @@ -388,7 +437,7 @@ namespace boost const chrono::duration<Rep, Period>& 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 @@ -403,12 +452,21 @@ namespace boost boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); BOOST_VERIFY(!pthread_cond_broadcast(&cond)); } - private: // used by boost::thread::try_join_until - + 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 <class lock_type> bool do_wait_until( lock_type& m, - struct timespec const &timeout) + detail::internal_platform_timepoint const &timeout) { int res=0; { @@ -419,7 +477,7 @@ namespace boost boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex); #endif guard.activate(m); - res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout); + res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout.getTs()); check_for_interruption.unlock_if_locked(); guard.deactivate(); } @@ -437,7 +495,6 @@ namespace boost return true; } }; - } #include <boost/config/abi_suffix.hpp> diff --git a/boost/thread/pthread/condition_variable_fwd.hpp b/boost/thread/pthread/condition_variable_fwd.hpp index 0ea34e238d..dc471d56a0 100644 --- a/boost/thread/pthread/condition_variable_fwd.hpp +++ b/boost/thread/pthread/condition_variable_fwd.hpp @@ -13,7 +13,9 @@ #include <boost/thread/mutex.hpp> #include <boost/thread/lock_types.hpp> #include <boost/thread/thread_time.hpp> -#include <boost/thread/pthread/timespec.hpp> +#include <boost/thread/detail/platform_time.hpp> +#include <boost/thread/pthread/pthread_helpers.hpp> + #if defined BOOST_THREAD_USES_DATETIME #include <boost/thread/xtime.hpp> #endif @@ -25,31 +27,12 @@ #include <boost/thread/detail/delete.hpp> #include <boost/date_time/posix_time/posix_time_duration.hpp> +#include <algorithm> + #include <boost/config/abi_prefix.hpp> namespace boost { - namespace detail { - inline int monotonic_pthread_cond_init(pthread_cond_t& cond) { - -#ifdef BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC - pthread_condattr_t attr; - int res = pthread_condattr_init(&attr); - if (res) - { - return res; - } - pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); - res=pthread_cond_init(&cond,&attr); - pthread_condattr_destroy(&attr); - return res; -#else - return pthread_cond_init(&cond,NULL); -#endif - - } - } - class condition_variable { private: @@ -61,21 +44,9 @@ namespace boost public: //private: // used by boost::thread::try_join_until - inline bool do_wait_until( + bool do_wait_until( unique_lock<mutex>& lock, - struct timespec const &timeout); - - bool do_wait_for( - unique_lock<mutex>& lock, - struct timespec const &timeout) - { -#if defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC - return do_wait_until(lock, boost::detail::timespec_plus(timeout, boost::detail::timespec_now_monotonic())); -#else - // old behavior was fine for monotonic - return do_wait_until(lock, boost::detail::timespec_plus(timeout, boost::detail::timespec_now_realtime())); -#endif - } + detail::internal_platform_timepoint const &timeout); public: BOOST_THREAD_NO_COPYABLE(condition_variable) @@ -93,14 +64,14 @@ namespace boost boost::throw_exception(thread_resource_error(res, "boost::condition_variable::condition_variable() constructor failed in pthread_mutex_init")); } //#endif - res = detail::monotonic_pthread_cond_init(cond); + res = pthread::cond_init(cond); if (res) { //#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS // ditto BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); //#endif - boost::throw_exception(thread_resource_error(res, "boost::condition_variable::condition_variable() constructor failed in detail::monotonic_pthread_cond_init")); + boost::throw_exception(thread_resource_error(res, "boost::condition_variable::condition_variable() constructor failed in pthread::cond_init")); } } ~condition_variable() @@ -124,20 +95,35 @@ namespace boost 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 - inline bool timed_wait( + bool timed_wait( unique_lock<mutex>& m, boost::system_time const& abs_time) { #if defined BOOST_THREAD_WAIT_BUG - struct timespec const timeout=detail::to_timespec(abs_time + BOOST_THREAD_WAIT_BUG); - return do_wait_until(m, timeout); + const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); +#else + const detail::real_platform_timepoint ts(abs_time); +#endif +#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 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 - struct timespec const timeout=detail::to_timespec(abs_time); - return do_wait_until(m, timeout); + return do_wait_until(m, ts); #endif } bool timed_wait( @@ -154,14 +140,28 @@ namespace boost { if (wait_duration.is_pos_infinity()) { - wait(m); // or do_wait(m,detail::timeout::sentinel()); + wait(m); return true; } if (wait_duration.is_special()) { return true; } - return timed_wait(m,get_system_time()+wait_duration); + detail::platform_duration d(wait_duration); +#if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !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 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 } template<typename predicate_type> @@ -169,12 +169,26 @@ namespace boost unique_lock<mutex>& 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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + 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<typename predicate_type> @@ -194,7 +208,7 @@ namespace boost { while (!pred()) { - wait(m); // or do_wait(m,detail::timeout::sentinel()); + wait(m); } return true; } @@ -202,26 +216,41 @@ namespace boost { return pred(); } - return timed_wait(m,get_system_time()+wait_duration,pred); + detail::platform_duration d(wait_duration); +#if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !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. + const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); + while (!pred()) + { + 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); + d = ts - detail::mono_platform_clock::now(); + } +#else + 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 -#ifndef BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC - #ifdef BOOST_THREAD_USES_CHRONO template <class Duration> cv_status wait_until( unique_lock<mutex>& lock, - const chrono::time_point<chrono::system_clock, Duration>& t) + const chrono::time_point<detail::internal_chrono_clock, Duration>& t) { - using namespace chrono; - typedef time_point<system_clock, nanoseconds> nano_sys_tmpt; - wait_until(lock, - nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - return system_clock::now() < t ? cv_status::no_timeout : - 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 <class Clock, class Duration> @@ -230,100 +259,44 @@ namespace boost unique_lock<mutex>& lock, 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(); - wait_until(lock, s_now + ceil<nanoseconds>(t - c_now)); - 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> cv_status wait_for( unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& d) { - using namespace chrono; - system_clock::time_point s_now = system_clock::now(); - steady_clock::time_point c_now = steady_clock::now(); - wait_until(lock, s_now + ceil<nanoseconds>(d)); - return steady_clock::now() - c_now < d ? cv_status::no_timeout : - cv_status::timeout; - - } - - inline cv_status wait_until( - unique_lock<mutex>& lk, - chrono::time_point<chrono::system_clock, chrono::nanoseconds> tp) - { - using namespace chrono; - nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); - if (do_wait_until(lk, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + return wait_until(lock, chrono::steady_clock::now() + d); } -#endif -#else // defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC -#ifdef BOOST_THREAD_USES_CHRONO - - template <class Duration> - cv_status - wait_until( - unique_lock<mutex>& lock, - const chrono::time_point<chrono::steady_clock, Duration>& t) - { - using namespace chrono; - typedef time_point<steady_clock, nanoseconds> nano_sys_tmpt; - wait_until(lock, - nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - return steady_clock::now() < t ? cv_status::no_timeout : - cv_status::timeout; - } - - template <class Clock, class Duration> - cv_status + template <class Duration, class Predicate> + bool wait_until( - unique_lock<mutex>& lock, - const chrono::time_point<Clock, Duration>& t) - { - using namespace chrono; - steady_clock::time_point s_now = steady_clock::now(); - typename Clock::time_point c_now = Clock::now(); - wait_until(lock, s_now + ceil<nanoseconds>(t - c_now)); - return Clock::now() < t ? cv_status::no_timeout : cv_status::timeout; - } - - template <class Rep, class Period> - cv_status - wait_for( - unique_lock<mutex>& lock, - const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - steady_clock::time_point c_now = steady_clock::now(); - wait_until(lock, c_now + ceil<nanoseconds>(d)); - return steady_clock::now() - c_now < d ? cv_status::no_timeout : - cv_status::timeout; - } - - inline cv_status wait_until( - unique_lock<mutex>& lk, - chrono::time_point<chrono::steady_clock, chrono::nanoseconds> tp) + unique_lock<mutex>& lock, + const chrono::time_point<detail::internal_chrono_clock, Duration>& t, + Predicate pred) { - using namespace chrono; - nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); - if (do_wait_until(lk, ts)) return cv_status::no_timeout; - else return cv_status::timeout; + const detail::internal_platform_timepoint ts(t); + while (!pred()) + { + if (!do_wait_until(lock, ts)) break; // timeout occurred + } + return pred(); } -#endif - -#endif // defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC -#ifdef BOOST_THREAD_USES_CHRONO template <class Clock, class Duration, class Predicate> bool wait_until( @@ -331,12 +304,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 Rep, class Period, class Predicate> @@ -346,7 +325,7 @@ namespace boost const chrono::duration<Rep, Period>& 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 @@ -359,15 +338,11 @@ namespace boost void notify_one() BOOST_NOEXCEPT; void notify_all() BOOST_NOEXCEPT; - - }; BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk); - } - #include <boost/config/abi_suffix.hpp> #endif diff --git a/boost/thread/pthread/mutex.hpp b/boost/thread/pthread/mutex.hpp index 9ac808b6f4..6180ac5ebd 100644 --- a/boost/thread/pthread/mutex.hpp +++ b/boost/thread/pthread/mutex.hpp @@ -16,24 +16,20 @@ #include <boost/thread/lock_types.hpp> #endif #include <boost/thread/thread_time.hpp> +#if defined BOOST_THREAD_USES_DATETIME #include <boost/thread/xtime.hpp> +#endif #include <boost/assert.hpp> #include <errno.h> -#include <boost/thread/pthread/timespec.hpp> +#include <boost/thread/detail/platform_time.hpp> #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp> +#include <boost/thread/pthread/pthread_helpers.hpp> #ifdef BOOST_THREAD_USES_CHRONO #include <boost/chrono/system_clocks.hpp> #include <boost/chrono/ceil.hpp> #endif #include <boost/thread/detail/delete.hpp> -#if (defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS-0)>=200112L) \ - || (defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21) -#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK -#define BOOST_PTHREAD_HAS_TIMEDLOCK -#endif -#endif - #include <boost/config/abi_prefix.hpp> @@ -165,7 +161,7 @@ namespace boost { private: pthread_mutex_t m; -#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK +#ifndef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK pthread_cond_t cond; bool is_locked; #endif @@ -178,13 +174,12 @@ namespace boost { boost::throw_exception(thread_resource_error(res, "boost:: timed_mutex constructor failed in pthread_mutex_init")); } -#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK - int const res2=pthread_cond_init(&cond,NULL); +#ifndef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK + int const res2=pthread::cond_init(cond); if(res2) { BOOST_VERIFY(!posix::pthread_mutex_destroy(&m)); - //BOOST_VERIFY(!pthread_mutex_destroy(&m)); - boost::throw_exception(thread_resource_error(res2, "boost:: timed_mutex constructor failed in pthread_cond_init")); + boost::throw_exception(thread_resource_error(res2, "boost:: timed_mutex constructor failed in pthread::cond_init")); } is_locked=false; #endif @@ -192,7 +187,7 @@ namespace boost ~timed_mutex() { BOOST_VERIFY(!posix::pthread_mutex_destroy(&m)); -#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK +#ifndef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK BOOST_VERIFY(!pthread_cond_destroy(&cond)); #endif } @@ -201,14 +196,36 @@ namespace boost template<typename TimeDuration> bool timed_lock(TimeDuration const & relative_time) { - return timed_lock(get_system_time()+relative_time); + if (relative_time.is_pos_infinity()) + { + lock(); + return true; + } + if (relative_time.is_special()) + { + return true; + } + detail::platform_duration d(relative_time); +#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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + while ( ! do_try_lock_until(detail::internal_platform_clock::now() + d) ) + { + d = ts - detail::mono_platform_clock::now(); + if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + return true; +#else + return do_try_lock_until(detail::internal_platform_clock::now() + d); +#endif } bool timed_lock(boost::xtime const & absolute_time) { return timed_lock(system_time(absolute_time)); } #endif -#ifdef BOOST_PTHREAD_HAS_TIMEDLOCK +#ifdef BOOST_THREAD_USES_PTHREAD_TIMEDLOCK void lock() { int res = posix::pthread_mutex_lock(&m); @@ -246,9 +263,9 @@ namespace boost private: - bool do_try_lock_until(struct timespec const &timeout) + bool do_try_lock_until(detail::internal_platform_timepoint const &timeout) { - int const res=pthread_mutex_timedlock(&m,&timeout); + int const res=pthread_mutex_timedlock(&m,&timeout.getTs()); BOOST_ASSERT(!res || res==ETIMEDOUT); return !res; } @@ -284,18 +301,22 @@ namespace boost } private: - bool do_try_lock_until(struct timespec const &timeout) + bool do_try_lock_until(detail::internal_platform_timepoint const &timeout) { boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); while(is_locked) { - int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout); + int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout.getTs()); if(cond_res==ETIMEDOUT) { - return false; + break; } BOOST_ASSERT(!cond_res); } + if(is_locked) + { + return false; + } is_locked=true; return true; } @@ -305,8 +326,20 @@ namespace boost #if defined BOOST_THREAD_USES_DATETIME bool timed_lock(system_time const & abs_time) { - struct timespec const ts=boost::detail::to_timespec(abs_time); + const detail::real_platform_timepoint ts(abs_time); +#if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + detail::platform_duration d(ts - detail::real_platform_clock::now()); + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + while ( ! do_try_lock_until(detail::internal_platform_clock::now() + d) ) + { + d = ts - detail::real_platform_clock::now(); + if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + return true; +#else return do_try_lock_until(ts); +#endif } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -318,23 +351,21 @@ namespace boost template <class Clock, class Duration> bool try_lock_until(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(); - return try_lock_until(s_now + ceil<nanoseconds>(t - c_now)); + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + common_duration d(t - Clock::now()); + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + while ( ! try_lock_until(detail::internal_chrono_clock::now() + d)) + { + d = t - Clock::now(); + if ( d <= common_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + } + return true; } template <class Duration> - bool try_lock_until(const chrono::time_point<chrono::system_clock, Duration>& t) + bool try_lock_until(const chrono::time_point<detail::internal_chrono_clock, Duration>& t) { - using namespace chrono; - typedef time_point<system_clock, nanoseconds> nano_sys_tmpt; - return try_lock_until(nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - } - bool try_lock_until(const chrono::time_point<chrono::system_clock, chrono::nanoseconds>& tp) - { - //using namespace chrono; - chrono::nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); + detail::internal_platform_timepoint ts(t); return do_try_lock_until(ts); } #endif @@ -352,7 +383,6 @@ namespace boost typedef scoped_timed_lock scoped_lock; #endif }; - } #include <boost/config/abi_suffix.hpp> diff --git a/boost/thread/pthread/pthread_helpers.hpp b/boost/thread/pthread/pthread_helpers.hpp new file mode 100644 index 0000000000..c1ff0f9239 --- /dev/null +++ b/boost/thread/pthread/pthread_helpers.hpp @@ -0,0 +1,42 @@ +#ifndef BOOST_THREAD_PTHREAD_PTHREAD_HELPERS_HPP +#define BOOST_THREAD_PTHREAD_PTHREAD_HELPERS_HPP +// Copyright (C) 2017 +// Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/thread/detail/config.hpp> +#include <pthread.h> + +#include <boost/config/abi_prefix.hpp> + +namespace boost +{ + namespace pthread + { + inline int cond_init(pthread_cond_t& cond) { + + #ifdef BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + pthread_condattr_t attr; + int res = pthread_condattr_init(&attr); + if (res) + { + return res; + } + pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + res=pthread_cond_init(&cond,&attr); + pthread_condattr_destroy(&attr); + return res; + #else + return pthread_cond_init(&cond,NULL); + #endif + + } + } +} + +#include <boost/config/abi_suffix.hpp> + +#endif diff --git a/boost/thread/pthread/recursive_mutex.hpp b/boost/thread/pthread/recursive_mutex.hpp index 4caae0b5df..ce9a8ce44b 100644 --- a/boost/thread/pthread/recursive_mutex.hpp +++ b/boost/thread/pthread/recursive_mutex.hpp @@ -19,27 +19,22 @@ #endif #include <boost/date_time/posix_time/conversion.hpp> #include <errno.h> -#include <boost/thread/pthread/timespec.hpp> +#include <boost/thread/detail/platform_time.hpp> #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp> +#include <boost/thread/pthread/pthread_helpers.hpp> #ifdef BOOST_THREAD_USES_CHRONO #include <boost/chrono/system_clocks.hpp> #include <boost/chrono/ceil.hpp> #endif #include <boost/thread/detail/delete.hpp> -#if (defined _POSIX_TIMEOUTS && (_POSIX_TIMEOUTS-0)>=200112L) \ - || (defined __ANDROID__ && defined __ANDROID_API__ && __ANDROID_API__ >= 21) -#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK -#define BOOST_PTHREAD_HAS_TIMEDLOCK -#endif -#endif #if defined BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE \ || defined __ANDROID__ #define BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE #endif -#if defined BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE && defined BOOST_PTHREAD_HAS_TIMEDLOCK +#if defined BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE && defined BOOST_THREAD_USES_PTHREAD_TIMEDLOCK #define BOOST_USE_PTHREAD_RECURSIVE_TIMEDLOCK #endif @@ -89,11 +84,11 @@ namespace boost { boost::throw_exception(thread_resource_error(res, "boost:: recursive_mutex constructor failed in pthread_mutex_init")); } - int const res2=pthread_cond_init(&cond,NULL); + int const res2=pthread::cond_init(cond); if(res2) { BOOST_VERIFY(!pthread_mutex_destroy(&m)); - boost::throw_exception(thread_resource_error(res2, "boost:: recursive_mutex constructor failed in pthread_cond_init")); + boost::throw_exception(thread_resource_error(res2, "boost:: recursive_mutex constructor failed in pthread::cond_init")); } is_locked=false; count=0; @@ -224,11 +219,11 @@ namespace boost { boost::throw_exception(thread_resource_error(res, "boost:: recursive_timed_mutex constructor failed in pthread_mutex_init")); } - int const res2=pthread_cond_init(&cond,NULL); + int const res2=pthread::cond_init(cond); if(res2) { BOOST_VERIFY(!pthread_mutex_destroy(&m)); - boost::throw_exception(thread_resource_error(res2, "boost:: recursive_timed_mutex constructor failed in pthread_cond_init")); + boost::throw_exception(thread_resource_error(res2, "boost:: recursive_timed_mutex constructor failed in pthread::cond_init")); } is_locked=false; count=0; @@ -246,7 +241,29 @@ namespace boost template<typename TimeDuration> bool timed_lock(TimeDuration const & relative_time) { - return timed_lock(get_system_time()+relative_time); + if (relative_time.is_pos_infinity()) + { + lock(); + return true; + } + if (relative_time.is_special()) + { + return true; + } + detail::platform_duration d(relative_time); +#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(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + while ( ! do_try_lock_until(detail::internal_platform_clock::now() + d) ) + { + d = ts - detail::mono_platform_clock::now(); + if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + return true; +#else + return do_try_lock_until(detail::internal_platform_clock::now() + d); +#endif } #endif @@ -268,9 +285,9 @@ namespace boost return !res; } private: - bool do_try_lock_until(struct timespec const &timeout) + bool do_try_lock_until(detail::internal_platform_timepoint const &timeout) { - int const res=pthread_mutex_timedlock(&m,&timeout); + int const res=pthread_mutex_timedlock(&m,&timeout.getTs()); BOOST_ASSERT(!res || res==ETIMEDOUT); return !res; } @@ -320,7 +337,7 @@ namespace boost } private: - bool do_try_lock_until(struct timespec const &timeout) + bool do_try_lock_until(detail::internal_platform_timepoint const &timeout) { boost::pthread::pthread_mutex_scoped_lock const local_lock(&m); if(is_locked && pthread_equal(owner,pthread_self())) @@ -330,13 +347,17 @@ namespace boost } while(is_locked) { - int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout); + int const cond_res=pthread_cond_timedwait(&cond,&m,&timeout.getTs()); if(cond_res==ETIMEDOUT) { - return false; + break; } BOOST_ASSERT(!cond_res); } + if(is_locked) + { + return false; + } is_locked=true; ++count; owner=pthread_self(); @@ -349,8 +370,20 @@ namespace boost #if defined BOOST_THREAD_USES_DATETIME bool timed_lock(system_time const & abs_time) { - struct timespec const ts=detail::to_timespec(abs_time); + const detail::real_platform_timepoint ts(abs_time); +#if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO + detail::platform_duration d(ts - detail::real_platform_clock::now()); + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + while ( ! do_try_lock_until(detail::internal_platform_clock::now() + d) ) + { + d = ts - detail::real_platform_clock::now(); + if ( d <= detail::platform_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + return true; +#else return do_try_lock_until(ts); +#endif } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -362,23 +395,22 @@ namespace boost template <class Clock, class Duration> bool try_lock_until(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(); - return try_lock_until(s_now + ceil<nanoseconds>(t - c_now)); + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + common_duration d(t - Clock::now()); + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + while ( ! try_lock_until(detail::internal_chrono_clock::now() + d)) + { + d = t - Clock::now(); + if ( d <= common_duration::zero() ) return false; // timeout occurred + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + } + return true; + } template <class Duration> - bool try_lock_until(const chrono::time_point<chrono::system_clock, Duration>& t) - { - using namespace chrono; - typedef time_point<system_clock, nanoseconds> nano_sys_tmpt; - return try_lock_until(nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); - } - bool try_lock_until(const chrono::time_point<chrono::system_clock, chrono::nanoseconds>& tp) + bool try_lock_until(const chrono::time_point<detail::internal_chrono_clock, Duration>& t) { - //using namespace chrono; - chrono::nanoseconds d = tp.time_since_epoch(); - timespec ts = boost::detail::to_timespec(d); + detail::internal_platform_timepoint ts(t); return do_try_lock_until(ts); } #endif diff --git a/boost/thread/pthread/shared_mutex.hpp b/boost/thread/pthread/shared_mutex.hpp index e4ec24fe3f..ed9a296f2e 100644 --- a/boost/thread/pthread/shared_mutex.hpp +++ b/boost/thread/pthread/shared_mutex.hpp @@ -9,6 +9,7 @@ // http://www.boost.org/LICENSE_1_0.txt) #include <boost/assert.hpp> +#include <boost/bind.hpp> #include <boost/static_assert.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/condition_variable.hpp> @@ -78,11 +79,6 @@ namespace boost return ! (shared_count || exclusive); } - void exclusive_blocked (bool blocked) - { - exclusive_waiting_blocked = blocked; - } - void lock () { exclusive = true; @@ -99,17 +95,19 @@ namespace boost return ! (exclusive || exclusive_waiting_blocked); } - bool more_shared () const + bool no_shared () const { - return shared_count > 0 ; + return shared_count==0; } - unsigned get_shared_count () const + + bool one_shared () const { - return shared_count ; + return shared_count==1; } - unsigned lock_shared () + + void lock_shared () { - return ++shared_count; + ++shared_count; } @@ -118,18 +116,6 @@ namespace boost --shared_count; } - bool unlock_shared_downgrades() - { - if (upgrade) { - upgrade=false; - exclusive=true; - return true; - } else { - exclusive_waiting_blocked=false; - return false; - } - } - void lock_upgrade () { ++shared_count; @@ -185,10 +171,7 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - while(!state.can_lock_shared()) - { - shared_cond.wait(lk); - } + shared_cond.wait(lk, boost::bind(&state_data::can_lock_shared, boost::ref(state))); state.lock_shared(); } @@ -211,13 +194,9 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock_shared()) + if(!shared_cond.timed_wait(lk, timeout, boost::bind(&state_data::can_lock_shared, boost::ref(state)))) { - if(!shared_cond.timed_wait(lk,timeout)) - { - return false; - } + return false; } state.lock_shared(); return true; @@ -226,7 +205,16 @@ namespace boost template<typename TimeDuration> bool timed_lock_shared(TimeDuration const & relative_time) { - return timed_lock_shared(get_system_time()+relative_time); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + boost::this_thread::disable_interruption do_not_disturb; +#endif + boost::unique_lock<boost::mutex> lk(state_change); + if(!shared_cond.timed_wait(lk, relative_time, boost::bind(&state_data::can_lock_shared, boost::ref(state)))) + { + return false; + } + state.lock_shared(); + return true; } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -242,14 +230,9 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock_shared()) - //while(state.exclusive || state.exclusive_waiting_blocked) + if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock_shared, boost::ref(state)))) { - if(cv_status::timeout==shared_cond.wait_until(lk,abs_time)) - { - return false; - } + return false; } state.lock_shared(); return true; @@ -260,11 +243,11 @@ namespace boost boost::unique_lock<boost::mutex> lk(state_change); state.assert_lock_shared(); state.unlock_shared(); - if (! state.more_shared()) + if (state.no_shared()) { if (state.upgrade) { - // As there is a thread doing a unlock_upgrade_and_lock that is waiting for ! state.more_shared() + // As there is a thread doing a unlock_upgrade_and_lock that is waiting for state.no_shared() // avoid other threads to lock, lock_upgrade or lock_shared, so only this thread is notified. state.upgrade=false; state.exclusive=true; @@ -286,12 +269,8 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - - while (state.shared_count || state.exclusive) - { - state.exclusive_waiting_blocked=true; - exclusive_cond.wait(lk); - } + state.exclusive_waiting_blocked=true; + exclusive_cond.wait(lk, boost::bind(&state_data::can_lock, boost::ref(state))); state.exclusive=true; } @@ -302,20 +281,12 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - - while(state.shared_count || state.exclusive) + state.exclusive_waiting_blocked=true; + if(!exclusive_cond.timed_wait(lk, timeout, boost::bind(&state_data::can_lock, boost::ref(state)))) { - state.exclusive_waiting_blocked=true; - if(!exclusive_cond.timed_wait(lk,timeout)) - { - if(state.shared_count || state.exclusive) - { - state.exclusive_waiting_blocked=false; - release_waiters(); - return false; - } - break; - } + state.exclusive_waiting_blocked=false; + release_waiters(); + return false; } state.exclusive=true; return true; @@ -324,7 +295,19 @@ namespace boost template<typename TimeDuration> bool timed_lock(TimeDuration const & relative_time) { - return timed_lock(get_system_time()+relative_time); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + boost::this_thread::disable_interruption do_not_disturb; +#endif + boost::unique_lock<boost::mutex> lk(state_change); + state.exclusive_waiting_blocked=true; + if(!exclusive_cond.timed_wait(lk, relative_time, boost::bind(&state_data::can_lock, boost::ref(state)))) + { + state.exclusive_waiting_blocked=false; + release_waiters(); + return false; + } + state.exclusive=true; + return true; } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -340,20 +323,12 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - - while(state.shared_count || state.exclusive) + state.exclusive_waiting_blocked=true; + if(!exclusive_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock, boost::ref(state)))) { - state.exclusive_waiting_blocked=true; - if(cv_status::timeout == exclusive_cond.wait_until(lk,abs_time)) - { - if(state.shared_count || state.exclusive) - { - state.exclusive_waiting_blocked=false; - release_waiters(); - return false; - } - break; - } + state.exclusive_waiting_blocked=false; + release_waiters(); + return false; } state.exclusive=true; return true; @@ -363,17 +338,12 @@ namespace boost bool try_lock() { boost::unique_lock<boost::mutex> lk(state_change); - - if(state.shared_count || state.exclusive) + if(!state.can_lock()) { return false; } - else - { - state.exclusive=true; - return true; - } - + state.exclusive=true; + return true; } void unlock() @@ -392,10 +362,7 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) - { - shared_cond.wait(lk); - } + shared_cond.wait(lk, boost::bind(&state_data::can_lock_upgrade, boost::ref(state))); state.lock_shared(); state.upgrade=true; } @@ -407,16 +374,9 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) + if(!shared_cond.timed_wait(lk, timeout, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) { - if(!shared_cond.timed_wait(lk,timeout)) - { - if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) - { - return false; - } - break; - } + return false; } state.lock_shared(); state.upgrade=true; @@ -426,7 +386,17 @@ namespace boost template<typename TimeDuration> bool timed_lock_upgrade(TimeDuration const & relative_time) { - return timed_lock_upgrade(get_system_time()+relative_time); +#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS + boost::this_thread::disable_interruption do_not_disturb; +#endif + boost::unique_lock<boost::mutex> lk(state_change); + if(!shared_cond.timed_wait(lk, relative_time, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) + { + return false; + } + state.lock_shared(); + state.upgrade=true; + return true; } #endif #ifdef BOOST_THREAD_USES_CHRONO @@ -442,16 +412,9 @@ namespace boost boost::this_thread::disable_interruption do_not_disturb; #endif boost::unique_lock<boost::mutex> lk(state_change); - while(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) + if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) { - if(cv_status::timeout == shared_cond.wait_until(lk,abs_time)) - { - if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) - { - return false; - } - break; - } + return false; } state.lock_shared(); state.upgrade=true; @@ -461,17 +424,14 @@ namespace boost bool try_lock_upgrade() { boost::unique_lock<boost::mutex> lk(state_change); - if(state.exclusive || state.exclusive_waiting_blocked || state.upgrade) + if(!state.can_lock_upgrade()) { return false; } - else - { - state.lock_shared(); - state.upgrade=true; - state.assert_lock_upgraded(); - return true; - } + state.lock_shared(); + state.upgrade=true; + state.assert_lock_upgraded(); + return true; } void unlock_upgrade() @@ -479,7 +439,7 @@ namespace boost boost::unique_lock<boost::mutex> lk(state_change); //state.upgrade=false; state.unlock_upgrade(); - if(! state.more_shared() ) + if(state.no_shared()) { state.exclusive_waiting_blocked=false; release_waiters(); @@ -497,10 +457,7 @@ namespace boost boost::unique_lock<boost::mutex> lk(state_change); state.assert_lock_upgraded(); state.unlock_shared(); - while (state.more_shared()) - { - upgrade_cond.wait(lk); - } + upgrade_cond.wait(lk, boost::bind(&state_data::no_shared, boost::ref(state))); state.upgrade=false; state.exclusive=true; state.assert_locked(); @@ -554,16 +511,9 @@ namespace boost #endif boost::unique_lock<boost::mutex> lk(state_change); state.assert_lock_upgraded(); - if (state.shared_count != 1) + if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::one_shared, boost::ref(state)))) { - for (;;) - { - cv_status status = shared_cond.wait_until(lk,abs_time); - if (state.shared_count == 1) - break; - if(status == cv_status::timeout) - return false; - } + return false; } state.upgrade=false; state.exclusive=true; @@ -619,16 +569,9 @@ namespace boost #endif boost::unique_lock<boost::mutex> lk(state_change); state.assert_lock_shared(); - if (state.shared_count != 1) + if(!shared_cond.wait_until(lk, abs_time, boost::bind(&state_data::one_shared, boost::ref(state)))) { - for (;;) - { - cv_status status = shared_cond.wait_until(lk,abs_time); - if (state.shared_count == 1) - break; - if(status == cv_status::timeout) - return false; - } + return false; } state.upgrade=false; state.exclusive=true; @@ -654,10 +597,7 @@ namespace boost { boost::unique_lock<boost::mutex> lk(state_change); state.assert_lock_shared(); - if( !state.exclusive - && !state.exclusive_waiting_blocked - && !state.upgrade - ) + if(state.can_lock_upgrade()) { state.upgrade=true; return true; @@ -683,22 +623,9 @@ namespace boost #endif boost::unique_lock<boost::mutex> lk(state_change); state.assert_lock_shared(); - if( state.exclusive - || state.exclusive_waiting_blocked - || state.upgrade - ) + if(!exclusive_cond.wait_until(lk, abs_time, boost::bind(&state_data::can_lock_upgrade, boost::ref(state)))) { - for (;;) - { - cv_status status = exclusive_cond.wait_until(lk,abs_time); - if( ! state.exclusive - && ! state.exclusive_waiting_blocked - && ! state.upgrade - ) - break; - if(status == cv_status::timeout) - return false; - } + return false; } state.upgrade=true; return true; diff --git a/boost/thread/pthread/shared_mutex_assert.hpp b/boost/thread/pthread/shared_mutex_assert.hpp deleted file mode 100644 index 186ab7984b..0000000000 --- a/boost/thread/pthread/shared_mutex_assert.hpp +++ /dev/null @@ -1,724 +0,0 @@ -#ifndef BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP -#define BOOST_THREAD_PTHREAD_SHARED_MUTEX_HPP - -// (C) Copyright 2006-8 Anthony Williams -// (C) Copyright 2012 Vicente J. Botet Escriba -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include <boost/assert.hpp> -#include <boost/static_assert.hpp> -#include <boost/thread/mutex.hpp> -#include <boost/thread/condition_variable.hpp> -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS -#include <boost/thread/detail/thread_interruption.hpp> -#endif -#ifdef BOOST_THREAD_USES_CHRONO -#include <boost/chrono/system_clocks.hpp> -#include <boost/chrono/ceil.hpp> -#endif -#include <boost/thread/detail/delete.hpp> -#include <boost/assert.hpp> - -#include <boost/config/abi_prefix.hpp> - -namespace boost -{ - class shared_mutex - { - private: - class state_data - { - public: - state_data () : - shared_count(0), - exclusive(false), - upgrade(false), - exclusive_waiting_blocked(false) - {} - - void assert_free() const - { - BOOST_ASSERT( ! exclusive ); - BOOST_ASSERT( ! upgrade ); - BOOST_ASSERT( shared_count==0 ); - } - - void assert_locked() const - { - BOOST_ASSERT( exclusive ); - BOOST_ASSERT( shared_count==0 ); - BOOST_ASSERT( ! upgrade ); - } - - void assert_lock_shared () const - { - BOOST_ASSERT( ! exclusive ); - BOOST_ASSERT( shared_count>0 ); - //BOOST_ASSERT( (! upgrade) || (shared_count>1)); - // if upgraded there are at least 2 threads sharing the mutex, - // except when unlock_upgrade_and_lock has decreased the number of readers but has not taken yet exclusive ownership. - } - - void assert_lock_upgraded () const - { - BOOST_ASSERT( ! exclusive ); - BOOST_ASSERT( upgrade ); - BOOST_ASSERT( shared_count>0 ); - } - - void assert_lock_not_upgraded () const - { - BOOST_ASSERT( ! upgrade ); - } - - bool can_lock () const - { - return ! (shared_count || exclusive); - } - - void exclusive_blocked (bool blocked) - { - exclusive_waiting_blocked = blocked; - } - - void lock () - { - exclusive = true; - } - - void unlock () - { - exclusive = false; - exclusive_waiting_blocked = false; - } - - bool can_lock_shared () const - { - return ! (exclusive || exclusive_waiting_blocked); - } - - bool is_last_shared () const - { - return !shared_count ; - } - unsigned get_shared_count () const - { - return shared_count ; - } - unsigned lock_shared () - { - return ++shared_count; - } - - - void unlock_shared () - { - --shared_count; - } - - bool unlock_shared_downgrades() - { - if (upgrade) { - upgrade=false; - exclusive=true; - return true; - } else { - exclusive_waiting_blocked=false; - return false; - } - } - - void lock_upgrade () - { - lock_shared (); - upgrade=true; - } - bool can_lock_upgrade () const - { - return ! (exclusive || exclusive_waiting_blocked || upgrade); - } - - void unlock_upgrade () - { - upgrade=false; - unlock_shared(); - } - - //private: - unsigned shared_count; - bool exclusive; - bool upgrade; - bool exclusive_waiting_blocked; - }; - - - - state_data state; - boost::mutex state_change; - boost::condition_variable shared_cond; - boost::condition_variable exclusive_cond; - boost::condition_variable upgrade_cond; - - void release_waiters() - { - exclusive_cond.notify_one(); - shared_cond.notify_all(); - } - - public: - BOOST_THREAD_NO_COPYABLE(shared_mutex) - - shared_mutex() - { - } - - ~shared_mutex() - { - } - - void lock_shared() - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock_shared()) - { - shared_cond.wait(lk); - } - state.lock_shared(); - } - - bool try_lock_shared() - { - boost::unique_lock<boost::mutex> lk(state_change); - if(!state.can_lock_shared()) - { - return false; - } - else - { - state.lock_shared(); - return true; - } - } - -#if defined BOOST_THREAD_USES_DATETIME - bool timed_lock_shared(system_time const& timeout) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock_shared()) - { - if(!shared_cond.timed_wait(lk,timeout)) - { - return false; - } - } - state.lock_shared(); - return true; - } - - template<typename TimeDuration> - bool timed_lock_shared(TimeDuration const & relative_time) - { - return timed_lock_shared(get_system_time()+relative_time); - } -#endif -#ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time) - { - return try_lock_shared_until(chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock_shared()) - { - if(cv_status::timeout==shared_cond.wait_until(lk,abs_time)) - { - return false; - } - } - state.lock_shared(); - return true; - } -#endif - void unlock_shared() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_shared(); - state.unlock_shared(); - if (state.get_shared_count () == 0) - { - if (state.unlock_shared_downgrades()) - { - lk.unlock(); - upgrade_cond.notify_one(); - } else { - lk.unlock(); - } - release_waiters(); - } - } - - void lock() - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock()) - { - state.exclusive_blocked(true); - exclusive_cond.wait(lk); - } - state.lock(); - } - -#if defined BOOST_THREAD_USES_DATETIME - bool timed_lock(system_time const& timeout) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock()) - { - state.exclusive_blocked(true); - if(!exclusive_cond.timed_wait(lk,timeout)) - { - if(!state.can_lock()) - { - state.exclusive_blocked(false); - release_waiters(); - return false; - } - break; - } - } - state.exclusive=true; - //state.lock(); - return true; - } - - template<typename TimeDuration> - bool timed_lock(TimeDuration const & relative_time) - { - return timed_lock(get_system_time()+relative_time); - } -#endif -#ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) - { - return try_lock_until(chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - - while(!state.can_lock()) - { - state.exclusive_blocked(true); - if(cv_status::timeout == exclusive_cond.wait_until(lk,abs_time)) - { - if(!state.can_lock()) - { - state.exclusive_blocked(false); - release_waiters(); - return false; - } - break; - } - } - state.exclusive=true; - //state.lock(); - return true; - } -#endif - - bool try_lock() - { - boost::unique_lock<boost::mutex> lk(state_change); - - if(!state.can_lock()) - { - return false; - } - else - { - state.lock(); - return true; - } - - } - - void unlock() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_locked(); - state.unlock(); - state.assert_free(); - release_waiters(); - } - - void lock_upgrade() - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - while(!state.can_lock_upgrade()) - { - shared_cond.wait(lk); - } - state.lock_upgrade(); - } - -#if defined BOOST_THREAD_USES_DATETIME - bool timed_lock_upgrade(system_time const& timeout) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - while(!state.can_lock_upgrade()) - { - if(!shared_cond.timed_wait(lk,timeout)) - { - if(!state.can_lock_upgrade()) - { - return false; - } - break; - } - } - state.lock_upgrade(); - return true; - } - - template<typename TimeDuration> - bool timed_lock_upgrade(TimeDuration const & relative_time) - { - return timed_lock_upgrade(get_system_time()+relative_time); - } -#endif -#ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool try_lock_upgrade_for(const chrono::duration<Rep, Period>& rel_time) - { - return try_lock_upgrade_until(chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool try_lock_upgrade_until(const chrono::time_point<Clock, Duration>& abs_time) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - while(!state.can_lock_upgrade()) - { - if(cv_status::timeout == shared_cond.wait_until(lk,abs_time)) - { - if(!state.can_lock_upgrade()) - { - return false; - } - break; - } - } - state.lock_upgrade(); - return true; - } -#endif - bool try_lock_upgrade() - { - boost::unique_lock<boost::mutex> lk(state_change); - if(!state.can_lock_upgrade()) - { - return false; - } - else - { - state.lock_upgrade(); - state.assert_lock_upgraded(); - return true; - } - } - - void unlock_upgrade() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_upgraded(); - state.unlock_upgrade(); - state.assert_lock_not_upgraded (); - if(state.get_shared_count () == 0) - { - state.exclusive_blocked(false); - lk.unlock(); - release_waiters(); - } else { - lk.unlock(); - shared_cond.notify_all(); - } - } - - // Upgrade <-> Exclusive - void unlock_upgrade_and_lock() - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_upgraded(); - // assert state.get_shared_count() >=1 - while( - //! state.exclusive_waiting_blocked // Fixme: is this needed? - //&& - state.get_shared_count()!=1) - { - upgrade_cond.wait(lk); - } - state.unlock_upgrade(); - state.lock(); - state.assert_locked(); - } - - void unlock_and_lock_upgrade() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_locked(); - state.unlock(); - state.lock_upgrade(); - state.assert_lock_upgraded(); - release_waiters(); - } - - bool try_unlock_upgrade_and_lock() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_upgraded(); - if( //!state.exclusive // this should be removed once the assertion work - ! state.exclusive_waiting_blocked // Fixme: why this is needed? - //&& state.upgrade // this should be removed once the assertion work - && state.get_shared_count()==1) - { - state.unlock_upgrade(); - state.lock(); - state.assert_locked(); - return true; - } - return false; - } -#ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool - try_unlock_upgrade_and_lock_for( - const chrono::duration<Rep, Period>& rel_time) - { - return try_unlock_upgrade_and_lock_until( - chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool - try_unlock_upgrade_and_lock_until( - const chrono::time_point<Clock, Duration>& abs_time) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_upgraded(); - if (//state.exclusive // this should be removed once the assertion work - state.exclusive_waiting_blocked // Fixme: is this needed? - //|| ! state.upgrade // this should be removed once the assertion work - || state.get_shared_count() != 1) - { - for (;;) - { - //cv_status status = shared_cond.wait_until(lk,abs_time); - cv_status status = upgrade_cond.wait_until(lk,abs_time); - if (//!state.exclusive // this should be removed once the assertion work - ! state.exclusive_waiting_blocked // Fixme: is this needed? - //&& ! state.upgrade // this should be removed once the assertion work - && state.get_shared_count() == 1) - break; - if(status == cv_status::timeout) - return false; - } - } - state.unlock_upgrade(); - state.lock(); - return true; - } -#endif - - // Shared <-> Exclusive - void unlock_and_lock_shared() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_locked(); - state.unlock(); - state.lock_shared(); - release_waiters(); - } - -#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS - bool try_unlock_shared_and_lock() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_shared(); - if( //!state.exclusive // this should be removed once the assertion work - ! state.exclusive_waiting_blocked // Fixme: why this is needed? - //&& ! state.upgrade // Fixme: why this is needed if state.get_shared_count()==1? - && state.get_shared_count()==1) - { - state.unlock_shared(); - state.lock(); - return true; - } - return false; - } -#ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool - try_unlock_shared_and_lock_for( - const chrono::duration<Rep, Period>& rel_time) - { - return try_unlock_shared_and_lock_until( - chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool - try_unlock_shared_and_lock_until( - const chrono::time_point<Clock, Duration>& abs_time) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_shared(); - if ( // !state.exclusive // this should be removed once the assertion work - state.exclusive_waiting_blocked // Fixme: is this needed? - //|| state.upgrade // Fixme: why this is needed if state.get_shared_count()==1? - || state.get_shared_count() != 1) - { - for (;;) - { - cv_status status = shared_cond.wait_until(lk,abs_time); - if ( //! state.exclusive // this should be removed once the assertion work - ! state.exclusive_waiting_blocked // Fixme: is this needed? - //&& ! state.upgrade - && state.get_shared_count() == 1) - break; - if(status == cv_status::timeout) - return false; - } - } - state.unlock_shared(); - state.lock(); - state.upgrade=false; // Is this absolutely needed? - state.exclusive_waiting_blocked=false; // Is this absolutely needed? - return true; - } -#endif -#endif - - // Shared <-> Upgrade - void unlock_upgrade_and_lock_shared() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_upgraded(); - //state.unlock_upgrade(); - //state.lock_shared(); // less efficient - state.upgrade=false; - state.exclusive_waiting_blocked=false; // Is this absolutely needed? - release_waiters(); - } - -#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS - bool try_unlock_shared_and_lock_upgrade() - { - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_shared(); - if( //! state.exclusive // this should be removed once the assertion work - ! state.exclusive_waiting_blocked // Fixme: is this needed? - && ! state.upgrade - ) - { - state.upgrade=true; - return true; - } - return false; - } -#ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool - try_unlock_shared_and_lock_upgrade_for( - const chrono::duration<Rep, Period>& rel_time) - { - return try_unlock_shared_and_lock_upgrade_until( - chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool - try_unlock_shared_and_lock_upgrade_until( - const chrono::time_point<Clock, Duration>& abs_time) - { -#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS - boost::this_thread::disable_interruption do_not_disturb; -#endif - boost::unique_lock<boost::mutex> lk(state_change); - state.assert_lock_shared(); - if( //state.exclusive // this should be removed once the assertion work - state.exclusive_waiting_blocked // Fixme: is this needed? - || state.upgrade - ) - { - for (;;) - { - cv_status status = exclusive_cond.wait_until(lk,abs_time); - if( //! state.exclusive // this should be removed once the assertion work - ! state.exclusive_waiting_blocked // Fixme: is this needed? - && ! state.upgrade - ) - break; - if(status == cv_status::timeout) - return false; - } - } - //state.unlock_shared(); - //state.lock_upgrade(); // less efficient - state.upgrade=true; - return true; - } -#endif -#endif - }; - - typedef shared_mutex upgrade_mutex; -} - -#include <boost/config/abi_suffix.hpp> - -#endif diff --git a/boost/thread/pthread/thread_data.hpp b/boost/thread/pthread/thread_data.hpp index f6575f1c22..859ccfbd2a 100644 --- a/boost/thread/pthread/thread_data.hpp +++ b/boost/thread/pthread/thread_data.hpp @@ -16,6 +16,7 @@ #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/assert.hpp> +#include <boost/thread/detail/platform_time.hpp> #ifdef BOOST_THREAD_USES_CHRONO #include <boost/chrono/system_clocks.hpp> #endif @@ -111,8 +112,6 @@ namespace boost pthread_t thread_handle; boost::mutex data_mutex; boost::condition_variable done_condition; - boost::mutex sleep_mutex; - boost::condition_variable sleep_condition; bool done; bool join_started; bool joined; @@ -241,66 +240,158 @@ namespace boost namespace this_thread { + void BOOST_THREAD_DECL yield() BOOST_NOEXCEPT; + namespace hidden { - void BOOST_THREAD_DECL sleep_for(const timespec& ts); - void BOOST_THREAD_DECL sleep_until_realtime(const timespec& ts); + inline bool always_false() + { + return false; + } + } + +#if defined BOOST_THREAD_USES_DATETIME +#ifdef __DECXXX + /// Workaround of DECCXX issue of incorrect template substitution + template<> +#endif + inline void sleep(system_time const& abs_time) + { + mutex mx; + unique_lock<mutex> lock(mx); + condition_variable cond; + cond.timed_wait(lock, abs_time, hidden::always_false); } + template<typename TimeDuration> + void sleep(TimeDuration const& rel_time) + { + mutex mx; + unique_lock<mutex> lock(mx); + condition_variable cond; + cond.timed_wait(lock, rel_time, hidden::always_false); + } +#endif + #ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d); -#ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY + template <class Clock, class Duration> + void sleep_until(const chrono::time_point<Clock, Duration>& t) + { + mutex mut; + unique_lock<mutex> lk(mut); + condition_variable cv; + cv.wait_until(lk, t, hidden::always_false); + } - inline - void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns) + template <class Rep, class Period> + void sleep_for(const chrono::duration<Rep, Period>& d) { - return boost::this_thread::hidden::sleep_for(boost::detail::to_timespec(ns)); + mutex mut; + unique_lock<mutex> lk(mut); + condition_variable cv; + cv.wait_for(lk, d, hidden::always_false); } #endif -#endif // BOOST_THREAD_USES_CHRONO namespace no_interruption_point { +#if defined BOOST_THREAD_SLEEP_FOR_IS_STEADY +// Use pthread_delay_np or nanosleep when available +// because they do not provide an interruption point. + namespace hidden { - void BOOST_THREAD_DECL sleep_for(const timespec& ts); - void BOOST_THREAD_DECL sleep_until_realtime(const timespec& ts); + void BOOST_THREAD_DECL sleep_for_internal(const detail::platform_duration& ts); } - #ifdef BOOST_THREAD_USES_CHRONO +#if defined BOOST_THREAD_USES_DATETIME +#ifdef __DECXXX + /// Workaround of DECCXX issue of incorrect template substitution + template<> +#endif + inline void sleep(system_time const& abs_time) + { + const detail::real_platform_timepoint ts(abs_time); + detail::platform_duration d(ts - detail::real_platform_clock::now()); + while (d > detail::platform_duration::zero()) + { + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + hidden::sleep_for_internal(d); + d = ts - detail::real_platform_clock::now(); + } + } + + template<typename TimeDuration> + void sleep(TimeDuration const& rel_time) + { + hidden::sleep_for_internal(detail::platform_duration(rel_time)); + } +#endif + +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d); - #ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY + void sleep_for(const chrono::duration<Rep, Period>& d) + { + hidden::sleep_for_internal(detail::platform_duration(d)); + } - inline - void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns) + template <class Duration> + void sleep_until(const chrono::time_point<chrono::steady_clock, Duration>& t) { - return boost::this_thread::no_interruption_point::hidden::sleep_for(boost::detail::to_timespec(ns)); + sleep_for(t - chrono::steady_clock::now()); } - #endif - #endif // BOOST_THREAD_USES_CHRONO - } // no_interruption_point + template <class Clock, class Duration> + void sleep_until(const chrono::time_point<Clock, Duration>& t) + { + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + common_duration d(t - Clock::now()); + while (d > common_duration::zero()) + { + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + hidden::sleep_for_internal(detail::platform_duration(d)); + d = t - Clock::now(); + } + } +#endif - void BOOST_THREAD_DECL yield() BOOST_NOEXCEPT; +#else // BOOST_THREAD_SLEEP_FOR_IS_STEADY +// When pthread_delay_np and nanosleep are not available, +// fall back to using the interruptible sleep functions. #if defined BOOST_THREAD_USES_DATETIME #ifdef __DECXXX - /// Workaround of DECCXX issue of incorrect template substitution - template<> + /// Workaround of DECCXX issue of incorrect template substitution + template<> #endif - inline void sleep(system_time const& abs_time) - { - return boost::this_thread::hidden::sleep_until_realtime(boost::detail::to_timespec(abs_time)); - } + inline void sleep(system_time const& abs_time) + { + this_thread::sleep(abs_time); + } - template<typename TimeDuration> - inline BOOST_SYMBOL_VISIBLE void sleep(TimeDuration const& rel_time) - { - this_thread::sleep(get_system_time()+rel_time); - } -#endif // BOOST_THREAD_USES_DATETIME + template<typename TimeDuration> + void sleep(TimeDuration const& rel_time) + { + this_thread::sleep(rel_time); + } +#endif + +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + void sleep_until(const chrono::time_point<Clock, Duration>& t) + { + this_thread::sleep_until(t); + } + + template <class Rep, class Period> + void sleep_for(const chrono::duration<Rep, Period>& d) + { + this_thread::sleep_for(d); + } +#endif + +#endif // BOOST_THREAD_SLEEP_FOR_IS_STEADY + } // no_interruption_point } // this_thread } diff --git a/boost/thread/pthread/timespec.hpp b/boost/thread/pthread/timespec.hpp deleted file mode 100644 index 1fb8de94c8..0000000000 --- a/boost/thread/pthread/timespec.hpp +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef BOOST_THREAD_PTHREAD_TIMESPEC_HPP -#define BOOST_THREAD_PTHREAD_TIMESPEC_HPP -// (C) Copyright 2007-8 Anthony Williams -// (C) Copyright 2012 Vicente J. Botet Escriba -// -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include <boost/thread/detail/config.hpp> -#include <boost/thread/thread_time.hpp> -#if defined BOOST_THREAD_USES_DATETIME -#include <boost/date_time/posix_time/conversion.hpp> -#endif -#include <pthread.h> -#ifndef _WIN32 -#include <unistd.h> -#endif -#ifdef BOOST_THREAD_USES_CHRONO -#include <boost/chrono/duration.hpp> -#endif - -#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) -# define BOOST_THREAD_TIMESPEC_MAC_API -#include <sys/time.h> //for gettimeofday and timeval -#else -#include <time.h> // for clock_gettime -#endif - -#include <boost/config/abi_prefix.hpp> - -namespace boost -{ - namespace detail - { -#if defined BOOST_THREAD_USES_DATETIME - inline struct timespec to_timespec(boost::system_time const& abs_time) - { - struct timespec timeout = { 0,0}; - boost::posix_time::time_duration const time_since_epoch=abs_time-boost::posix_time::from_time_t(0); - - timeout.tv_sec=time_since_epoch.total_seconds(); - timeout.tv_nsec=(long)(time_since_epoch.fractional_seconds()*(1000000000l/time_since_epoch.ticks_per_second())); - return timeout; - } -#endif -#if defined BOOST_THREAD_USES_CHRONO - inline timespec to_timespec(chrono::nanoseconds const& ns) - { - struct timespec ts; - ts.tv_sec = static_cast<long>(chrono::duration_cast<chrono::seconds>(ns).count()); - ts.tv_nsec = static_cast<long>((ns - chrono::duration_cast<chrono::seconds>(ns)).count()); - return ts; - } - -#endif - - inline timespec to_timespec(boost::intmax_t const& ns) - { - boost::intmax_t s = ns / 1000000000l; - struct timespec ts; - ts.tv_sec = static_cast<long> (s); - ts.tv_nsec = static_cast<long> (ns - s * 1000000000l); - return ts; - } - inline boost::intmax_t to_nanoseconds_int_max(timespec const& ts) - { - return static_cast<boost::intmax_t>(ts.tv_sec) * 1000000000l + ts.tv_nsec; - } - inline bool timespec_ge_zero(timespec const& ts) - { - return (ts.tv_sec >= 0) || (ts.tv_nsec >= 0); - } -#if defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC - - inline timespec timespec_now_monotonic() - { - timespec ts; - - if ( ::clock_gettime( CLOCK_MONOTONIC, &ts ) ) - { - ts.tv_sec = 0; - ts.tv_nsec = 0; - BOOST_ASSERT(0 && "Boost::Thread - Internal Error"); - } - return ts; - } -#endif - - inline timespec timespec_now_realtime() - { - timespec ts; - -#if defined(BOOST_THREAD_TIMESPEC_MAC_API) - timeval tv; - ::gettimeofday(&tv, 0); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; -#else - if ( ::clock_gettime( CLOCK_REALTIME, &ts ) ) - { - ts.tv_sec = 0; - ts.tv_nsec = 0; - BOOST_ASSERT(0 && "Boost::Thread - Internal Error"); - } -#endif - return ts; - } - inline timespec timespec_zero() - { - timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = 0; - return ts; - } - inline timespec timespec_plus(timespec const& lhs, timespec const& rhs) - { - return to_timespec(to_nanoseconds_int_max(lhs) + to_nanoseconds_int_max(rhs)); - } - inline timespec timespec_minus(timespec const& lhs, timespec const& rhs) - { - return to_timespec(to_nanoseconds_int_max(lhs) - to_nanoseconds_int_max(rhs)); - } - inline bool timespec_gt(timespec const& lhs, timespec const& rhs) - { - return to_nanoseconds_int_max(lhs) > to_nanoseconds_int_max(rhs); - } - inline bool timespec_ge(timespec const& lhs, timespec const& rhs) - { - return to_nanoseconds_int_max(lhs) >= to_nanoseconds_int_max(rhs); - } - - } -} - -#include <boost/config/abi_suffix.hpp> - -#endif diff --git a/boost/thread/shared_mutex.hpp b/boost/thread/shared_mutex.hpp index ce5d6d1bfb..20a95d8c51 100644 --- a/boost/thread/shared_mutex.hpp +++ b/boost/thread/shared_mutex.hpp @@ -13,13 +13,20 @@ #include <boost/thread/detail/config.hpp> #if defined(BOOST_THREAD_PLATFORM_WIN32) #if defined(BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN) +#if defined(BOOST_THREAD_V2_SHARED_MUTEX) +#include <boost/thread/v2/shared_mutex.hpp> +#else #include <boost/thread/pthread/shared_mutex.hpp> +#endif #else #include <boost/thread/win32/shared_mutex.hpp> #endif #elif defined(BOOST_THREAD_PLATFORM_PTHREAD) -//#include <boost/thread/v2/shared_mutex.hpp> +#if defined(BOOST_THREAD_V2_SHARED_MUTEX) +#include <boost/thread/v2/shared_mutex.hpp> +#else #include <boost/thread/pthread/shared_mutex.hpp> +#endif #else #error "Boost threads unavailable on this platform" #endif diff --git a/boost/thread/thread_only.hpp b/boost/thread/thread_only.hpp index 0d0c07061f..d408344eef 100644 --- a/boost/thread/thread_only.hpp +++ b/boost/thread/thread_only.hpp @@ -23,7 +23,7 @@ #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS #include <boost/thread/detail/thread_interruption.hpp> #endif -#include <boost/thread/v2/thread.hpp> +#include <boost/thread/condition_variable.hpp> #endif diff --git a/boost/thread/v2/shared_mutex.hpp b/boost/thread/v2/shared_mutex.hpp index 5e8b24f70a..e30a59e413 100644 --- a/boost/thread/v2/shared_mutex.hpp +++ b/boost/thread/v2/shared_mutex.hpp @@ -153,222 +153,292 @@ public: #include <boost/thread/mutex.hpp> #include <boost/thread/condition_variable.hpp> #include <boost/thread/mutex.hpp> +#ifdef BOOST_THREAD_USES_CHRONO #include <boost/chrono.hpp> +#endif #include <climits> #include <boost/system/system_error.hpp> -#define BOOST_THREAD_INLINE inline +#include <boost/bind.hpp> namespace boost { namespace thread_v2 { class shared_mutex { - typedef ::boost::mutex mutex_t; - typedef ::boost::condition_variable cond_t; - typedef unsigned count_t; + typedef boost::mutex mutex_t; + typedef boost::condition_variable cond_t; + typedef unsigned count_t; mutex_t mut_; cond_t gate1_; + // the gate2_ condition variable is only used by functions that + // have taken write_entered_ but are waiting for no_readers() cond_t gate2_; count_t state_; static const count_t write_entered_ = 1U << (sizeof(count_t)*CHAR_BIT - 1); static const count_t n_readers_ = ~write_entered_; - public: - BOOST_THREAD_INLINE shared_mutex(); - BOOST_THREAD_INLINE ~shared_mutex(); - -#ifndef BOOST_NO_CXX11_DELETED_FUNCTIONS - shared_mutex(shared_mutex const&) = delete; - shared_mutex& operator=(shared_mutex const&) = delete; -#else // BOOST_NO_CXX11_DELETED_FUNCTIONS - private: + bool no_writer() const + { + return (state_ & write_entered_) == 0; + } + + bool one_writer() const + { + return (state_ & write_entered_) != 0; + } + + bool no_writer_no_readers() const + { + //return (state_ & write_entered_) == 0 && + // (state_ & n_readers_) == 0; + return state_ == 0; + } + + bool no_writer_no_max_readers() const + { + return (state_ & write_entered_) == 0 && + (state_ & n_readers_) != n_readers_; + } + + bool no_readers() const + { + return (state_ & n_readers_) == 0; + } + + bool one_or_more_readers() const + { + return (state_ & n_readers_) > 0; + } + shared_mutex(shared_mutex const&); shared_mutex& operator=(shared_mutex const&); + public: -#endif // BOOST_NO_CXX11_DELETED_FUNCTIONS + shared_mutex(); + ~shared_mutex(); // Exclusive ownership - BOOST_THREAD_INLINE void lock(); - BOOST_THREAD_INLINE bool try_lock(); + void lock(); + bool try_lock(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> bool try_lock_for(const boost::chrono::duration<Rep, Period>& rel_time) { - return try_lock_until(boost::chrono::steady_clock::now() + rel_time); + return try_lock_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_lock_until( + bool try_lock_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock(); - +#endif +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool timed_lock(T const & abs_or_rel_time); +#endif + void unlock(); // Shared ownership - BOOST_THREAD_INLINE void lock_shared(); - BOOST_THREAD_INLINE bool try_lock_shared(); + void lock_shared(); + bool try_lock_shared(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - bool - try_lock_shared_for(const boost::chrono::duration<Rep, Period>& rel_time) + bool try_lock_shared_for(const boost::chrono::duration<Rep, Period>& rel_time) { - return try_lock_shared_until(boost::chrono::steady_clock::now() + - rel_time); + return try_lock_shared_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_lock_shared_until( + bool try_lock_shared_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock_shared(); - +#endif #if defined BOOST_THREAD_USES_DATETIME - bool timed_lock(system_time const& timeout); - template<typename TimeDuration> - bool timed_lock(TimeDuration const & relative_time) + template<typename T> + bool timed_lock_shared(T const & abs_or_rel_time); +#endif + void unlock_shared(); + }; + + inline shared_mutex::shared_mutex() + : state_(0) + { + } + + inline shared_mutex::~shared_mutex() + { + boost::lock_guard<mutex_t> _(mut_); + } + + // Exclusive ownership + + inline void shared_mutex::lock() + { + boost::unique_lock<mutex_t> lk(mut_); + gate1_.wait(lk, boost::bind(&shared_mutex::no_writer, boost::ref(*this))); + state_ |= write_entered_; + gate2_.wait(lk, boost::bind(&shared_mutex::no_readers, boost::ref(*this))); + } + + inline bool shared_mutex::try_lock() + { + boost::unique_lock<mutex_t> lk(mut_); + if (!no_writer_no_readers()) + { + return false; + } + state_ = write_entered_; + return true; + } + +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + bool shared_mutex::try_lock_until( + const boost::chrono::time_point<Clock, Duration>& abs_time) + { + boost::unique_lock<mutex_t> lk(mut_); + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &shared_mutex::no_writer, boost::ref(*this)))) { - return timed_lock(get_system_time()+relative_time); + return false; } - bool timed_lock_shared(system_time const& timeout); - template<typename TimeDuration> - bool timed_lock_shared(TimeDuration const & relative_time) + state_ |= write_entered_; + if (!gate2_.wait_until(lk, abs_time, boost::bind( + &shared_mutex::no_readers, boost::ref(*this)))) { - return timed_lock_shared(get_system_time()+relative_time); + state_ &= ~write_entered_; + return false; } + return true; + } #endif - }; - template <class Clock, class Duration> - bool - shared_mutex::try_lock_until( - const boost::chrono::time_point<Clock, Duration>& abs_time) +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool shared_mutex::timed_lock(T const & abs_or_rel_time) { boost::unique_lock<mutex_t> lk(mut_); - if (state_ & write_entered_) + if (!gate1_.timed_wait(lk, abs_or_rel_time, boost::bind( + &shared_mutex::no_writer, boost::ref(*this)))) { - for (;;) - { - boost::cv_status status = gate1_.wait_until(lk, abs_time); - if ((state_ & write_entered_) == 0) - break; - if (status == boost::cv_status::timeout) - return false; - } + return false; } state_ |= write_entered_; - if (state_ & n_readers_) + if (!gate2_.timed_wait(lk, abs_or_rel_time, boost::bind( + &shared_mutex::no_readers, boost::ref(*this)))) + { + state_ &= ~write_entered_; + return false; + } + return true; + } +#endif + + inline void shared_mutex::unlock() + { + boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(one_writer()); + BOOST_ASSERT(no_readers()); + state_ = 0; + // notify all since multiple *lock_shared*() calls may be able + // to proceed in response to this notification + gate1_.notify_all(); + } + + // Shared ownership + + inline void shared_mutex::lock_shared() + { + boost::unique_lock<mutex_t> lk(mut_); + gate1_.wait(lk, boost::bind(&shared_mutex::no_writer_no_max_readers, boost::ref(*this))); + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= num_readers; + } + + inline bool shared_mutex::try_lock_shared() + { + boost::unique_lock<mutex_t> lk(mut_); + if (!no_writer_no_max_readers()) { - for (;;) - { - boost::cv_status status = gate2_.wait_until(lk, abs_time); - if ((state_ & n_readers_) == 0) - break; - if (status == boost::cv_status::timeout) - { - state_ &= ~write_entered_; - return false; - } - } + return false; } + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= num_readers; return true; } +#ifdef BOOST_THREAD_USES_CHRONO template <class Clock, class Duration> - bool - shared_mutex::try_lock_shared_until( + bool shared_mutex::try_lock_shared_until( const boost::chrono::time_point<Clock, Duration>& abs_time) { boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &shared_mutex::no_writer_no_max_readers, boost::ref(*this)))) { - for (;;) - { - boost::cv_status status = gate1_.wait_until(lk, abs_time); - if ((state_ & write_entered_) == 0 && - (state_ & n_readers_) < n_readers_) - break; - if (status == boost::cv_status::timeout) - return false; - } + return false; } count_t num_readers = (state_ & n_readers_) + 1; state_ &= ~n_readers_; state_ |= num_readers; return true; } +#endif #if defined BOOST_THREAD_USES_DATETIME - bool shared_mutex::timed_lock(system_time const& abs_time) + template<typename T> + bool shared_mutex::timed_lock_shared(T const & abs_or_rel_time) { boost::unique_lock<mutex_t> lk(mut_); - if (state_ & write_entered_) - { - for (;;) - { - bool status = gate1_.timed_wait(lk, abs_time); - if ((state_ & write_entered_) == 0) - break; - if (!status) - return false; - } - } - state_ |= write_entered_; - if (state_ & n_readers_) + if (!gate1_.timed_wait(lk, abs_or_rel_time, boost::bind( + &shared_mutex::no_writer_no_max_readers, boost::ref(*this)))) { - for (;;) - { - bool status = gate2_.timed_wait(lk, abs_time); - if ((state_ & n_readers_) == 0) - break; - if (!status) - { - state_ &= ~write_entered_; - return false; - } - } + return false; } + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= num_readers; return true; } - bool shared_mutex::timed_lock_shared(system_time const& abs_time) +#endif + + inline void shared_mutex::unlock_shared() + { + boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(one_or_more_readers()); + count_t num_readers = (state_ & n_readers_) - 1; + state_ &= ~n_readers_; + state_ |= num_readers; + if (no_writer()) { - boost::unique_lock<mutex_t> lk(mut_); - if (state_ & write_entered_) - { - for (;;) - { - bool status = gate1_.timed_wait(lk, abs_time); - if ((state_ & write_entered_) == 0) - break; - if (!status ) - return false; - } - } - state_ |= write_entered_; - if (state_ & n_readers_) - { - for (;;) - { - bool status = gate2_.timed_wait(lk, abs_time); - if ((state_ & n_readers_) == 0) - break; - if (!status) - { - state_ &= ~write_entered_; - return false; - } - } - } - return true; + if (num_readers == n_readers_ - 1) + gate1_.notify_one(); } -#endif + else + { + if (num_readers == 0) + gate2_.notify_one(); + } + } + + } // thread_v2 +} // boost + +namespace boost { + namespace thread_v2 { + class upgrade_mutex { typedef boost::mutex mutex_t; typedef boost::condition_variable cond_t; - typedef unsigned count_t; + typedef unsigned count_t; mutex_t mut_; cond_t gate1_; + // the gate2_ condition variable is only used by functions that + // have taken write_entered_ but are waiting for no_readers() cond_t gate2_; count_t state_; @@ -376,677 +446,597 @@ namespace boost { static const unsigned upgradable_entered_ = write_entered_ >> 1; static const unsigned n_readers_ = ~(write_entered_ | upgradable_entered_); - public: + bool no_writer() const + { + return (state_ & write_entered_) == 0; + } + + bool one_writer() const + { + return (state_ & write_entered_) != 0; + } + + bool no_writer_no_max_readers() const + { + return (state_ & write_entered_) == 0 && + (state_ & n_readers_) != n_readers_; + } + + bool no_writer_no_upgrader() const + { + return (state_ & (write_entered_ | upgradable_entered_)) == 0; + } + + bool no_writer_no_upgrader_no_readers() const + { + //return (state_ & (write_entered_ | upgradable_entered_)) == 0 && + // (state_ & n_readers_) == 0; + return state_ == 0; + } + + bool no_writer_no_upgrader_one_reader() const + { + //return (state_ & (write_entered_ | upgradable_entered_)) == 0 && + // (state_ & n_readers_) == 1; + return state_ == 1; + } + + bool no_writer_no_upgrader_no_max_readers() const + { + return (state_ & (write_entered_ | upgradable_entered_)) == 0 && + (state_ & n_readers_) != n_readers_; + } + + bool no_upgrader() const + { + return (state_ & upgradable_entered_) == 0; + } + + bool one_upgrader() const + { + return (state_ & upgradable_entered_) != 0; + } + + bool no_readers() const + { + return (state_ & n_readers_) == 0; + } - BOOST_THREAD_INLINE upgrade_mutex(); - BOOST_THREAD_INLINE ~upgrade_mutex(); + bool one_reader() const + { + return (state_ & n_readers_) == 1; + } + + bool one_or_more_readers() const + { + return (state_ & n_readers_) > 0; + } -#ifndef BOOST_CXX11_NO_DELETED_FUNCTIONS - upgrade_mutex(const upgrade_mutex&) = delete; - upgrade_mutex& operator=(const upgrade_mutex&) = delete; -#else // BOOST_CXX11_NO_DELETED_FUNCTIONS - private: upgrade_mutex(const upgrade_mutex&); upgrade_mutex& operator=(const upgrade_mutex&); + public: -#endif // BOOST_CXX11_NO_DELETED_FUNCTIONS + upgrade_mutex(); + ~upgrade_mutex(); // Exclusive ownership - BOOST_THREAD_INLINE void lock(); - BOOST_THREAD_INLINE bool try_lock(); + void lock(); + bool try_lock(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> bool try_lock_for(const boost::chrono::duration<Rep, Period>& rel_time) { - return try_lock_until(boost::chrono::steady_clock::now() + rel_time); + return try_lock_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_lock_until( + bool try_lock_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock(); +#endif +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool timed_lock(T const & abs_or_rel_time); +#endif + void unlock(); // Shared ownership - BOOST_THREAD_INLINE void lock_shared(); - BOOST_THREAD_INLINE bool try_lock_shared(); + void lock_shared(); + bool try_lock_shared(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - bool - try_lock_shared_for(const boost::chrono::duration<Rep, Period>& rel_time) + bool try_lock_shared_for(const boost::chrono::duration<Rep, Period>& rel_time) { - return try_lock_shared_until(boost::chrono::steady_clock::now() + - rel_time); + return try_lock_shared_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_lock_shared_until( + bool try_lock_shared_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock_shared(); +#endif +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool timed_lock_shared(T const & abs_or_rel_time); +#endif + void unlock_shared(); // Upgrade ownership - BOOST_THREAD_INLINE void lock_upgrade(); - BOOST_THREAD_INLINE bool try_lock_upgrade(); + void lock_upgrade(); + bool try_lock_upgrade(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - bool - try_lock_upgrade_for( + bool try_lock_upgrade_for( const boost::chrono::duration<Rep, Period>& rel_time) { - return try_lock_upgrade_until(boost::chrono::steady_clock::now() + - rel_time); + return try_lock_upgrade_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_lock_upgrade_until( + bool try_lock_upgrade_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock_upgrade(); +#endif +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool timed_lock_upgrade(T const & abs_or_rel_time); +#endif + void unlock_upgrade(); // Shared <-> Exclusive - BOOST_THREAD_INLINE bool try_unlock_shared_and_lock(); +#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS + //bool unlock_shared_and_lock(); // can cause a deadlock if used + bool try_unlock_shared_and_lock(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - bool - try_unlock_shared_and_lock_for( + bool try_unlock_shared_and_lock_for( const boost::chrono::duration<Rep, Period>& rel_time) { - return try_unlock_shared_and_lock_until( - boost::chrono::steady_clock::now() + rel_time); + return try_unlock_shared_and_lock_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_unlock_shared_and_lock_until( + bool try_unlock_shared_and_lock_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock_and_lock_shared(); +#endif +#endif + void unlock_and_lock_shared(); // Shared <-> Upgrade - BOOST_THREAD_INLINE bool try_unlock_shared_and_lock_upgrade(); +#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS + //bool unlock_shared_and_lock_upgrade(); // can cause a deadlock if used + bool try_unlock_shared_and_lock_upgrade(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - bool - try_unlock_shared_and_lock_upgrade_for( + bool try_unlock_shared_and_lock_upgrade_for( const boost::chrono::duration<Rep, Period>& rel_time) { - return try_unlock_shared_and_lock_upgrade_until( - boost::chrono::steady_clock::now() + rel_time); + return try_unlock_shared_and_lock_upgrade_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_unlock_shared_and_lock_upgrade_until( + bool try_unlock_shared_and_lock_upgrade_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock_upgrade_and_lock_shared(); +#endif +#endif + void unlock_upgrade_and_lock_shared(); // Upgrade <-> Exclusive - BOOST_THREAD_INLINE void unlock_upgrade_and_lock(); - BOOST_THREAD_INLINE bool try_unlock_upgrade_and_lock(); + void unlock_upgrade_and_lock(); + bool try_unlock_upgrade_and_lock(); +#ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> - bool - try_unlock_upgrade_and_lock_for( + bool try_unlock_upgrade_and_lock_for( const boost::chrono::duration<Rep, Period>& rel_time) { - return try_unlock_upgrade_and_lock_until( - boost::chrono::steady_clock::now() + rel_time); + return try_unlock_upgrade_and_lock_until(chrono::steady_clock::now() + rel_time); } template <class Clock, class Duration> - bool - try_unlock_upgrade_and_lock_until( + bool try_unlock_upgrade_and_lock_until( const boost::chrono::time_point<Clock, Duration>& abs_time); - BOOST_THREAD_INLINE void unlock_and_lock_upgrade(); - -#if defined BOOST_THREAD_USES_DATETIME - inline bool timed_lock(system_time const& abs_time); - template<typename TimeDuration> - bool timed_lock(TimeDuration const & relative_time) - { - return timed_lock(get_system_time()+relative_time); - } - inline bool timed_lock_shared(system_time const& abs_time); - template<typename TimeDuration> - bool timed_lock_shared(TimeDuration const & relative_time) - { - return timed_lock_shared(get_system_time()+relative_time); - } - inline bool timed_lock_upgrade(system_time const& abs_time); - template<typename TimeDuration> - bool timed_lock_upgrade(TimeDuration const & relative_time) - { - return timed_lock_upgrade(get_system_time()+relative_time); - } #endif - + void unlock_and_lock_upgrade(); }; - template <class Clock, class Duration> - bool - upgrade_mutex::try_lock_until( - const boost::chrono::time_point<Clock, Duration>& abs_time) + inline upgrade_mutex::upgrade_mutex() + : gate1_(), + gate2_(), + state_(0) { - boost::unique_lock<mutex_t> lk(mut_); - if (state_ & (write_entered_ | upgradable_entered_)) - { - for (;;) - { - boost::cv_status status = gate1_.wait_until(lk, abs_time); - if ((state_ & (write_entered_ | upgradable_entered_)) == 0) - break; - if (status == boost::cv_status::timeout) - return false; - } - } - state_ |= write_entered_; - if (state_ & n_readers_) - { - for (;;) - { - boost::cv_status status = gate2_.wait_until(lk, abs_time); - if ((state_ & n_readers_) == 0) - break; - if (status == boost::cv_status::timeout) - { - state_ &= ~write_entered_; - return false; - } - } - } - return true; } - template <class Clock, class Duration> - bool - upgrade_mutex::try_lock_shared_until( - const boost::chrono::time_point<Clock, Duration>& abs_time) + inline upgrade_mutex::~upgrade_mutex() { - boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) - { - for (;;) - { - boost::cv_status status = gate1_.wait_until(lk, abs_time); - if ((state_ & write_entered_) == 0 && - (state_ & n_readers_) < n_readers_) - break; - if (status == boost::cv_status::timeout) - return false; - } - } - count_t num_readers = (state_ & n_readers_) + 1; - state_ &= ~n_readers_; - state_ |= num_readers; - return true; + boost::lock_guard<mutex_t> _(mut_); } - template <class Clock, class Duration> - bool - upgrade_mutex::try_lock_upgrade_until( - const boost::chrono::time_point<Clock, Duration>& abs_time) + // Exclusive ownership + + inline void upgrade_mutex::lock() { boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & (write_entered_ | upgradable_entered_)) || - (state_ & n_readers_) == n_readers_) - { - for (;;) - { - boost::cv_status status = gate1_.wait_until(lk, abs_time); - if ((state_ & (write_entered_ | upgradable_entered_)) == 0 && - (state_ & n_readers_) < n_readers_) - break; - if (status == boost::cv_status::timeout) - return false; - } - } - count_t num_readers = (state_ & n_readers_) + 1; - state_ &= ~n_readers_; - state_ |= upgradable_entered_ | num_readers; - return true; + gate1_.wait(lk, boost::bind(&upgrade_mutex::no_writer_no_upgrader, boost::ref(*this))); + state_ |= write_entered_; + gate2_.wait(lk, boost::bind(&upgrade_mutex::no_readers, boost::ref(*this))); } -#if defined BOOST_THREAD_USES_DATETIME - bool upgrade_mutex::timed_lock(system_time const& abs_time) - { - boost::unique_lock<mutex_t> lk(mut_); - if (state_ & (write_entered_ | upgradable_entered_)) - { - for (;;) - { - bool status = gate1_.timed_wait(lk, abs_time); - if ((state_ & (write_entered_ | upgradable_entered_)) == 0) - break; - if (!status) - return false; - } - } - state_ |= write_entered_; - if (state_ & n_readers_) - { - for (;;) - { - bool status = gate2_.timed_wait(lk, abs_time); - if ((state_ & n_readers_) == 0) - break; - if (!status) - { - state_ &= ~write_entered_; - return false; - } - } - } - return true; - } - bool upgrade_mutex::timed_lock_shared(system_time const& abs_time) - { - boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) - { - for (;;) - { - bool status = gate1_.timed_wait(lk, abs_time); - if ((state_ & write_entered_) == 0 && - (state_ & n_readers_) < n_readers_) - break; - if (!status) - return false; - } - } - count_t num_readers = (state_ & n_readers_) + 1; - state_ &= ~n_readers_; - state_ |= num_readers; - return true; - } - bool upgrade_mutex::timed_lock_upgrade(system_time const& abs_time) - { - boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & (write_entered_ | upgradable_entered_)) || - (state_ & n_readers_) == n_readers_) - { - for (;;) - { - bool status = gate1_.timed_wait(lk, abs_time); - if ((state_ & (write_entered_ | upgradable_entered_)) == 0 && - (state_ & n_readers_) < n_readers_) - break; - if (!status) - return false; - } - } - count_t num_readers = (state_ & n_readers_) + 1; - state_ &= ~n_readers_; - state_ |= upgradable_entered_ | num_readers; - return true; - } - -#endif - template <class Clock, class Duration> - bool - upgrade_mutex::try_unlock_shared_and_lock_until( - const boost::chrono::time_point<Clock, Duration>& abs_time) + inline bool upgrade_mutex::try_lock() { boost::unique_lock<mutex_t> lk(mut_); - if (state_ != 1) + if (!no_writer_no_upgrader_no_readers()) { - for (;;) - { - boost::cv_status status = gate2_.wait_until(lk, abs_time); - if (state_ == 1) - break; - if (status == boost::cv_status::timeout) - return false; - } + return false; } state_ = write_entered_; return true; } +#ifdef BOOST_THREAD_USES_CHRONO template <class Clock, class Duration> - bool - upgrade_mutex::try_unlock_shared_and_lock_upgrade_until( + bool upgrade_mutex::try_lock_until( const boost::chrono::time_point<Clock, Duration>& abs_time) { boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & (write_entered_ | upgradable_entered_)) != 0) + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_writer_no_upgrader, boost::ref(*this)))) { - for (;;) - { - boost::cv_status status = gate2_.wait_until(lk, abs_time); - if ((state_ & (write_entered_ | upgradable_entered_)) == 0) - break; - if (status == boost::cv_status::timeout) - return false; - } + return false; + } + state_ |= write_entered_; + if (!gate2_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_readers, boost::ref(*this)))) + { + state_ &= ~write_entered_; + return false; } - state_ |= upgradable_entered_; return true; } +#endif - template <class Clock, class Duration> - bool - upgrade_mutex::try_unlock_upgrade_and_lock_until( - const boost::chrono::time_point<Clock, Duration>& abs_time) +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool upgrade_mutex::timed_lock(T const & abs_or_rel_time) { boost::unique_lock<mutex_t> lk(mut_); - if ((state_ & n_readers_) != 1) + if (!gate1_.timed_wait(lk, abs_or_rel_time, boost::bind( + &upgrade_mutex::no_writer_no_upgrader, boost::ref(*this)))) { - for (;;) - { - boost::cv_status status = gate2_.wait_until(lk, abs_time); - if ((state_ & n_readers_) == 1) - break; - if (status == boost::cv_status::timeout) - return false; - } + return false; + } + state_ |= write_entered_; + if (!gate2_.timed_wait(lk, abs_or_rel_time, boost::bind( + &upgrade_mutex::no_readers, boost::ref(*this)))) + { + state_ &= ~write_entered_; + return false; } - state_ = write_entered_; return true; } +#endif - ////// - // shared_mutex - - shared_mutex::shared_mutex() - : state_(0) - { - } - - shared_mutex::~shared_mutex() + inline void upgrade_mutex::unlock() { boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(one_writer()); + BOOST_ASSERT(no_upgrader()); + BOOST_ASSERT(no_readers()); + state_ = 0; + // notify all since multiple *lock_shared*() calls and a *lock_upgrade*() + // call may be able to proceed in response to this notification + gate1_.notify_all(); } - // Exclusive ownership + // Shared ownership - void - shared_mutex::lock() + inline void upgrade_mutex::lock_shared() { boost::unique_lock<mutex_t> lk(mut_); - while (state_ & write_entered_) - gate1_.wait(lk); - state_ |= write_entered_; - while (state_ & n_readers_) - gate2_.wait(lk); + gate1_.wait(lk, boost::bind(&upgrade_mutex::no_writer_no_max_readers, boost::ref(*this))); + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= num_readers; } - bool - shared_mutex::try_lock() + inline bool upgrade_mutex::try_lock_shared() { boost::unique_lock<mutex_t> lk(mut_); - if (state_ == 0) + if (!no_writer_no_max_readers()) { - state_ = write_entered_; - return true; + return false; } - return false; - } - - void - shared_mutex::unlock() - { - boost::lock_guard<mutex_t> _(mut_); - state_ = 0; - gate1_.notify_all(); + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= num_readers; + return true; } - // Shared ownership - - void - shared_mutex::lock_shared() +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + bool upgrade_mutex::try_lock_shared_until( + const boost::chrono::time_point<Clock, Duration>& abs_time) { boost::unique_lock<mutex_t> lk(mut_); - while ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) - gate1_.wait(lk); + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_writer_no_max_readers, boost::ref(*this)))) + { + return false; + } count_t num_readers = (state_ & n_readers_) + 1; state_ &= ~n_readers_; state_ |= num_readers; + return true; } +#endif - bool - shared_mutex::try_lock_shared() +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool upgrade_mutex::timed_lock_shared(T const & abs_or_rel_time) { boost::unique_lock<mutex_t> lk(mut_); - count_t num_readers = state_ & n_readers_; - if (!(state_ & write_entered_) && num_readers != n_readers_) + if (!gate1_.timed_wait(lk, abs_or_rel_time, boost::bind( + &upgrade_mutex::no_writer_no_max_readers, boost::ref(*this)))) { - ++num_readers; - state_ &= ~n_readers_; - state_ |= num_readers; - return true; + return false; } - return false; + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= num_readers; + return true; } +#endif - void - shared_mutex::unlock_shared() + inline void upgrade_mutex::unlock_shared() { boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(one_or_more_readers()); count_t num_readers = (state_ & n_readers_) - 1; state_ &= ~n_readers_; state_ |= num_readers; - if (state_ & write_entered_) + if (no_writer()) { - if (num_readers == 0) - gate2_.notify_one(); + if (num_readers == n_readers_ - 1) + gate1_.notify_one(); } else { - if (num_readers == n_readers_ - 1) - gate1_.notify_one(); + if (num_readers == 0) + gate2_.notify_one(); } } - // upgrade_mutex - - upgrade_mutex::upgrade_mutex() - : gate1_(), - gate2_(), - state_(0) - { - } - - upgrade_mutex::~upgrade_mutex() - { - boost::lock_guard<mutex_t> _(mut_); - } - - // Exclusive ownership + // Upgrade ownership - void - upgrade_mutex::lock() + inline void upgrade_mutex::lock_upgrade() { boost::unique_lock<mutex_t> lk(mut_); - while (state_ & (write_entered_ | upgradable_entered_)) - gate1_.wait(lk); - state_ |= write_entered_; - while (state_ & n_readers_) - gate2_.wait(lk); + gate1_.wait(lk, boost::bind(&upgrade_mutex::no_writer_no_upgrader_no_max_readers, boost::ref(*this))); + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= upgradable_entered_ | num_readers; } - bool - upgrade_mutex::try_lock() + inline bool upgrade_mutex::try_lock_upgrade() { boost::unique_lock<mutex_t> lk(mut_); - if (state_ == 0) + if (!no_writer_no_upgrader_no_max_readers()) { - state_ = write_entered_; - return true; + return false; } - return false; - } - - void - upgrade_mutex::unlock() - { - boost::lock_guard<mutex_t> _(mut_); - state_ = 0; - gate1_.notify_all(); + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= upgradable_entered_ | num_readers; + return true; } - // Shared ownership - - void - upgrade_mutex::lock_shared() +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + bool upgrade_mutex::try_lock_upgrade_until( + const boost::chrono::time_point<Clock, Duration>& abs_time) { boost::unique_lock<mutex_t> lk(mut_); - while ((state_ & write_entered_) || (state_ & n_readers_) == n_readers_) - gate1_.wait(lk); + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_writer_no_upgrader_no_max_readers, boost::ref(*this)))) + { + return false; + } count_t num_readers = (state_ & n_readers_) + 1; state_ &= ~n_readers_; - state_ |= num_readers; + state_ |= upgradable_entered_ | num_readers; + return true; } +#endif - bool - upgrade_mutex::try_lock_shared() +#if defined BOOST_THREAD_USES_DATETIME + template<typename T> + bool upgrade_mutex::timed_lock_upgrade(T const & abs_or_rel_time) { boost::unique_lock<mutex_t> lk(mut_); - count_t num_readers = state_ & n_readers_; - if (!(state_ & write_entered_) && num_readers != n_readers_) + if (!gate1_.timed_wait(lk, abs_or_rel_time, boost::bind( + &upgrade_mutex::no_writer_no_upgrader_no_max_readers, boost::ref(*this)))) { - ++num_readers; - state_ &= ~n_readers_; - state_ |= num_readers; - return true; + return false; } - return false; + count_t num_readers = (state_ & n_readers_) + 1; + state_ &= ~n_readers_; + state_ |= upgradable_entered_ | num_readers; + return true; } +#endif - void - upgrade_mutex::unlock_shared() + inline void upgrade_mutex::unlock_upgrade() { boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(no_writer()); + BOOST_ASSERT(one_upgrader()); + BOOST_ASSERT(one_or_more_readers()); count_t num_readers = (state_ & n_readers_) - 1; - state_ &= ~n_readers_; + state_ &= ~(upgradable_entered_ | n_readers_); state_ |= num_readers; - if (state_ & write_entered_) - { - if (num_readers == 0) - gate2_.notify_one(); - } - else - { - if (num_readers == n_readers_ - 1) - gate1_.notify_one(); - } + // notify all since both a *lock*() and a *lock_shared*() call + // may be able to proceed in response to this notification + gate1_.notify_all(); } - // Upgrade ownership + // Shared <-> Exclusive - void - upgrade_mutex::lock_upgrade() +#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS + inline bool upgrade_mutex::try_unlock_shared_and_lock() { boost::unique_lock<mutex_t> lk(mut_); - while ((state_ & (write_entered_ | upgradable_entered_)) || - (state_ & n_readers_) == n_readers_) - gate1_.wait(lk); - count_t num_readers = (state_ & n_readers_) + 1; - state_ &= ~n_readers_; - state_ |= upgradable_entered_ | num_readers; + BOOST_ASSERT(one_or_more_readers()); + if (!no_writer_no_upgrader_one_reader()) + { + return false; + } + state_ = write_entered_; + return true; } - bool - upgrade_mutex::try_lock_upgrade() +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + bool upgrade_mutex::try_unlock_shared_and_lock_until( + const boost::chrono::time_point<Clock, Duration>& abs_time) { boost::unique_lock<mutex_t> lk(mut_); - count_t num_readers = state_ & n_readers_; - if (!(state_ & (write_entered_ | upgradable_entered_)) - && num_readers != n_readers_) + BOOST_ASSERT(one_or_more_readers()); + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_writer_no_upgrader, boost::ref(*this)))) + { + return false; + } + count_t num_readers = (state_ & n_readers_) - 1; + state_ &= ~n_readers_; + state_ |= (write_entered_ | num_readers); + if (!gate2_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_readers, boost::ref(*this)))) { ++num_readers; - state_ &= ~n_readers_; - state_ |= upgradable_entered_ | num_readers; - return true; + state_ &= ~(write_entered_ | n_readers_); + state_ |= num_readers; + return false; } - return false; + return true; } +#endif +#endif - void - upgrade_mutex::unlock_upgrade() + inline void upgrade_mutex::unlock_and_lock_shared() { - { - boost::lock_guard<mutex_t> _(mut_); - count_t num_readers = (state_ & n_readers_) - 1; - state_ &= ~(upgradable_entered_ | n_readers_); - state_ |= num_readers; - } + boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(one_writer()); + BOOST_ASSERT(no_upgrader()); + BOOST_ASSERT(no_readers()); + state_ = 1; + // notify all since multiple *lock_shared*() calls and a *lock_upgrade*() + // call may be able to proceed in response to this notification gate1_.notify_all(); } - // Shared <-> Exclusive + // Shared <-> Upgrade - bool - upgrade_mutex::try_unlock_shared_and_lock() +#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS + inline bool upgrade_mutex::try_unlock_shared_and_lock_upgrade() { boost::unique_lock<mutex_t> lk(mut_); - if (state_ == 1) - { - state_ = write_entered_; - return true; - } - return false; - } - - void - upgrade_mutex::unlock_and_lock_shared() - { + BOOST_ASSERT(one_or_more_readers()); + if (!no_writer_no_upgrader()) { - boost::lock_guard<mutex_t> _(mut_); - state_ = 1; + return false; } - gate1_.notify_all(); + state_ |= upgradable_entered_; + return true; } - // Shared <-> Upgrade - - bool - upgrade_mutex::try_unlock_shared_and_lock_upgrade() +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + bool upgrade_mutex::try_unlock_shared_and_lock_upgrade_until( + const boost::chrono::time_point<Clock, Duration>& abs_time) { boost::unique_lock<mutex_t> lk(mut_); - if (!(state_ & (write_entered_ | upgradable_entered_))) + BOOST_ASSERT(one_or_more_readers()); + if (!gate1_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_writer_no_upgrader, boost::ref(*this)))) { - state_ |= upgradable_entered_; - return true; + return false; } - return false; + state_ |= upgradable_entered_; + return true; } +#endif +#endif - void - upgrade_mutex::unlock_upgrade_and_lock_shared() + inline void upgrade_mutex::unlock_upgrade_and_lock_shared() { - { - boost::lock_guard<mutex_t> _(mut_); - state_ &= ~upgradable_entered_; - } + boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(no_writer()); + BOOST_ASSERT(one_upgrader()); + BOOST_ASSERT(one_or_more_readers()); + state_ &= ~upgradable_entered_; + // notify all since only one *lock*() or *lock_upgrade*() call can win and + // proceed in response to this notification, but a *lock_shared*() call may + // also be waiting and could steal the notification gate1_.notify_all(); } // Upgrade <-> Exclusive - void - upgrade_mutex::unlock_upgrade_and_lock() + inline void upgrade_mutex::unlock_upgrade_and_lock() { boost::unique_lock<mutex_t> lk(mut_); + BOOST_ASSERT(no_writer()); + BOOST_ASSERT(one_upgrader()); + BOOST_ASSERT(one_or_more_readers()); count_t num_readers = (state_ & n_readers_) - 1; state_ &= ~(upgradable_entered_ | n_readers_); state_ |= write_entered_ | num_readers; - while (state_ & n_readers_) - gate2_.wait(lk); + gate2_.wait(lk, boost::bind(&upgrade_mutex::no_readers, boost::ref(*this))); } - bool - upgrade_mutex::try_unlock_upgrade_and_lock() + inline bool upgrade_mutex::try_unlock_upgrade_and_lock() { boost::unique_lock<mutex_t> lk(mut_); - if (state_ == (upgradable_entered_ | 1)) + BOOST_ASSERT(no_writer()); + BOOST_ASSERT(one_upgrader()); + BOOST_ASSERT(one_or_more_readers()); + if (!one_reader()) { - state_ = write_entered_; - return true; + return false; } - return false; + state_ = write_entered_; + return true; } - void - upgrade_mutex::unlock_and_lock_upgrade() +#ifdef BOOST_THREAD_USES_CHRONO + template <class Clock, class Duration> + bool upgrade_mutex::try_unlock_upgrade_and_lock_until( + const boost::chrono::time_point<Clock, Duration>& abs_time) { + boost::unique_lock<mutex_t> lk(mut_); + BOOST_ASSERT(no_writer()); + BOOST_ASSERT(one_upgrader()); + BOOST_ASSERT(one_or_more_readers()); + count_t num_readers = (state_ & n_readers_) - 1; + state_ &= ~(upgradable_entered_ | n_readers_); + state_ |= (write_entered_ | num_readers); + if (!gate2_.wait_until(lk, abs_time, boost::bind( + &upgrade_mutex::no_readers, boost::ref(*this)))) { - boost::lock_guard<mutex_t> _(mut_); - state_ = upgradable_entered_ | 1; + ++num_readers; + state_ &= ~(write_entered_ | n_readers_); + state_ |= (upgradable_entered_ | num_readers); + return false; } + return true; + } +#endif + + inline void upgrade_mutex::unlock_and_lock_upgrade() + { + boost::lock_guard<mutex_t> _(mut_); + BOOST_ASSERT(one_writer()); + BOOST_ASSERT(no_upgrader()); + BOOST_ASSERT(no_readers()); + state_ = upgradable_entered_ | 1; + // notify all since multiple *lock_shared*() calls may be able + // to proceed in response to this notification gate1_.notify_all(); } diff --git a/boost/thread/v2/thread.hpp b/boost/thread/v2/thread.hpp deleted file mode 100644 index 53560610e2..0000000000 --- a/boost/thread/v2/thread.hpp +++ /dev/null @@ -1,155 +0,0 @@ -// Distributed under the Boost Software License, Version 1.0. (See -// accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -// (C) Copyright 2011 Vicente J. Botet Escriba - -#ifndef BOOST_THREAD_V2_THREAD_HPP -#define BOOST_THREAD_V2_THREAD_HPP - -#include <boost/thread/detail/config.hpp> -#ifdef BOOST_THREAD_USES_CHRONO -#include <boost/chrono/system_clocks.hpp> -#include <boost/chrono/ceil.hpp> -#endif -#include <boost/thread/condition_variable.hpp> -#include <boost/thread/lock_types.hpp> - -namespace boost -{ - namespace this_thread - { - namespace no_interruption_point - { -#ifdef BOOST_THREAD_USES_CHRONO - - template <class Clock, class Duration> - void sleep_until(const chrono::time_point<Clock, Duration>& t) - { - using namespace chrono; - mutex mut; - condition_variable cv; - unique_lock<mutex> lk(mut); - while (Clock::now() < t) - cv.wait_until(lk, t); - } - -#ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY - - template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - if (d > duration<Rep, Period>::zero()) - { - duration<long double> Max = nanoseconds::max BOOST_PREVENT_MACRO_SUBSTITUTION (); - nanoseconds ns; - if (d < Max) - { - ns = duration_cast<nanoseconds>(d); - if (ns < d) - ++ns; - } - else - ns = nanoseconds:: max BOOST_PREVENT_MACRO_SUBSTITUTION (); - sleep_for(ns); - } - } - - template <class Duration> - inline BOOST_SYMBOL_VISIBLE - void sleep_until(const chrono::time_point<chrono::steady_clock, Duration>& t) - { - using namespace chrono; - sleep_for(t - steady_clock::now()); - } -#else - template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - if (d > duration<Rep, Period>::zero()) - { - steady_clock::time_point c_timeout = steady_clock::now() + ceil<nanoseconds>(d); - sleep_until(c_timeout); - } - } - -#endif - -#endif - } -#ifdef BOOST_THREAD_USES_CHRONO - - template <class Clock, class Duration> - void sleep_until(const chrono::time_point<Clock, Duration>& t) - { - using namespace chrono; - mutex mut; - condition_variable cv; - unique_lock<mutex> lk(mut); - while (Clock::now() < t) - cv.wait_until(lk, t); - } - -#if defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC && defined BOOST_CHRONO_HAS_CLOCK_STEADY - template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - if (d > duration<Rep, Period>::zero()) - { - steady_clock::time_point c_timeout = steady_clock::now() + ceil<nanoseconds>(d); - sleep_until(c_timeout); - } - } - -#elif defined BOOST_THREAD_SLEEP_FOR_IS_STEADY - - template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - if (d > duration<Rep, Period>::zero()) - { - duration<long double> Max = nanoseconds::max BOOST_PREVENT_MACRO_SUBSTITUTION (); - nanoseconds ns; - if (d < Max) - { - ns = duration_cast<nanoseconds>(d); - if (ns < d) - ++ns; - } - else - ns = nanoseconds:: max BOOST_PREVENT_MACRO_SUBSTITUTION (); - sleep_for(ns); - } - } - - template <class Duration> - inline BOOST_SYMBOL_VISIBLE - void sleep_until(const chrono::time_point<chrono::steady_clock, Duration>& t) - { - using namespace chrono; - sleep_for(t - steady_clock::now()); - } -#else - template <class Rep, class Period> - void sleep_for(const chrono::duration<Rep, Period>& d) - { - using namespace chrono; - if (d > duration<Rep, Period>::zero()) - { - //system_clock::time_point c_timeout = time_point_cast<system_clock::duration>(system_clock::now() + ceil<nanoseconds>(d)); - system_clock::time_point c_timeout = system_clock::now() + ceil<system_clock::duration>(d); - sleep_until(c_timeout); - } - } - -#endif - -#endif - } -} - - -#endif diff --git a/boost/thread/win32/basic_recursive_mutex.hpp b/boost/thread/win32/basic_recursive_mutex.hpp index 351f9acc71..6913c5bf62 100644 --- a/boost/thread/win32/basic_recursive_mutex.hpp +++ b/boost/thread/win32/basic_recursive_mutex.hpp @@ -4,7 +4,7 @@ // basic_recursive_mutex.hpp // // (C) Copyright 2006-8 Anthony Williams -// (C) Copyright 2011-2012 Vicente J. Botet Escriba +// (C) Copyright 2011-2012,2017-2018 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -44,13 +44,13 @@ namespace boost bool try_lock() BOOST_NOEXCEPT { - long const current_thread_id=boost::detail::winapi::GetCurrentThreadId(); + long const current_thread_id=boost::winapi::GetCurrentThreadId(); return try_recursive_lock(current_thread_id) || try_basic_lock(current_thread_id); } void lock() { - long const current_thread_id=boost::detail::winapi::GetCurrentThreadId(); + long const current_thread_id=boost::winapi::GetCurrentThreadId(); if(!try_recursive_lock(current_thread_id)) { mutex.lock(); @@ -61,29 +61,30 @@ namespace boost #if defined BOOST_THREAD_USES_DATETIME bool timed_lock(::boost::system_time const& target) { - long const current_thread_id=boost::detail::winapi::GetCurrentThreadId(); + long const current_thread_id=boost::winapi::GetCurrentThreadId(); return try_recursive_lock(current_thread_id) || try_timed_lock(current_thread_id,target); } template<typename Duration> - bool timed_lock(Duration const& timeout) + bool timed_lock(Duration const& target) { - return timed_lock(get_system_time()+timeout); + long const current_thread_id=boost::detail::winapi::GetCurrentThreadId(); + return try_recursive_lock(current_thread_id) || try_timed_lock(current_thread_id,target); } #endif #ifdef BOOST_THREAD_USES_CHRONO - template <class Rep, class Period> - bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) - { + template <class Rep, class Period> + bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) + { long const current_thread_id=boost::detail::winapi::GetCurrentThreadId(); return try_recursive_lock(current_thread_id) || try_timed_lock_for(current_thread_id,rel_time); - } - template <class Clock, class Duration> - bool try_lock_until(const chrono::time_point<Clock, Duration>& t) - { + } + template <class Clock, class Duration> + bool try_lock_until(const chrono::time_point<Clock, Duration>& t) + { long const current_thread_id=boost::detail::winapi::GetCurrentThreadId(); return try_recursive_lock(current_thread_id) || try_timed_lock_until(current_thread_id,t); - } + } #endif void unlock() { @@ -127,6 +128,17 @@ namespace boost } return false; } + template<typename Duration> + bool try_timed_lock(long current_thread_id,Duration const& target) + { + if(mutex.timed_lock(target)) + { + BOOST_INTERLOCKED_EXCHANGE(&locking_thread_id,current_thread_id); + recursion_count=1; + return true; + } + return false; + } #endif template <typename TP> bool try_timed_lock_until(long current_thread_id,TP const& target) diff --git a/boost/thread/win32/basic_timed_mutex.hpp b/boost/thread/win32/basic_timed_mutex.hpp index b579d50530..b332dab752 100644 --- a/boost/thread/win32/basic_timed_mutex.hpp +++ b/boost/thread/win32/basic_timed_mutex.hpp @@ -22,6 +22,8 @@ #include <boost/chrono/system_clocks.hpp> #include <boost/chrono/ceil.hpp> #endif +#include <boost/thread/detail/platform_time.hpp> + #include <boost/config/abi_prefix.hpp> namespace boost @@ -59,7 +61,7 @@ namespace boost } } - + // Take the lock flag if it's available bool try_lock() BOOST_NOEXCEPT { return !win32::interlocked_bit_test_and_set(&active_count,lock_flag_bit); @@ -76,21 +78,21 @@ namespace boost if(old_count&lock_flag_value) { - bool lock_acquired=false; void* const sem=get_event(); do { - unsigned const retval(winapi::WaitForSingleObjectEx(sem, ::boost::detail::win32::infinite,0)); - BOOST_VERIFY(0 == retval || ::boost::detail::win32::wait_abandoned == retval); -// BOOST_VERIFY(winapi::WaitForSingleObject( -// sem,::boost::detail::win32::infinite)==0); - clear_waiting_and_try_lock(old_count); - lock_acquired=!(old_count&lock_flag_value); + if(winapi::WaitForSingleObjectEx(sem,::boost::detail::win32::infinite,0)==0) + { + clear_waiting_and_try_lock(old_count); + } } - while(!lock_acquired); + while(old_count&lock_flag_value); } } + + // Loop until the number of waiters has been incremented or we've taken the lock flag + // The loop is necessary since this function may be called by multiple threads simultaneously void mark_waiting_and_try_lock(long& old_count) { for(;;) @@ -102,12 +104,19 @@ namespace boost { if(was_locked) old_count=new_count; + // else we've taken the lock flag + // don't update old_count so that the calling function can see that + // the old lock flag was 0 and know that we've taken the lock flag break; } old_count=current; } } + // Loop until someone else has taken the lock flag and cleared the event set flag or + // until we've taken the lock flag and cleared the event set flag and decremented the + // number of waiters + // The loop is necessary since this function may be called by multiple threads simultaneously void clear_waiting_and_try_lock(long& old_count) { old_count&=~lock_flag_value; @@ -118,117 +127,124 @@ namespace boost long const current=BOOST_INTERLOCKED_COMPARE_EXCHANGE(&active_count,new_count,old_count); if(current==old_count) { + // if someone else has taken the lock flag + // no need to update old_count since old_count == new_count (ignoring + // event_set_flag_value which the calling function doesn't care about) + // else we've taken the lock flag + // don't update old_count so that the calling function can see that + // the old lock flag was 0 and know that we've taken the lock flag break; } old_count=current; } } + private: + unsigned long getMs(detail::platform_duration const& d) + { + return static_cast<unsigned long>(d.getMs()); + } + + template <typename Duration> + unsigned long getMs(Duration const& d) + { + return static_cast<unsigned long>(chrono::ceil<chrono::milliseconds>(d).count()); + } -#if defined BOOST_THREAD_USES_DATETIME - bool timed_lock(::boost::system_time const& wait_until) + template <typename Clock, typename Timepoint, typename Duration> + bool do_lock_until(Timepoint const& t, Duration const& max) { if(try_lock()) { return true; } + long old_count=active_count; mark_waiting_and_try_lock(old_count); if(old_count&lock_flag_value) { - bool lock_acquired=false; void* const sem=get_event(); + // If the clock is the system clock, it may jump while this function + // is waiting. To compensate for this and time out near the correct + // time, we call WaitForSingleObjectEx() in a loop with a short + // timeout and recheck the time remaining each time through the loop. do { - if(winapi::WaitForSingleObjectEx(sem,::boost::detail::get_milliseconds_until(wait_until),0)!=0) + Duration d(t - Clock::now()); + if(d <= Duration::zero()) // timeout occurred { BOOST_INTERLOCKED_DECREMENT(&active_count); return false; } - clear_waiting_and_try_lock(old_count); - lock_acquired=!(old_count&lock_flag_value); + if(max != Duration::zero()) + { + d = (std::min)(d, max); + } + if(winapi::WaitForSingleObjectEx(sem,getMs(d),0)==0) + { + clear_waiting_and_try_lock(old_count); + } } - while(!lock_acquired); + while(old_count&lock_flag_value); } return true; } + public: + +#if defined BOOST_THREAD_USES_DATETIME + bool timed_lock(::boost::system_time const& wait_until) + { + const detail::real_platform_timepoint t(wait_until); + return do_lock_until<detail::real_platform_clock>(t, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } template<typename Duration> bool timed_lock(Duration const& timeout) { - return timed_lock(get_system_time()+timeout); + const detail::mono_platform_timepoint t(detail::mono_platform_clock::now() + detail::platform_duration(timeout)); + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_until<detail::mono_platform_clock>(t, detail::platform_duration::zero()); } bool timed_lock(boost::xtime const& timeout) { - return timed_lock(system_time(timeout)); + return timed_lock(boost::system_time(timeout)); } #endif #ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) { - return try_lock_until(chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool try_lock_until(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(); - return try_lock_until(s_now + ceil<system_clock::duration>(t - c_now)); + const chrono::steady_clock::time_point t(chrono::steady_clock::now() + rel_time); + typedef typename chrono::duration<Rep, Period> Duration; + typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_until<chrono::steady_clock>(t, common_duration::zero()); } template <class Duration> - bool try_lock_until(const chrono::time_point<chrono::system_clock, Duration>& t) + bool try_lock_until(const chrono::time_point<chrono::steady_clock, Duration>& t) { - using namespace chrono; - typedef time_point<chrono::system_clock, chrono::system_clock::duration> sys_tmpt; - return try_lock_until(sys_tmpt(chrono::ceil<chrono::system_clock::duration>(t.time_since_epoch()))); + typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_until<chrono::steady_clock>(t, common_duration::zero()); } - bool try_lock_until(const chrono::time_point<chrono::system_clock, chrono::system_clock::duration>& tp) + template <class Clock, class Duration> + bool try_lock_until(const chrono::time_point<Clock, Duration>& t) { - if(try_lock()) - { - return true; - } - long old_count=active_count; - mark_waiting_and_try_lock(old_count); - - if(old_count&lock_flag_value) - { - bool lock_acquired=false; - void* const sem=get_event(); - - do - { - chrono::time_point<chrono::system_clock, chrono::system_clock::duration> now = chrono::system_clock::now(); - if (tp<=now) { - BOOST_INTERLOCKED_DECREMENT(&active_count); - return false; - } - chrono::milliseconds rel_time= chrono::ceil<chrono::milliseconds>(tp-now); - - if(winapi::WaitForSingleObjectEx(sem,static_cast<unsigned long>(rel_time.count()),0)!=0) - { - BOOST_INTERLOCKED_DECREMENT(&active_count); - return false; - } - clear_waiting_and_try_lock(old_count); - lock_acquired=!(old_count&lock_flag_value); - } - while(!lock_acquired); - } - return true; + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + return do_lock_until<Clock>(t, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); } #endif void unlock() { - long const offset=lock_flag_value; + // Clear the lock flag using atomic addition (works since long is always 32 bits on Windows) long const old_count=BOOST_INTERLOCKED_EXCHANGE_ADD(&active_count,lock_flag_value); - if(!(old_count&event_set_flag_value) && (old_count>offset)) + // If someone is waiting to take the lock, set the event set flag and, if + // the event set flag hadn't already been set, send an event. + if(!(old_count&event_set_flag_value) && (old_count>lock_flag_value)) { if(!win32::interlocked_bit_test_and_set(&active_count,event_set_flag_bit)) { @@ -238,6 +254,8 @@ namespace boost } private: + // Create an event in a thread-safe way + // The first thread to create the event wins and all other thread will use that event void* get_event() { void* current_event=::boost::detail::interlocked_read_acquire(&event); 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> diff --git a/boost/thread/win32/once.hpp b/boost/thread/win32/once.hpp index e7c565fbfa..3c515bae7d 100644 --- a/boost/thread/win32/once.hpp +++ b/boost/thread/win32/once.hpp @@ -136,9 +136,9 @@ namespace boost } #ifdef BOOST_NO_ANSI_APIS - return ::boost::detail::winapi::OpenEventW( + return ::boost::winapi::OpenEventW( #else - return ::boost::detail::winapi::OpenEventA( + return ::boost::winapi::OpenEventA( #endif ::boost::detail::win32::synchronize | ::boost::detail::win32::event_modify_state, @@ -186,7 +186,7 @@ namespace boost } if(ctx.event_handle) { - ::boost::detail::winapi::ResetEvent(ctx.event_handle); + ::boost::winapi::ResetEvent(ctx.event_handle); } return true; } @@ -207,7 +207,7 @@ namespace boost } if(ctx.event_handle) { - ::boost::detail::winapi::SetEvent(ctx.event_handle); + ::boost::winapi::SetEvent(ctx.event_handle); } } inline void rollback_once_region(once_flag& flag, once_context& ctx) BOOST_NOEXCEPT @@ -219,13 +219,13 @@ namespace boost } if(ctx.event_handle) { - ::boost::detail::winapi::SetEvent(ctx.event_handle); + ::boost::winapi::SetEvent(ctx.event_handle); } } } #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) -//#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR) +//#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR) inline void call_once(once_flag& flag, void (*f)()) { // Try for a quick win: if the procedure has already been called @@ -264,7 +264,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite, 0)); } } @@ -308,7 +308,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -355,7 +355,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -400,7 +400,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -443,7 +443,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -486,7 +486,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -529,7 +529,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -574,7 +574,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -617,7 +617,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -660,7 +660,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -703,13 +703,13 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } #endif #if 1 -#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNTION_PTR) +#if defined(BOOST_THREAD_RVALUE_REFERENCES_DONT_MATCH_FUNCTION_PTR) inline void call_once(once_flag& flag, void (*f)()) { // Try for a quick win: if the procedure has already been called @@ -748,7 +748,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -793,7 +793,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -839,7 +839,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -886,7 +886,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -930,7 +930,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -977,7 +977,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -1024,7 +1024,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } @@ -1073,7 +1073,7 @@ namespace boost continue; } } - BOOST_VERIFY(!::boost::detail::winapi::WaitForSingleObjectEx( + BOOST_VERIFY(!::boost::winapi::WaitForSingleObjectEx( ctx.event_handle,::boost::detail::win32::infinite,0)); } } diff --git a/boost/thread/win32/shared_mutex.hpp b/boost/thread/win32/shared_mutex.hpp index d1bd971770..76ee2579b3 100644 --- a/boost/thread/win32/shared_mutex.hpp +++ b/boost/thread/win32/shared_mutex.hpp @@ -2,7 +2,7 @@ #define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP // (C) Copyright 2006-8 Anthony Williams -// (C) Copyright 2011-2012 Vicente J. Botet Escriba +// (C) Copyright 2011-2012,2017-2018 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -19,6 +19,7 @@ #include <boost/chrono/ceil.hpp> #endif #include <boost/thread/detail/delete.hpp> +#include <boost/thread/detail/platform_time.hpp> #include <boost/config/abi_prefix.hpp> @@ -29,7 +30,7 @@ namespace boost private: struct state_data { - unsigned shared_count:11, + unsigned long shared_count:11, shared_waiting:11, exclusive:1, upgrade:1, @@ -38,19 +39,16 @@ namespace boost friend bool operator==(state_data const& lhs,state_data const& rhs) { - return *reinterpret_cast<unsigned const*>(&lhs)==*reinterpret_cast<unsigned const*>(&rhs); + return *reinterpret_cast<unsigned long const*>(&lhs)==*reinterpret_cast<unsigned long const*>(&rhs); } }; - - template<typename T> - T interlocked_compare_exchange(T* target,T new_value,T comparand) + state_data interlocked_compare_exchange(state_data* target, state_data new_value, state_data comparand) { - BOOST_STATIC_ASSERT(sizeof(T)==sizeof(long)); long const res=BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast<long*>(target), *reinterpret_cast<long*>(&new_value), *reinterpret_cast<long*>(&comparand)); - return *reinterpret_cast<T const*>(&res); + return *reinterpret_cast<state_data const*>(&res); } enum @@ -67,19 +65,19 @@ namespace boost { if(old_state.exclusive_waiting) { - BOOST_VERIFY(detail::winapi::ReleaseSemaphore(semaphores[exclusive_sem],1,0)!=0); + BOOST_VERIFY(winapi::ReleaseSemaphore(semaphores[exclusive_sem],1,0)!=0); } if(old_state.shared_waiting || old_state.exclusive_waiting) { - BOOST_VERIFY(detail::winapi::ReleaseSemaphore(semaphores[unlock_sem],old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0); + BOOST_VERIFY(winapi::ReleaseSemaphore(semaphores[unlock_sem],old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0); } } void release_shared_waiters(state_data old_state) { if(old_state.shared_waiting || old_state.exclusive_waiting) { - BOOST_VERIFY(detail::winapi::ReleaseSemaphore(semaphores[unlock_sem],old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0); + BOOST_VERIFY(winapi::ReleaseSemaphore(semaphores[unlock_sem],old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0); } } @@ -107,9 +105,9 @@ namespace boost ~shared_mutex() { - detail::winapi::CloseHandle(upgrade_sem); - detail::winapi::CloseHandle(semaphores[unlock_sem]); - detail::winapi::CloseHandle(semaphores[exclusive_sem]); + winapi::CloseHandle(upgrade_sem); + winapi::CloseHandle(semaphores[unlock_sem]); + winapi::CloseHandle(semaphores[exclusive_sem]); } bool try_lock_shared() @@ -139,21 +137,60 @@ namespace boost void lock_shared() { + for(;;) + { + state_data old_state=state; + for(;;) + { + state_data new_state=old_state; + if(new_state.exclusive || new_state.exclusive_waiting_blocked) + { + ++new_state.shared_waiting; + if(!new_state.shared_waiting) + { + boost::throw_exception(boost::lock_error()); + } + } + else + { + ++new_state.shared_count; + if(!new_state.shared_count) + { + boost::throw_exception(boost::lock_error()); + } + } -#if defined BOOST_THREAD_USES_DATETIME - BOOST_VERIFY(timed_lock_shared(::boost::detail::get_system_time_sentinel())); -#else - BOOST_VERIFY(try_lock_shared_until(chrono::steady_clock::now())); -#endif + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + + if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) + { + return; + } + + BOOST_VERIFY(winapi::WaitForSingleObjectEx(semaphores[unlock_sem],::boost::detail::win32::infinite,0)==0); + } } -#if defined BOOST_THREAD_USES_DATETIME - template<typename TimeDuration> - bool timed_lock_shared(TimeDuration const & relative_time) + private: + unsigned long getMs(detail::platform_duration const& d) { - return timed_lock_shared(get_system_time()+relative_time); + return static_cast<unsigned long>(d.getMs()); } - bool timed_lock_shared(boost::system_time const& wait_until) + + template <typename Duration> + unsigned long getMs(Duration const& d) + { + return static_cast<unsigned long>(chrono::ceil<chrono::milliseconds>(d).count()); + } + + template <typename Clock, typename Timepoint, typename Duration> + bool do_lock_shared_until(Timepoint const& t, Duration const& max) { for(;;) { @@ -191,7 +228,30 @@ namespace boost return true; } - unsigned long const res=detail::winapi::WaitForSingleObjectEx(semaphores[unlock_sem],::boost::detail::get_milliseconds_until(wait_until), 0); + // If the clock is the system clock, it may jump while this function + // is waiting. To compensate for this and time out near the correct + // time, we call WaitForSingleObjectEx() in a loop with a short + // timeout and recheck the time remaining each time through the loop. + unsigned long res=0; + for(;;) + { + Duration d(t - Clock::now()); + if(d <= Duration::zero()) // timeout occurred + { + res=detail::win32::timeout; + break; + } + if(max != Duration::zero()) + { + d = (std::min)(d, max); + } + res=winapi::WaitForSingleObjectEx(semaphores[unlock_sem],getMs(d),0); + if(res!=detail::win32::timeout) // semaphore released + { + break; + } + } + if(res==detail::win32::timeout) { for(;;) @@ -231,114 +291,45 @@ namespace boost BOOST_ASSERT(res==0); } } + public: + +#if defined BOOST_THREAD_USES_DATETIME + template<typename TimeDuration> + bool timed_lock_shared(TimeDuration const & relative_time) + { + const detail::mono_platform_timepoint t(detail::mono_platform_clock::now() + detail::platform_duration(relative_time)); + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_shared_until<detail::mono_platform_clock>(t, detail::platform_duration::zero()); + } + bool timed_lock_shared(boost::system_time const& wait_until) + { + const detail::real_platform_timepoint t(wait_until); + return do_lock_shared_until<detail::real_platform_clock>(t, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } #endif #ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> bool try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time) { - return try_lock_shared_until(chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool try_lock_shared_until(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(); - return try_lock_shared_until(s_now + ceil<system_clock::duration>(t - c_now)); + const chrono::steady_clock::time_point t(chrono::steady_clock::now() + rel_time); + typedef typename chrono::duration<Rep, Period> Duration; + typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_shared_until<chrono::steady_clock>(t, common_duration::zero()); } template <class Duration> - bool try_lock_shared_until(const chrono::time_point<chrono::system_clock, Duration>& t) + bool try_lock_shared_until(const chrono::time_point<chrono::steady_clock, Duration>& t) { - using namespace chrono; - typedef time_point<chrono::system_clock, chrono::system_clock::duration> sys_tmpt; - return try_lock_shared_until(sys_tmpt(chrono::ceil<chrono::system_clock::duration>(t.time_since_epoch()))); + typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_shared_until<chrono::steady_clock>(t, common_duration::zero()); } - bool try_lock_shared_until(const chrono::time_point<chrono::system_clock, chrono::system_clock::duration>& tp) + template <class Clock, class Duration> + bool try_lock_shared_until(const chrono::time_point<Clock, Duration>& t) { - for(;;) - { - state_data old_state=state; - for(;;) - { - state_data new_state=old_state; - if(new_state.exclusive || new_state.exclusive_waiting_blocked) - { - ++new_state.shared_waiting; - if(!new_state.shared_waiting) - { - boost::throw_exception(boost::lock_error()); - } - } - else - { - ++new_state.shared_count; - if(!new_state.shared_count) - { - boost::throw_exception(boost::lock_error()); - } - } - - state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); - if(current_state==old_state) - { - break; - } - old_state=current_state; - } - - if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) - { - return true; - } - - chrono::system_clock::time_point n = chrono::system_clock::now(); - unsigned long res; - if (tp>n) { - chrono::milliseconds rel_time= chrono::ceil<chrono::milliseconds>(tp-n); - res=detail::winapi::WaitForSingleObjectEx(semaphores[unlock_sem], - static_cast<unsigned long>(rel_time.count()), 0); - } else { - res=detail::win32::timeout; - } - if(res==detail::win32::timeout) - { - for(;;) - { - state_data new_state=old_state; - if(new_state.exclusive || new_state.exclusive_waiting_blocked) - { - if(new_state.shared_waiting) - { - --new_state.shared_waiting; - } - } - else - { - ++new_state.shared_count; - if(!new_state.shared_count) - { - return false; - } - } - - state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); - if(current_state==old_state) - { - break; - } - old_state=current_state; - } - - if(!(old_state.exclusive| old_state.exclusive_waiting_blocked)) - { - return true; - } - return false; - } - - BOOST_ASSERT(res==0); - } + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + return do_lock_shared_until<Clock>(t, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); } #endif @@ -375,7 +366,7 @@ namespace boost { if(old_state.upgrade) { - BOOST_VERIFY(detail::winapi::ReleaseSemaphore(upgrade_sem,1,0)!=0); + BOOST_VERIFY(winapi::ReleaseSemaphore(upgrade_sem,1,0)!=0); } else { @@ -388,24 +379,6 @@ namespace boost } } - void lock() - { - -#if defined BOOST_THREAD_USES_DATETIME - BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel())); -#else - BOOST_VERIFY(try_lock_until(chrono::steady_clock::now())); -#endif - } - -#if defined BOOST_THREAD_USES_DATETIME - template<typename TimeDuration> - bool timed_lock(TimeDuration const & relative_time) - { - return timed_lock(get_system_time()+relative_time); - } -#endif - bool try_lock() { state_data old_state=state; @@ -431,14 +404,11 @@ namespace boost return true; } - -#if defined BOOST_THREAD_USES_DATETIME - bool timed_lock(boost::system_time const& wait_until) + void lock() { for(;;) { state_data old_state=state; - for(;;) { state_data new_state=old_state; @@ -467,14 +437,86 @@ namespace boost if(!old_state.shared_count && !old_state.exclusive) { - return true; + return; } + #ifndef UNDER_CE const bool wait_all = true; #else const bool wait_all = false; #endif - unsigned long const wait_res=detail::winapi::WaitForMultipleObjectsEx(2,semaphores,wait_all,::boost::detail::get_milliseconds_until(wait_until), 0); + BOOST_VERIFY(winapi::WaitForMultipleObjectsEx(2,semaphores,wait_all,::boost::detail::win32::infinite,0)<2); + } + } + + private: + template <typename Clock, typename Timepoint, typename Duration> + bool do_lock_until(Timepoint const& t, Duration const& max) + { + for(;;) + { + state_data old_state=state; + for(;;) + { + state_data new_state=old_state; + if(new_state.shared_count || new_state.exclusive) + { + ++new_state.exclusive_waiting; + if(!new_state.exclusive_waiting) + { + boost::throw_exception(boost::lock_error()); + } + + new_state.exclusive_waiting_blocked=true; + } + else + { + new_state.exclusive=true; + } + + state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); + if(current_state==old_state) + { + break; + } + old_state=current_state; + } + + if(!old_state.shared_count && !old_state.exclusive) + { + return true; + } + + // If the clock is the system clock, it may jump while this function + // is waiting. To compensate for this and time out near the correct + // time, we call WaitForMultipleObjectsEx() in a loop with a short + // timeout and recheck the time remaining each time through the loop. + unsigned long wait_res=0; + for(;;) + { + Duration d(t - Clock::now()); + if(d <= Duration::zero()) // timeout occurred + { + wait_res=detail::win32::timeout; + break; + } + if(max != Duration::zero()) + { + d = (std::min)(d, max); + } + #ifndef UNDER_CE + wait_res=winapi::WaitForMultipleObjectsEx(2,semaphores,true,getMs(d),0); + #else + wait_res=winapi::WaitForMultipleObjectsEx(2,semaphores,false,getMs(d),0); + #endif + //wait_res=winapi::WaitForMultipleObjectsEx(2,semaphores,wait_all,getMs(d), 0); + + if(wait_res!=detail::win32::timeout) // semaphore released + { + break; + } + } + if(wait_res==detail::win32::timeout) { for(;;) @@ -500,7 +542,7 @@ namespace boost state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); if (must_notify) { - BOOST_VERIFY(detail::winapi::ReleaseSemaphore(semaphores[unlock_sem],1,0)!=0); + BOOST_VERIFY(winapi::ReleaseSemaphore(semaphores[unlock_sem],1,0)!=0); } if(current_state==old_state) @@ -515,123 +557,48 @@ namespace boost } return false; } + BOOST_ASSERT(wait_res<2); } } + public: + +#if defined BOOST_THREAD_USES_DATETIME + bool timed_lock(boost::system_time const& wait_until) + { + const detail::real_platform_timepoint t(wait_until); + return do_lock_until<detail::real_platform_clock>(t, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + } + template<typename TimeDuration> + bool timed_lock(TimeDuration const & relative_time) + { + const detail::mono_platform_timepoint t(detail::mono_platform_clock::now() + detail::platform_duration(relative_time)); + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_until<detail::mono_platform_clock>(t, detail::platform_duration::zero()); + } #endif #ifdef BOOST_THREAD_USES_CHRONO template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) { - return try_lock_until(chrono::steady_clock::now() + rel_time); - } - template <class Clock, class Duration> - bool try_lock_until(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(); - return try_lock_until(s_now + ceil<system_clock::duration>(t - c_now)); + const chrono::steady_clock::time_point t(chrono::steady_clock::now() + rel_time); + typedef typename chrono::duration<Rep, Period> Duration; + typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_until<chrono::steady_clock>(t, common_duration::zero()); } template <class Duration> - bool try_lock_until(const chrono::time_point<chrono::system_clock, Duration>& t) + bool try_lock_until(const chrono::time_point<chrono::steady_clock, Duration>& t) { - using namespace chrono; - typedef time_point<chrono::system_clock, chrono::system_clock::duration> sys_tmpt; - return try_lock_until(sys_tmpt(chrono::ceil<chrono::system_clock::duration>(t.time_since_epoch()))); + typedef typename common_type<Duration, typename chrono::steady_clock::duration>::type common_duration; + // The reference clock is steady and so no need to poll periodically, thus 0 ms max (i.e. no max) + return do_lock_until<chrono::steady_clock>(t, common_duration::zero()); } - bool try_lock_until(const chrono::time_point<chrono::system_clock, chrono::system_clock::duration>& tp) + template <class Clock, class Duration> + bool try_lock_until(const chrono::time_point<Clock, Duration>& t) { - for(;;) - { - state_data old_state=state; - - for(;;) - { - state_data new_state=old_state; - if(new_state.shared_count || new_state.exclusive) - { - ++new_state.exclusive_waiting; - if(!new_state.exclusive_waiting) - { - boost::throw_exception(boost::lock_error()); - } - - new_state.exclusive_waiting_blocked=true; - } - else - { - new_state.exclusive=true; - } - - state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); - if(current_state==old_state) - { - break; - } - old_state=current_state; - } - - if(!old_state.shared_count && !old_state.exclusive) - { - return true; - } - #ifndef UNDER_CE - const bool wait_all = true; - #else - const bool wait_all = false; - #endif - - chrono::system_clock::time_point n = chrono::system_clock::now(); - unsigned long wait_res; - if (tp>n) { - chrono::milliseconds rel_time= chrono::ceil<chrono::milliseconds>(tp-chrono::system_clock::now()); - wait_res=detail::winapi::WaitForMultipleObjectsEx(2,semaphores,wait_all, - static_cast<unsigned long>(rel_time.count()), 0); - } else { - wait_res=detail::win32::timeout; - } - if(wait_res==detail::win32::timeout) - { - for(;;) - { - bool must_notify = false; - state_data new_state=old_state; - if(new_state.shared_count || new_state.exclusive) - { - if(new_state.exclusive_waiting) - { - if(!--new_state.exclusive_waiting) - { - new_state.exclusive_waiting_blocked=false; - must_notify = true; - } - } - } - else - { - new_state.exclusive=true; - } - - state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state); - if (must_notify) - { - BOOST_VERIFY(detail::winapi::ReleaseSemaphore(semaphores[unlock_sem],1,0)!=0); - } - if(current_state==old_state) - { - break; - } - old_state=current_state; - } - if(!old_state.shared_count && !old_state.exclusive) - { - return true; - } - return false; - } - BOOST_ASSERT(wait_res<2); - } + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + return do_lock_until<Clock>(t, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); } #endif @@ -698,7 +665,7 @@ namespace boost return; } - BOOST_VERIFY(!detail::winapi::WaitForSingleObjectEx(semaphores[unlock_sem],detail::winapi::infinite, 0)); + BOOST_VERIFY(winapi::WaitForSingleObjectEx(semaphores[unlock_sem],winapi::infinite,0)==0); } } @@ -790,7 +757,7 @@ namespace boost { if(!last_reader) { - BOOST_VERIFY(!detail::winapi::WaitForSingleObjectEx(upgrade_sem,detail::win32::infinite, 0)); + BOOST_VERIFY(winapi::WaitForSingleObjectEx(upgrade_sem,detail::win32::infinite,0)==0); } break; } @@ -823,27 +790,6 @@ namespace boost } release_waiters(old_state); } -// bool try_unlock_upgrade_and_lock() -// { -// return false; -// } -//#ifdef BOOST_THREAD_USES_CHRONO -// template <class Rep, class Period> -// bool -// try_unlock_upgrade_and_lock_for( -// const chrono::duration<Rep, Period>& rel_time) -// { -// return try_unlock_upgrade_and_lock_until( -// chrono::steady_clock::now() + rel_time); -// } -// template <class Clock, class Duration> -// bool -// try_unlock_upgrade_and_lock_until( -// const chrono::time_point<Clock, Duration>& abs_time) -// { -// return false; -// } -//#endif void unlock_and_lock_shared() { diff --git a/boost/thread/win32/thread_data.hpp b/boost/thread/win32/thread_data.hpp index ed74198fbe..f87889efa7 100644 --- a/boost/thread/win32/thread_data.hpp +++ b/boost/thread/win32/thread_data.hpp @@ -10,6 +10,7 @@ #include <boost/thread/thread_time.hpp> #include <boost/thread/win32/thread_primitives.hpp> #include <boost/thread/win32/thread_heap_alloc.hpp> +#include <boost/thread/detail/platform_time.hpp> #include <boost/predef/platform.h> @@ -153,7 +154,7 @@ namespace boost #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS void interrupt() { - BOOST_VERIFY(detail::winapi::SetEvent(interruption_handle)!=0); + BOOST_VERIFY(winapi::SetEvent(interruption_handle)!=0); } #endif typedef detail::win32::handle native_handle_type; @@ -174,146 +175,111 @@ namespace boost BOOST_THREAD_DECL thread_data_base* get_current_thread_data(); typedef boost::intrusive_ptr<detail::thread_data_base> thread_data_ptr; - - struct BOOST_SYMBOL_VISIBLE timeout - { - win32::ticks_type start; - uintmax_t milliseconds; - bool relative; - boost::system_time abs_time; - - static unsigned long const max_non_infinite_wait=0xfffffffe; - - timeout(uintmax_t milliseconds_): - start(win32::GetTickCount64_()()), - milliseconds(milliseconds_), - relative(true) - //, - // abs_time(boost::get_system_time()) - {} - - timeout(boost::system_time const& abs_time_): - start(win32::GetTickCount64_()()), - milliseconds(0), - relative(false), - abs_time(abs_time_) - {} - - struct BOOST_SYMBOL_VISIBLE remaining_time - { - bool more; - unsigned long milliseconds; - - remaining_time(uintmax_t remaining): - more(remaining>max_non_infinite_wait), - milliseconds(more?max_non_infinite_wait:(unsigned long)remaining) - {} - }; - - remaining_time remaining_milliseconds() const - { - if(is_sentinel()) - { - return remaining_time(win32::infinite); - } - else if(relative) - { - win32::ticks_type const now=win32::GetTickCount64_()(); - win32::ticks_type const elapsed=now-start; - return remaining_time((elapsed<milliseconds)?(milliseconds-elapsed):0); - } - else - { - system_time const now=get_system_time(); - if(abs_time<=now) - { - return remaining_time(0); - } - return remaining_time((abs_time-now).total_milliseconds()+1); - } - } - - bool is_sentinel() const - { - return milliseconds==~uintmax_t(0); - } - - - static timeout sentinel() - { - return timeout(sentinel_type()); - } - private: - struct sentinel_type - {}; - - explicit timeout(sentinel_type): - start(0),milliseconds(~uintmax_t(0)),relative(true) - {} - }; - - inline uintmax_t pin_to_zero(intmax_t value) - { - return (value<0)?0u:(uintmax_t)value; - } } namespace this_thread { void BOOST_THREAD_DECL yield() BOOST_NOEXCEPT; - bool BOOST_THREAD_DECL interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time); - inline void interruptible_wait(uintmax_t milliseconds) + bool BOOST_THREAD_DECL interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout); + +#if defined BOOST_THREAD_USES_DATETIME + template<typename TimeDuration> + BOOST_SYMBOL_VISIBLE void sleep(TimeDuration const& rel_time) { - interruptible_wait(detail::win32::invalid_handle_value,milliseconds); + interruptible_wait(detail::win32::invalid_handle_value, detail::internal_platform_clock::now() + detail::platform_duration(rel_time)); } - inline BOOST_SYMBOL_VISIBLE void interruptible_wait(system_time const& abs_time) + + inline BOOST_SYMBOL_VISIBLE void sleep(system_time const& abs_time) { - interruptible_wait(detail::win32::invalid_handle_value,abs_time); + const detail::real_platform_timepoint ts(abs_time); + detail::platform_duration d(ts - detail::real_platform_clock::now()); + while (d > detail::platform_duration::zero()) + { + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + interruptible_wait(detail::win32::invalid_handle_value, detail::internal_platform_clock::now() + d); + d = ts - detail::real_platform_clock::now(); + } } - template<typename TimeDuration> - inline BOOST_SYMBOL_VISIBLE void sleep(TimeDuration const& rel_time) +#endif + +#ifdef BOOST_THREAD_USES_CHRONO + template <class Rep, class Period> + void sleep_for(const chrono::duration<Rep, Period>& d) { - interruptible_wait(detail::pin_to_zero(rel_time.total_milliseconds())); + interruptible_wait(detail::win32::invalid_handle_value, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - inline BOOST_SYMBOL_VISIBLE void sleep(system_time const& abs_time) + + template <class Duration> + void sleep_until(const chrono::time_point<chrono::steady_clock, Duration>& t) { - interruptible_wait(abs_time); + sleep_for(t - chrono::steady_clock::now()); } -// #11322 sleep_for() nanoseconds overload will always return too early on windows -//#ifdef BOOST_THREAD_USES_CHRONO -// inline void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns) -// { -// interruptible_wait(chrono::duration_cast<chrono::milliseconds>(ns).count()); -// } -//#endif + + template <class Clock, class Duration> + void sleep_until(const chrono::time_point<Clock, Duration>& t) + { + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + common_duration d(t - Clock::now()); + while (d > common_duration::zero()) + { + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + sleep_for(d); + d = t - Clock::now(); + } + } +#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) + bool BOOST_THREAD_DECL non_interruptible_wait(detail::win32::handle handle_to_wait_for, detail::internal_platform_timepoint const &timeout); + +#if defined BOOST_THREAD_USES_DATETIME + template<typename TimeDuration> + BOOST_SYMBOL_VISIBLE void sleep(TimeDuration const& rel_time) { - non_interruptible_wait(detail::win32::invalid_handle_value,milliseconds); + non_interruptible_wait(detail::win32::invalid_handle_value, detail::internal_platform_clock::now() + detail::platform_duration(rel_time)); } - inline BOOST_SYMBOL_VISIBLE void non_interruptible_wait(system_time const& abs_time) + + inline BOOST_SYMBOL_VISIBLE void sleep(system_time const& abs_time) { - non_interruptible_wait(detail::win32::invalid_handle_value,abs_time); + const detail::real_platform_timepoint ts(abs_time); + detail::platform_duration d(ts - detail::real_platform_clock::now()); + while (d > detail::platform_duration::zero()) + { + d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); + non_interruptible_wait(detail::win32::invalid_handle_value, detail::internal_platform_clock::now() + d); + d = ts - detail::real_platform_clock::now(); + } } - template<typename TimeDuration> - inline BOOST_SYMBOL_VISIBLE void sleep(TimeDuration const& rel_time) +#endif + +#ifdef BOOST_THREAD_USES_CHRONO + template <class Rep, class Period> + void sleep_for(const chrono::duration<Rep, Period>& d) { - non_interruptible_wait(detail::pin_to_zero(rel_time.total_milliseconds())); + non_interruptible_wait(detail::win32::invalid_handle_value, detail::internal_platform_clock::now() + detail::platform_duration(d)); } - inline BOOST_SYMBOL_VISIBLE void sleep(system_time const& abs_time) + + template <class Duration> + void sleep_until(const chrono::time_point<chrono::steady_clock, Duration>& t) { - non_interruptible_wait(abs_time); + sleep_for(t - chrono::steady_clock::now()); } -// #11322 sleep_for() nanoseconds overload will always return too early on windows -//#ifdef BOOST_THREAD_USES_CHRONO -// inline void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns) -// { -// non_interruptible_wait(chrono::duration_cast<chrono::milliseconds>(ns).count()); -// } -//#endif + + template <class Clock, class Duration> + void sleep_until(const chrono::time_point<Clock, Duration>& t) + { + typedef typename common_type<Duration, typename Clock::duration>::type common_duration; + common_duration d(t - Clock::now()); + while (d > common_duration::zero()) + { + d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); + sleep_for(d); + d = t - Clock::now(); + } + } +#endif } } diff --git a/boost/thread/win32/thread_heap_alloc.hpp b/boost/thread/win32/thread_heap_alloc.hpp index 96621355c5..176d269e05 100644 --- a/boost/thread/win32/thread_heap_alloc.hpp +++ b/boost/thread/win32/thread_heap_alloc.hpp @@ -12,7 +12,7 @@ #include <boost/throw_exception.hpp> #include <boost/core/no_exceptions_support.hpp> -#include <boost/detail/winapi/heap_memory.hpp> +#include <boost/winapi/heap_memory.hpp> #include <boost/config/abi_prefix.hpp> @@ -22,7 +22,7 @@ namespace boost { inline void* allocate_raw_heap_memory(unsigned size) { - void* const heap_memory=detail::winapi::HeapAlloc(detail::winapi::GetProcessHeap(),0,size); + void* const heap_memory=winapi::HeapAlloc(winapi::GetProcessHeap(),0,size); if(!heap_memory) { boost::throw_exception(std::bad_alloc()); @@ -32,7 +32,7 @@ namespace boost inline void free_raw_heap_memory(void* heap_memory) { - BOOST_VERIFY(detail::winapi::HeapFree(detail::winapi::GetProcessHeap(),0,heap_memory)!=0); + BOOST_VERIFY(winapi::HeapFree(winapi::GetProcessHeap(),0,heap_memory)!=0); } #if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) && ! defined (BOOST_NO_CXX11_RVALUE_REFERENCES) template<typename T,typename... Args> diff --git a/boost/thread/win32/thread_primitives.hpp b/boost/thread/win32/thread_primitives.hpp index f93cc2438e..5e378f7664 100644 --- a/boost/thread/win32/thread_primitives.hpp +++ b/boost/thread/win32/thread_primitives.hpp @@ -16,23 +16,22 @@ #include <boost/assert.hpp> #include <boost/thread/exceptions.hpp> #include <boost/detail/interlocked.hpp> -#include <boost/detail/winapi/config.hpp> - -#include <boost/detail/winapi/semaphore.hpp> -#include <boost/detail/winapi/dll.hpp> -#include <boost/detail/winapi/system.hpp> -#include <boost/detail/winapi/time.hpp> -#include <boost/detail/winapi/event.hpp> -#include <boost/detail/winapi/thread.hpp> -#include <boost/detail/winapi/get_current_thread.hpp> -#include <boost/detail/winapi/get_current_thread_id.hpp> -#include <boost/detail/winapi/get_current_process.hpp> -#include <boost/detail/winapi/get_current_process_id.hpp> -#include <boost/detail/winapi/wait.hpp> -#include <boost/detail/winapi/handles.hpp> -#include <boost/detail/winapi/access_rights.hpp> - -//#include <boost/detail/winapi/synchronization.hpp> + +#include <boost/winapi/config.hpp> +#include <boost/winapi/basic_types.hpp> +#include <boost/winapi/semaphore.hpp> +#include <boost/winapi/system.hpp> +#include <boost/winapi/event.hpp> +#include <boost/winapi/thread.hpp> +#include <boost/winapi/get_current_thread.hpp> +#include <boost/winapi/get_current_thread_id.hpp> +#include <boost/winapi/get_current_process.hpp> +#include <boost/winapi/get_current_process_id.hpp> +#include <boost/winapi/wait.hpp> +#include <boost/winapi/handles.hpp> +#include <boost/winapi/access_rights.hpp> + +//#include <boost/winapi/synchronization.hpp> #include <boost/thread/win32/interlocked_read.hpp> #include <algorithm> @@ -46,20 +45,19 @@ namespace boost { namespace win32 { - typedef ::boost::detail::winapi::HANDLE_ handle; - typedef ::boost::detail::winapi::SYSTEM_INFO_ system_info; - typedef unsigned __int64 ticks_type; - typedef ::boost::detail::winapi::FARPROC_ farproc_t; - unsigned const infinite=::boost::detail::winapi::INFINITE_; - unsigned const timeout=::boost::detail::winapi::WAIT_TIMEOUT_; - handle const invalid_handle_value=::boost::detail::winapi::INVALID_HANDLE_VALUE_; - unsigned const event_modify_state=::boost::detail::winapi::EVENT_MODIFY_STATE_; - unsigned const synchronize=::boost::detail::winapi::SYNCHRONIZE_; - unsigned const wait_abandoned=::boost::detail::winapi::WAIT_ABANDONED_; + typedef ::boost::winapi::HANDLE_ handle; + typedef ::boost::winapi::SYSTEM_INFO_ system_info; + typedef ::boost::winapi::ULONGLONG_ ticks_type; + unsigned const infinite=::boost::winapi::INFINITE_; + unsigned const timeout=::boost::winapi::WAIT_TIMEOUT_; + handle const invalid_handle_value=::boost::winapi::INVALID_HANDLE_VALUE_; + unsigned const event_modify_state=::boost::winapi::EVENT_MODIFY_STATE_; + unsigned const synchronize=::boost::winapi::SYNCHRONIZE_; + unsigned const wait_abandoned=::boost::winapi::WAIT_ABANDONED_; unsigned const create_event_initial_set = 0x00000002; unsigned const create_event_manual_reset = 0x00000001; - unsigned const event_all_access = ::boost::detail::winapi::EVENT_ALL_ACCESS_; - unsigned const semaphore_all_access = boost::detail::winapi::SEMAPHORE_ALL_ACCESS_; + unsigned const event_all_access = ::boost::winapi::EVENT_ALL_ACCESS_; + unsigned const semaphore_all_access = boost::winapi::SEMAPHORE_ALL_ACCESS_; } } } @@ -72,96 +70,8 @@ namespace boost { namespace win32 { - namespace detail { typedef ticks_type (__stdcall *gettickcount64_t)(); } -#if !BOOST_PLAT_WINDOWS_RUNTIME - extern "C" - { -#ifdef _MSC_VER - long _InterlockedCompareExchange(long volatile *, long, long); -#pragma intrinsic(_InterlockedCompareExchange) -#elif defined(__MINGW64_VERSION_MAJOR) - long _InterlockedCompareExchange(long volatile *, long, long); -#else - // Mingw doesn't provide intrinsics -#define _InterlockedCompareExchange InterlockedCompareExchange -#endif - } - // Borrowed from https://stackoverflow.com/questions/8211820/userland-interrupt-timer-access-such-as-via-kequeryinterrupttime-or-similar - inline ticks_type __stdcall GetTickCount64emulation() - { - static long count = -1l; - unsigned long previous_count, current_tick32, previous_count_zone, current_tick32_zone; - ticks_type current_tick64; - - previous_count = (unsigned long) boost::detail::interlocked_read_acquire(&count); - current_tick32 = ::boost::detail::winapi::GetTickCount(); - - if(previous_count == (unsigned long)-1l) - { - // count has never been written - unsigned long initial_count; - initial_count = current_tick32 >> 28; - previous_count = (unsigned long) _InterlockedCompareExchange(&count, (long)initial_count, -1l); - - current_tick64 = initial_count; - current_tick64 <<= 28; - current_tick64 += current_tick32 & 0x0FFFFFFF; - return current_tick64; - } - - previous_count_zone = previous_count & 15; - current_tick32_zone = current_tick32 >> 28; - - if(current_tick32_zone == previous_count_zone) - { - // The top four bits of the 32-bit tick count haven't changed since count was last written. - current_tick64 = previous_count; - current_tick64 <<= 28; - current_tick64 += current_tick32 & 0x0FFFFFFF; - return current_tick64; - } - - if(current_tick32_zone == previous_count_zone + 1 || (current_tick32_zone == 0 && previous_count_zone == 15)) - { - // The top four bits of the 32-bit tick count have been incremented since count was last written. - unsigned long new_count = previous_count + 1; - _InterlockedCompareExchange(&count, (long)new_count, (long)previous_count); - current_tick64 = new_count; - current_tick64 <<= 28; - current_tick64 += current_tick32 & 0x0FFFFFFF; - return current_tick64; - } - - // Oops, we weren't called often enough, we're stuck - return 0xFFFFFFFF; - } -#else -#endif - inline detail::gettickcount64_t GetTickCount64_() - { - static detail::gettickcount64_t gettickcount64impl; - if(gettickcount64impl) - return gettickcount64impl; - - // GetTickCount and GetModuleHandle are not allowed in the Windows Runtime, - // and kernel32 isn't used in Windows Phone. -#if BOOST_PLAT_WINDOWS_RUNTIME - gettickcount64impl = &::boost::detail::winapi::GetTickCount64; -#else - farproc_t addr=GetProcAddress( -#if !defined(BOOST_NO_ANSI_APIS) - ::boost::detail::winapi::GetModuleHandleA("KERNEL32.DLL"), -#else - ::boost::detail::winapi::GetModuleHandleW(L"KERNEL32.DLL"), -#endif - "GetTickCount64"); - if(addr) - gettickcount64impl=(detail::gettickcount64_t) addr; - else - gettickcount64impl=&GetTickCount64emulation; -#endif - return gettickcount64impl; - } + namespace detail { typedef ticks_type (WINAPI *gettickcount64_t)(); } + extern BOOST_THREAD_DECL boost::detail::win32::detail::gettickcount64_t gettickcount64; enum event_type { @@ -185,11 +95,11 @@ namespace boost initial_event_state state) { #if !defined(BOOST_NO_ANSI_APIS) - handle const res = ::boost::detail::winapi::CreateEventA(0, type, state, mutex_name); + handle const res = ::boost::winapi::CreateEventA(0, type, state, mutex_name); #elif BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA - handle const res = ::boost::detail::winapi::CreateEventW(0, type, state, mutex_name); + handle const res = ::boost::winapi::CreateEventW(0, type, state, mutex_name); #else - handle const res = ::boost::detail::winapi::CreateEventExW( + handle const res = ::boost::winapi::CreateEventExW( 0, mutex_name, type ? create_event_manual_reset : 0 | state ? create_event_initial_set : 0, @@ -211,12 +121,12 @@ namespace boost inline handle create_anonymous_semaphore_nothrow(long initial_count,long max_count) { #if !defined(BOOST_NO_ANSI_APIS) - handle const res=::boost::detail::winapi::CreateSemaphoreA(0,initial_count,max_count,0); + handle const res=::boost::winapi::CreateSemaphoreA(0,initial_count,max_count,0); #else #if BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA - handle const res=::boost::detail::winapi::CreateSemaphoreEx(0,initial_count,max_count,0,0); + handle const res=::boost::winapi::CreateSemaphoreEx(0,initial_count,max_count,0,0); #else - handle const res=::boost::detail::winapi::CreateSemaphoreExW(0,initial_count,max_count,0,0,semaphore_all_access); + handle const res=::boost::winapi::CreateSemaphoreExW(0,initial_count,max_count,0,0,semaphore_all_access); #endif #endif return res; @@ -234,10 +144,10 @@ namespace boost inline handle duplicate_handle(handle source) { - handle const current_process=::boost::detail::winapi::GetCurrentProcess(); + handle const current_process=::boost::winapi::GetCurrentProcess(); long const same_access_flag=2; handle new_handle=0; - bool const success=::boost::detail::winapi::DuplicateHandle(current_process,source,current_process,&new_handle,0,false,same_access_flag)!=0; + bool const success=::boost::winapi::DuplicateHandle(current_process,source,current_process,&new_handle,0,false,same_access_flag)!=0; if(!success) { boost::throw_exception(thread_resource_error()); @@ -247,15 +157,15 @@ namespace boost inline void release_semaphore(handle semaphore,long count) { - BOOST_VERIFY(::boost::detail::winapi::ReleaseSemaphore(semaphore,count,0)!=0); + BOOST_VERIFY(::boost::winapi::ReleaseSemaphore(semaphore,count,0)!=0); } inline void get_system_info(system_info *info) { #if BOOST_PLAT_WINDOWS_RUNTIME - ::boost::detail::winapi::GetNativeSystemInfo(info); + ::boost::winapi::GetNativeSystemInfo(info); #else - ::boost::detail::winapi::GetSystemInfo(info); + ::boost::winapi::GetSystemInfo(info); #endif } @@ -266,15 +176,15 @@ namespace boost #if BOOST_PLAT_WINDOWS_RUNTIME std::this_thread::yield(); #else - ::boost::detail::winapi::Sleep(0); + ::boost::winapi::Sleep(0); #endif } else { #if BOOST_PLAT_WINDOWS_RUNTIME - ::boost::detail::winapi::WaitForSingleObjectEx(::boost::detail::winapi::GetCurrentThread(), milliseconds, 0); + ::boost::winapi::WaitForSingleObjectEx(::boost::winapi::GetCurrentThread(), milliseconds, 0); #else - ::boost::detail::winapi::Sleep(milliseconds); + ::boost::winapi::Sleep(milliseconds); #endif } } @@ -290,7 +200,7 @@ namespace boost { if (m_completionHandle != ::boost::detail::win32::invalid_handle_value) { - ::boost::detail::winapi::CloseHandle(m_completionHandle); + ::boost::winapi::CloseHandle(m_completionHandle); } } @@ -318,7 +228,7 @@ namespace boost { if(handle_to_manage && handle_to_manage!=invalid_handle_value) { - BOOST_VERIFY(::boost::detail::winapi::CloseHandle(handle_to_manage)); + BOOST_VERIFY(::boost::winapi::CloseHandle(handle_to_manage)); } } |