diff --git a/beast/threads/Stoppable.h b/beast/threads/Stoppable.h index 24d8f38a..25ac9959 100644 --- a/beast/threads/Stoppable.h +++ b/beast/threads/Stoppable.h @@ -22,10 +22,11 @@ #include #include - #include - #include +#include +#include +#include namespace beast { @@ -189,6 +190,15 @@ public: /** Returns `true` if all children have stopped. */ bool areChildrenStopped () const; + /** Sleep or wake up on stop. + + @return `true` if we are stopping + */ + template + bool + alertable_sleep_for( + std::chrono::duration const& d); + protected: /** Called by derived classes to indicate that the stoppable has stopped. */ void stopped (); @@ -316,6 +326,15 @@ public: */ void stop (Journal j); + /** Sleep or wake up on stop. + + @return `true` if we are stopping + */ + template + bool + alertable_sleep_for( + std::chrono::duration const& d); + private: /* Notify a root stoppable and children to stop, without waiting. Has no effect if the stoppable was already notified. @@ -326,11 +345,34 @@ private: void stopAsync(Journal j); std::atomic m_prepared; - std::atomic m_calledStop; + bool m_calledStop; std::atomic m_calledStopAsync; + std::mutex m_; + std::condition_variable c_; }; /** @} */ +//------------------------------------------------------------------------------ + +template +bool +RootStoppable::alertable_sleep_for( + std::chrono::duration const& d) +{ + std::unique_lock lock(m_); + if (m_calledStop) + return true; + return c_.wait_for(lock, d, [this]{return m_calledStop;}); +} + +template +bool +Stoppable::alertable_sleep_for( + std::chrono::duration const& d) +{ + return m_root.alertable_sleep_for(d); +} + } #endif diff --git a/beast/threads/impl/Stoppable.cpp b/beast/threads/impl/Stoppable.cpp index d5466623..479366e4 100644 --- a/beast/threads/impl/Stoppable.cpp +++ b/beast/threads/impl/Stoppable.cpp @@ -183,12 +183,16 @@ void RootStoppable::stop (Journal j) // Must have a prior call to start() bassert (m_started); - if (m_calledStop.exchange (true) == true) { - j.warning << "Stoppable::stop called again"; - return; + std::lock_guard lock(m_); + if (m_calledStop) + { + j.warning << "Stoppable::stop called again"; + return; + } + m_calledStop = true; + c_.notify_all(); } - stopAsync (j); stopRecursive (j); }