Chris@16: // Distributed under the Boost Software License, Version 1.0. (See Chris@16: // accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: // (C) Copyright 2013 Vicente J. Botet Escriba Chris@16: Chris@16: #ifndef BOOST_THREAD_COMPLETION_LATCH_HPP Chris@16: #define BOOST_THREAD_COMPLETION_LATCH_HPP Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@101: //#include Chris@101: #include Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost Chris@16: { Chris@16: namespace thread_detail Chris@16: { Chris@16: void noop() Chris@16: { Chris@16: } Chris@16: } Chris@16: class completion_latch Chris@16: { Chris@16: public: Chris@16: /// the implementation defined completion function type Chris@101: //typedef detail::nullary_function completion_function; Chris@101: typedef csbl::function completion_function; Chris@16: /// noop completion function factory Chris@16: static completion_function noop() Chris@16: { Chris@16: return completion_function(&thread_detail::noop); Chris@16: } Chris@16: Chris@16: private: Chris@16: struct around_wait; Chris@16: friend struct around_wait; Chris@16: struct around_wait Chris@16: { Chris@16: completion_latch &that_; Chris@16: boost::unique_lock &lk_; Chris@16: around_wait(completion_latch &that, boost::unique_lock &lk) Chris@16: : that_(that), lk_(lk) Chris@16: { Chris@16: that_.leavers_.cond_.wait(lk, detail::counter_is_zero(that_.leavers_)); Chris@16: that_.waiters_.inc_and_notify_all(); Chris@16: that_.leavers_.cond_.wait(lk, detail::counter_is_not_zero(that_.leavers_)); Chris@16: } Chris@16: ~around_wait() Chris@16: { Chris@16: that_.waiters_.dec_and_notify_all(); Chris@16: } Chris@16: }; Chris@16: Chris@16: bool count_down(unique_lock &lk) Chris@16: { Chris@16: BOOST_ASSERT(count_ > 0); Chris@16: if (--count_ == 0) Chris@16: { Chris@16: waiters_.cond_.wait(lk, detail::counter_is_not_zero(waiters_)); Chris@16: leavers_.assign_and_notify_all(waiters_); Chris@16: count_.cond_.notify_all(); Chris@16: waiters_.cond_.wait(lk, detail::counter_is_zero(waiters_)); Chris@16: leavers_.assign_and_notify_all(0); Chris@16: lk.unlock(); Chris@16: funct_(); Chris@16: return true; Chris@16: } Chris@16: return false; Chris@16: } Chris@16: Chris@16: public: Chris@16: BOOST_THREAD_NO_COPYABLE( completion_latch ) Chris@16: Chris@16: /// Constructs a latch with a given count. Chris@16: completion_latch(std::size_t count) : Chris@16: count_(count), funct_(noop()), waiters_(0), leavers_(0) Chris@16: { Chris@16: } Chris@16: Chris@16: /// Constructs a latch with a given count and a completion function. Chris@16: template Chris@16: completion_latch(std::size_t count, BOOST_THREAD_RV_REF(F) funct) : Chris@16: count_(count), Chris@16: funct_(boost::move(funct)), Chris@16: waiters_(0), Chris@16: leavers_(0) Chris@16: { Chris@16: } Chris@16: template Chris@16: completion_latch(std::size_t count, void(*funct)()) : Chris@16: count_(count), funct_(funct), waiters_(0), leavers_(0) Chris@16: { Chris@16: } Chris@16: Chris@16: /// Chris@16: ~completion_latch() Chris@16: { Chris@16: } Chris@16: Chris@16: /// Blocks until the latch has counted down to zero. Chris@16: void wait() Chris@16: { Chris@16: boost::unique_lock lk(mutex_); Chris@16: around_wait aw(*this, lk); Chris@16: count_.cond_.wait(lk, detail::counter_is_zero(count_)); Chris@16: } Chris@16: Chris@16: /// @return true if the internal counter is already 0, false otherwise Chris@16: bool try_wait() Chris@16: { Chris@16: boost::unique_lock lk(mutex_); Chris@16: around_wait aw(*this, lk); Chris@16: return (count_ == 0); Chris@16: } Chris@16: Chris@16: /// try to wait for a specified amount of time Chris@16: /// @return whether there is a timeout or not. Chris@16: template Chris@16: cv_status wait_for(const chrono::duration& rel_time) Chris@16: { Chris@16: boost::unique_lock lk(mutex_); Chris@16: around_wait aw(*this, lk); Chris@16: return count_.cond_.wait_for(lk, rel_time, detail::counter_is_zero(count_)) Chris@16: ? cv_status::no_timeout Chris@16: : cv_status::timeout; Chris@16: } Chris@16: Chris@16: /// try to wait until the specified time_point is reached Chris@16: /// @return whether there is a timeout or not. Chris@16: template Chris@16: cv_status wait_until(const chrono::time_point& abs_time) Chris@16: { Chris@16: boost::unique_lock lk(mutex_); Chris@16: around_wait aw(*this, lk); Chris@16: return count_.cond_.wait_until(lk, abs_time, detail::counter_is_zero(count_)) Chris@16: ? cv_status::no_timeout Chris@16: : cv_status::timeout; Chris@16: } Chris@16: Chris@16: /// Decrement the count and notify anyone waiting if we reach zero. Chris@16: /// @Requires count must be greater than 0 Chris@16: void count_down() Chris@16: { Chris@16: unique_lock lk(mutex_); Chris@16: count_down(lk); Chris@16: } Chris@16: void signal() Chris@16: { Chris@16: count_down(); Chris@16: } Chris@16: Chris@16: /// Decrement the count and notify anyone waiting if we reach zero. Chris@16: /// Blocks until the latch has counted down to zero. Chris@16: /// @Requires count must be greater than 0 Chris@16: void count_down_and_wait() Chris@16: { Chris@16: boost::unique_lock lk(mutex_); Chris@16: if (count_down(lk)) Chris@16: { Chris@16: return; Chris@16: } Chris@16: around_wait aw(*this, lk); Chris@16: count_.cond_.wait(lk, detail::counter_is_zero(count_)); Chris@16: } Chris@16: void sync() Chris@16: { Chris@16: count_down_and_wait(); Chris@16: } Chris@16: Chris@16: /// Reset the counter Chris@16: /// #Requires This method may only be invoked when there are no other threads currently inside the count_down_and_wait() method. Chris@16: void reset(std::size_t count) Chris@16: { Chris@16: boost::lock_guard lk(mutex_); Chris@16: //BOOST_ASSERT(count_ == 0); Chris@16: count_ = count; Chris@16: } Chris@16: Chris@16: /// Resets the latch with the new completion function. Chris@16: /// The next time the internal count reaches 0, this function will be invoked. Chris@16: /// This completion function may only be invoked when there are no other threads Chris@16: /// currently inside the count_down and wait related functions. Chris@16: /// It may also be invoked from within the registered completion function. Chris@16: /// @Returns the old completion function if any or noop if Chris@16: Chris@16: #ifdef BOOST_NO_CXX11_HDR_FUNCTIONAL Chris@16: template Chris@16: completion_function then(BOOST_THREAD_RV_REF(F) funct) Chris@16: { Chris@16: boost::lock_guard lk(mutex_); Chris@16: completion_function tmp(funct_); Chris@16: funct_ = boost::move(funct); Chris@16: return tmp; Chris@16: } Chris@16: #endif Chris@16: completion_function then(void(*funct)()) Chris@16: { Chris@16: boost::lock_guard lk(mutex_); Chris@16: completion_function tmp(funct_); Chris@16: funct_ = completion_function(funct); Chris@16: return tmp; Chris@16: } Chris@16: Chris@16: private: Chris@16: mutex mutex_; Chris@16: detail::counter count_; Chris@16: completion_function funct_; Chris@16: detail::counter waiters_; Chris@16: detail::counter leavers_; Chris@16: }; Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif