From f07515eb885333fcfff6ca0e06eb78a2ffa0a6af Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 28 Sep 2013 15:00:35 -0700 Subject: [PATCH] Add Stoppable prepare and start interfaces --- Builds/VisualStudio2012/Beast.props | 12 +- Builds/VisualStudio2012/beast.vcxproj | 33 +-- Builds/VisualStudio2012/beast.vcxproj.filters | 38 +-- beast/ByteOrder.h | 2 +- .../diagnostic => beast}/SafeBool.h | 42 +-- beast/Utility.h | 2 + beast/chrono/impl/RelativeTime.cpp | 2 +- .../diagnostic => beast/utility}/Debug.h | 10 +- .../diagnostic => beast/utility}/Error.h | 17 +- beast/utility/Utility.cpp | 6 +- .../utility/impl}/Debug.cpp | 7 +- .../utility/impl}/Error.cpp | 21 ++ .../VisualStudio2012 => config}/BeastConfig.h | 0 modules/beast_core/beast_core.cpp | 2 - modules/beast_core/beast_core.h | 4 +- modules/beast_core/thread/Stoppable.cpp | 212 +++++++++------- modules/beast_core/thread/Stoppable.h | 239 +++++++++++------- scripts/compile.sh | 4 +- 18 files changed, 395 insertions(+), 258 deletions(-) rename {modules/beast_core/diagnostic => beast}/SafeBool.h (93%) rename {modules/beast_core/diagnostic => beast/utility}/Debug.h (95%) rename {modules/beast_core/diagnostic => beast/utility}/Error.h (95%) rename {modules/beast_core/diagnostic => beast/utility/impl}/Debug.cpp (99%) rename {modules/beast_core/diagnostic => beast/utility/impl}/Error.cpp (96%) rename {Builds/VisualStudio2012 => config}/BeastConfig.h (100%) diff --git a/Builds/VisualStudio2012/Beast.props b/Builds/VisualStudio2012/Beast.props index c7e4aad4d0..792f3409ca 100644 --- a/Builds/VisualStudio2012/Beast.props +++ b/Builds/VisualStudio2012/Beast.props @@ -1,7 +1,9 @@  - + + ..\.. + @@ -9,8 +11,12 @@ BEAST_COMPILING_STATIC_LIBARARY=1;_CRTDBG_MAP_ALLOC;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions) true false - %(AdditionalIncludeDirectories) + $(RepoDir)\config;%(AdditionalIncludeDirectories) - + + + $(RepoDir) + + \ No newline at end of file diff --git a/Builds/VisualStudio2012/beast.vcxproj b/Builds/VisualStudio2012/beast.vcxproj index 3cf894ed28..58e8fb8ec4 100644 --- a/Builds/VisualStudio2012/beast.vcxproj +++ b/Builds/VisualStudio2012/beast.vcxproj @@ -78,6 +78,7 @@ + @@ -119,6 +120,7 @@ + @@ -143,9 +145,12 @@ + + + @@ -217,13 +222,10 @@ - - - @@ -392,7 +394,6 @@ - @@ -490,6 +491,18 @@ true + + true + true + true + true + + + true + true + true + true + true true @@ -701,18 +714,6 @@ true true - - true - true - true - true - - - true - true - true - true - true true diff --git a/Builds/VisualStudio2012/beast.vcxproj.filters b/Builds/VisualStudio2012/beast.vcxproj.filters index e83071e182..36e80f17eb 100644 --- a/Builds/VisualStudio2012/beast.vcxproj.filters +++ b/Builds/VisualStudio2012/beast.vcxproj.filters @@ -73,6 +73,9 @@ beast\crypto\impl\sha2 + + scripts + @@ -270,6 +273,9 @@ {39886e0f-1607-4b7a-81cf-011d83dadee3} + + {c65af439-8c23-46c3-9b95-7da15651e5f6} + @@ -578,21 +584,12 @@ beast_core\containers - - beast_core\diagnostic - - - beast_core\diagnostic - beast_core\diagnostic beast_core\diagnostic - - beast_core\diagnostic - beast_core\diagnostic @@ -1178,7 +1175,6 @@ beast\http - beast @@ -1209,6 +1205,16 @@ beast\chrono + + beast + + + + beast\utility + + + beast\utility + @@ -1451,12 +1457,6 @@ beast_core\native - - beast_core\diagnostic - - - beast_core\diagnostic - beast_core\diagnostic @@ -1781,6 +1781,12 @@ beast\chrono + + beast\utility\impl + + + beast\utility\impl + diff --git a/beast/ByteOrder.h b/beast/ByteOrder.h index e574cf29e3..0e40616e7d 100644 --- a/beast/ByteOrder.h +++ b/beast/ByteOrder.h @@ -34,7 +34,7 @@ namespace beast { /** Contains static methods for converting the byte order between different endiannesses. */ -class BEAST_API ByteOrder : public Uncopyable +class ByteOrder : public Uncopyable { public: //============================================================================== diff --git a/modules/beast_core/diagnostic/SafeBool.h b/beast/SafeBool.h similarity index 93% rename from modules/beast_core/diagnostic/SafeBool.h rename to beast/SafeBool.h index 1b6dfad0e3..fbae195923 100644 --- a/modules/beast_core/diagnostic/SafeBool.h +++ b/beast/SafeBool.h @@ -20,23 +20,11 @@ #ifndef BEAST_SAFEBOOL_H_INCLUDED #define BEAST_SAFEBOOL_H_INCLUDED -/** Safe evaluation of class as `bool`. +namespace beast { - This allows a class to be safely evaluated as a bool without the usual - harmful side effects of the straightforward operator conversion approach. - To use it, derive your class from SafeBool and implement `asBoolean()` as: +namespace detail { - @code - - bool asBoolean () const; - - @endcode - - Ideas from http://www.artima.com/cppsource/safebool.html - - @class SafeBool -*/ -class BEAST_API SafeBoolBase +class SafeBoolBase { private: void disallowed () const { } @@ -56,11 +44,29 @@ protected: ~SafeBoolBase () { } }; +} + +/** Safe evaluation of class as `bool`. + + This allows a class to be safely evaluated as a bool without the usual + harmful side effects of the straightforward operator conversion approach. + To use it, derive your class from SafeBool and implement `asBoolean()` as: + + @code + + bool asBoolean () const; + + @endcode + + Ideas from http://www.artima.com/cppsource/safebool.html + + @class SafeBool +*/ template -class SafeBool : public SafeBoolBase +class SafeBool : public detail::SafeBoolBase { public: - operator boolean_t () const + operator detail::SafeBoolBase::boolean_t () const { return (static_cast (this))->asBoolean () ? &SafeBoolBase::allowed : 0; @@ -82,6 +88,8 @@ void operator!= (SafeBool const& lhs, SafeBool const& rhs) lhs.disallowed (); } +} + #endif diff --git a/beast/Utility.h b/beast/Utility.h index 807a251801..f27cc5947c 100644 --- a/beast/Utility.h +++ b/beast/Utility.h @@ -20,7 +20,9 @@ #ifndef BEAST_UTILITY_H_INCLUDED #define BEAST_UTILITY_H_INCLUDED +#include "utility/Debug.h" #include "utility/EnableIf.h" +#include "utility/Error.h" #include "utility/Journal.h" #endif diff --git a/beast/chrono/impl/RelativeTime.cpp b/beast/chrono/impl/RelativeTime.cpp index ca58862b29..ac78d39948 100644 --- a/beast/chrono/impl/RelativeTime.cpp +++ b/beast/chrono/impl/RelativeTime.cpp @@ -21,7 +21,7 @@ */ //============================================================================== -#include "RelativeTime.h" +#include "../RelativeTime.h" // VFALCO TODO Migrate the localizable strings interfaces for this file diff --git a/modules/beast_core/diagnostic/Debug.h b/beast/utility/Debug.h similarity index 95% rename from modules/beast_core/diagnostic/Debug.h rename to beast/utility/Debug.h index e80418e042..0a1d5e3e0f 100644 --- a/modules/beast_core/diagnostic/Debug.h +++ b/beast/utility/Debug.h @@ -17,8 +17,12 @@ */ //============================================================================== -#ifndef BEAST_DEBUG_H_INCLUDED -#define BEAST_DEBUG_H_INCLUDED +#ifndef BEAST_UTILITY_DEBUG_H_INCLUDED +#define BEAST_UTILITY_DEBUG_H_INCLUDED + +#include "../strings/String.h" + +namespace beast { // Auxiliary outines for debugging @@ -83,4 +87,6 @@ extern void checkHeap (); } +} + #endif diff --git a/modules/beast_core/diagnostic/Error.h b/beast/utility/Error.h similarity index 95% rename from modules/beast_core/diagnostic/Error.h rename to beast/utility/Error.h index 946270b758..f9b188925e 100644 --- a/modules/beast_core/diagnostic/Error.h +++ b/beast/utility/Error.h @@ -17,8 +17,17 @@ */ //============================================================================== -#ifndef BEAST_ERROR_H_INCLUDED -#define BEAST_ERROR_H_INCLUDED +#ifndef BEAST_UTILITY_ERROR_H_INCLUDED +#define BEAST_UTILITY_ERROR_H_INCLUDED + +#include "../Config.h" + +#include "../SafeBool.h" +#include "../strings/String.h" + +#include + +namespace beast { /** A concise error report. @@ -32,7 +41,7 @@ @ingroup beast_core */ -class BEAST_API Error +class Error : public std::exception , public SafeBool { @@ -119,4 +128,6 @@ private: mutable char const* m_szWhat; }; +} + #endif diff --git a/beast/utility/Utility.cpp b/beast/utility/Utility.cpp index fbfc1450a2..0eac282565 100644 --- a/beast/utility/Utility.cpp +++ b/beast/utility/Utility.cpp @@ -19,6 +19,10 @@ #include "BeastConfig.h" -#include "../../modules/beast_core/beast_core.h" +#include "impl/Error.cpp" + +// For Journal and Debug +#include "../../modules/beast_core/beast_core.h" #include "impl/Journal.cpp" +#include "impl/Debug.cpp" diff --git a/modules/beast_core/diagnostic/Debug.cpp b/beast/utility/impl/Debug.cpp similarity index 99% rename from modules/beast_core/diagnostic/Debug.cpp rename to beast/utility/impl/Debug.cpp index e9e6545926..85b8d25a26 100644 --- a/modules/beast_core/diagnostic/Debug.cpp +++ b/beast/utility/impl/Debug.cpp @@ -17,8 +17,9 @@ */ //============================================================================== -namespace Debug -{ +namespace beast { + +namespace Debug { void breakPoint () { @@ -358,3 +359,5 @@ public: }; static DebugTests debugTests; + +} diff --git a/modules/beast_core/diagnostic/Error.cpp b/beast/utility/impl/Error.cpp similarity index 96% rename from modules/beast_core/diagnostic/Error.cpp rename to beast/utility/impl/Error.cpp index 055d89a09a..9083413fed 100644 --- a/modules/beast_core/diagnostic/Error.cpp +++ b/beast/utility/impl/Error.cpp @@ -17,6 +17,19 @@ */ //============================================================================== +#include "../Error.h" +#include "../Debug.h" + +#include + +// VFALCO TODO Localizable strings +#ifndef TRANS +#define TRANS(s) (s) +#define UNDEF_TRANS +#endif + +namespace beast { + Error::Error () : m_code (success) , m_lineNumber (0) @@ -241,3 +254,11 @@ String const Error::getReasonTextForCode (Code code) return s; } + +#ifdef UNDEF_TRANS +#undef TRANs +#undef UNDEF_TRANS +#endif + +} + diff --git a/Builds/VisualStudio2012/BeastConfig.h b/config/BeastConfig.h similarity index 100% rename from Builds/VisualStudio2012/BeastConfig.h rename to config/BeastConfig.h diff --git a/modules/beast_core/beast_core.cpp b/modules/beast_core/beast_core.cpp index af54ca760e..a24524820a 100644 --- a/modules/beast_core/beast_core.cpp +++ b/modules/beast_core/beast_core.cpp @@ -138,8 +138,6 @@ namespace beast #include "containers/DynamicList.cpp" #include "containers/HashMap.cpp" -#include "diagnostic/Debug.cpp" -#include "diagnostic/Error.cpp" #include "diagnostic/FatalError.cpp" #include "diagnostic/FPUFlags.cpp" #include "diagnostic/LeakChecked.cpp" diff --git a/modules/beast_core/beast_core.h b/modules/beast_core/beast_core.h index e8c4cc9119..881856bab6 100644 --- a/modules/beast_core/beast_core.h +++ b/modules/beast_core/beast_core.h @@ -54,6 +54,7 @@ #include "../../beast/Memory.h" #include "../../beast/Intrusive.h" #include "../../beast/Net.h" +#include "../../beast/SafeBool.h" #include "../../beast/Strings.h" #include "../../beast/TypeTraits.h" #include "../../beast/Thread.h" @@ -146,7 +147,6 @@ class FileOutputStream; #include "memory/MemoryAlignment.h" #include "memory/CacheLine.h" #include "threads/ReadWriteMutex.h" -#include "diagnostic/SafeBool.h" #include "threads/WaitableEvent.h" #include "threads/Thread.h" #include "threads/SpinLock.h" @@ -154,8 +154,6 @@ class FileOutputStream; #include "thread/MutexTraits.h" #include "thread/TrackedMutex.h" #include "diagnostic/FatalError.h" -#include "diagnostic/Error.h" -#include "diagnostic/Debug.h" #include "text/LexicalCast.h" #include "memory/ContainerDeletePolicy.h" #include "maths/Math.h" diff --git a/modules/beast_core/thread/Stoppable.cpp b/modules/beast_core/thread/Stoppable.cpp index e8773f27f7..bdb866c84a 100644 --- a/modules/beast_core/thread/Stoppable.cpp +++ b/modules/beast_core/thread/Stoppable.cpp @@ -17,84 +17,45 @@ */ //============================================================================== - -Stoppable::Stoppable (char const* name, Stoppable& parent) +Stoppable::Stoppable (char const* name, RootStoppable& root) : m_name (name) - , m_root (false) + , m_root (root) , m_child (this) - , m_calledStop (false) , m_stopped (false) , m_childrenStopped (false) { - // must not have had stop called +} + +Stoppable::Stoppable (char const* name, Stoppable& parent) + : m_name (name) + , m_root (parent.m_root) + , m_child (this) + , m_stopped (false) + , m_childrenStopped (false) +{ + // Must not have stopping parent. bassert (! parent.isStopping()); parent.m_children.push_front (&m_child); } -Stoppable::Stoppable (char const* name, Stoppable* parent) - : m_name (name) - , m_root (parent == nullptr) - , m_child (this) - , m_calledStop (false) - , m_stopped (false) - , m_childrenStopped (false) -{ - if (parent != nullptr) - { - // must not have had stop called - bassert (! parent->isStopping()); - - parent->m_children.push_front (&m_child); - } -} - Stoppable::~Stoppable () { - // must be stopped - bassert (m_stopped); - - // children must be stopped + // Children must be stopped. bassert (m_childrenStopped); } -void Stoppable::stop (Journal::Stream stream) +bool Stoppable::isStopping() const { - // may only be called once - if (m_calledStop) - return; - - m_calledStop = true; - - // must be called from a root stoppable - bassert (m_root); - - // send the notification - stopAsync (); - - // now block on the tree of Stoppable objects from the leaves up. - stopRecursive (stream); + return m_root.isStopping(); } -void Stoppable::stopAsync () -{ - // must be called from a root stoppable - bassert (m_root); - - stopAsyncRecursive (); -} - -bool Stoppable::isStopping () -{ - return m_calledStopAsync.get() != 0; -} - -bool Stoppable::isStopped () +bool Stoppable::isStopped () const { return m_stopped; } -bool Stoppable::areChildrenStopped () +bool Stoppable::areChildrenStopped () const { return m_childrenStopped; } @@ -104,75 +65,134 @@ void Stoppable::stopped () m_stoppedEvent.signal(); } -void Stoppable::onStop() +void Stoppable::onPrepare (Journal journal) +{ +} + +void Stoppable::onStart (Journal journal) +{ +} + +void Stoppable::onStop (Journal journal) { stopped(); } -void Stoppable::onChildrenStopped () +void Stoppable::onChildrenStopped (Journal journal) { } //------------------------------------------------------------------------------ -void Stoppable::stopAsyncRecursive () +void Stoppable::prepareRecursive (Journal journal) { - // make sure we only do this once - if (m_root) - { - // if this fails, some other thread got to it first - if (! m_calledStopAsync.compareAndSetBool (1, 0)) - return; - } - else - { - // can't possibly already be set - bassert (m_calledStopAsync.get() == 0); - - m_calledStopAsync.set (1); - } - - // notify this stoppable - onStop (); - - // notify children + onPrepare (journal); for (Children::const_iterator iter (m_children.cbegin ()); iter != m_children.cend(); ++iter) - { - iter->stoppable->stopAsyncRecursive(); - } + iter->stoppable->prepareRecursive (journal); } -void Stoppable::stopRecursive (Journal::Stream stream) +void Stoppable::startRecursive (Journal journal) { - // Block on each child recursively. Thinking of the Stoppable - // hierarchy as a tree with the root at the top, we will block - // first on leaves, and then at each successivly higher level. + onStart (journal); + for (Children::const_iterator iter (m_children.cbegin ()); + iter != m_children.cend(); ++iter) + iter->stoppable->startRecursive (journal); +} + +void Stoppable::stopAsyncRecursive (Journal journal) +{ + onStop (journal); + for (Children::const_iterator iter (m_children.cbegin ()); + iter != m_children.cend(); ++iter) + iter->stoppable->stopAsyncRecursive (journal); +} + +void Stoppable::stopRecursive (Journal journal) +{ + // Block on each child from the bottom of the tree up. // for (Children::const_iterator iter (m_children.cbegin ()); iter != m_children.cend(); ++iter) - { - iter->stoppable->stopRecursive (stream); - } + iter->stoppable->stopRecursive (journal); - // Once we get here, we either have no children, or all of - // our children have stopped, so update state accordingly. + // if we get here then all children have stopped // + memoryBarrier (); m_childrenStopped = true; + onChildrenStopped (journal); - // Notify derived class that children have stopped. - onChildrenStopped (); - - // Block until this stoppable stops. First we do a timed wait of 1 second, and - // if that times out we report to the Journal and then do an infinite wait. + // Now block on this Stoppable. // bool const timedOut (! m_stoppedEvent.wait (1 * 1000)); // milliseconds if (timedOut) { - stream << "Waiting for '" << m_name << "' to stop"; + journal.warning << "Waiting for '" << m_name << "' to stop"; m_stoppedEvent.wait (); } // once we get here, we know the stoppable has stopped. m_stopped = true; } + +//------------------------------------------------------------------------------ + +RootStoppable::RootStoppable (char const* name) + : Stoppable (name, *this) +{ +} + +RootStoppable::~RootStoppable () +{ +} + +bool RootStoppable::isStopping() const +{ + return m_calledStopAsync.get() != 0; +} + +void RootStoppable::prepare (Journal journal) +{ + if (! m_prepared.compareAndSetBool (1, 0)) + { + journal.warning << "Stoppable::prepare called again"; + return; + } + + prepareRecursive (journal); +} + +void RootStoppable::start (Journal journal) +{ + // Courtesy call to prepare. + if (m_prepared.compareAndSetBool (1, 0)) + prepareRecursive (journal); + + if (! m_started.compareAndSetBool (1, 0)) + { + journal.warning << "Stoppable::start called again"; + return; + } + + startRecursive (journal); +} + +void RootStoppable::stop (Journal journal) +{ + if (! m_calledStop.compareAndSetBool (1, 0)) + { + journal.warning << "Stoppable::stop called again"; + return; + } + + stopAsync (journal); + stopRecursive (journal); +} + +void RootStoppable::stopAsync (Journal journal) +{ + if (! m_calledStopAsync.compareAndSetBool (1, 0)) + return; + + stopAsyncRecursive (journal); +} diff --git a/modules/beast_core/thread/Stoppable.h b/modules/beast_core/thread/Stoppable.h index 5aa2cb1f78..f7f96d982c 100644 --- a/modules/beast_core/thread/Stoppable.h +++ b/modules/beast_core/thread/Stoppable.h @@ -20,16 +20,66 @@ #ifndef BEAST_CORE_STOPPABLE_H_INCLUDED #define BEAST_CORE_STOPPABLE_H_INCLUDED -/** Provides an interface for stopping. +class RootStoppable; + +/** Provides an interface for starting and stopping. + + A common method of structuring server or peer to peer code is to isolate + conceptual portions of functionality into individual classes, aggregated + into some larger "application" or "core" object which holds all the parts. + Frequently, these components are dependent on each other in unavoidably + complex ways. They also often use threads and perform asynchronous i/o + operations involving sockets or other operating system objects. The process + of starting and stopping such a system can be complex. This interface + provides a set of behaviors for ensuring that the start and stop of a + composite application-style object is well defined. + + Upon the initialization of the composite object these steps are peformed: + + 1. Construct sub-components. + + These are all typically derived from Stoppable. There can be a deep + hierarchy: Stoppable objects may themselves have Stoppable child + objects. This captures the relationship of dependencies. + + 2. prepare() + + Because some components may depend on others, preparatory steps require + that all objects be first constructed. The prepare step calls all + Stoppable objects in the tree starting from the leaves and working up + to the root. In this stage we are guaranteed that all objects have been + constructed and are in a well-defined state. + + 3. onPrepare() + + This override is called for all Stoppable objects in the hierarchy + during the prepare stage. Objects are called from the bottom up. + It is guaranteed that all child Stoppable objects have already been + prepared when this is called. + + 4. start() + + At this point all sub-components have been constructed and prepared, + so it should be safe for them to be started. While some Stoppable + objects may do nothing in their start function, others will start + threads or call asynchronous i/o initiating functions like timers or + sockets. + + 5. onStart() + + This override is called for all Stoppable objects in the hierarchy + during the start stage. Objects are called from the bottom up. + It is guaranteed that all child Stoppable objects have already been + started when this is called. This is the sequence of events involved in stopping: - 1. stopAsync() [optional] + 6. stopAsync() [optional] This notifies the root Stoppable and all its children that a stop is requested. - 2. stop() + 7. stop() This first calls stopAsync(), and then blocks on each child Stoppable in the in the tree from the bottom up, until the Stoppable indicates it has @@ -37,21 +87,21 @@ when some external signal indicates that the process should stop. For example, an RPC 'stop' command, or a SIGINT POSIX signal. - 3. onStop() + 8. onStop() This override is called for the root Stoppable and all its children when stopAsync() is called. Derived classes should cancel pending I/O and timers, signal that threads should exit, queue cleanup jobs, and perform any other necessary final actions in preparation for exit. - 4. onChildrenStopped() + 9. onChildrenStopped() This override is called when all the children have stopped. This informs the Stoppable that there should not be any more dependents making calls into its member functions. A Stoppable that has no children will still have this function called. - 5. stopped() + 10. stopped() The derived class calls this function to inform the Stoppable API that it has completed the stop. This unblocks the caller of stop(). @@ -102,85 +152,44 @@ @note A Stoppable may not be restarted. */ +/** @{ */ class Stoppable { -public: - /** Create the stoppable. - A stoppable without a parent is a root stoppable. - @param name A name used in log output. - @param parent Optional parent of this stoppable. - */ - /** @{ */ - Stoppable (char const* name, Stoppable& parent); - explicit Stoppable (char const* name, Stoppable* parent = nullptr); - /** @} */ +protected: + Stoppable (char const* name, RootStoppable& root); - /** Destroy the stoppable. - Undefined behavior results if the object is not stopped first. - Stoppable objects should not be created and destroyed dynamically during - the process lifetime. Rather, the set of stoppables should be static and - well-defined after initialization. If the set of domain-specific objects - which need to stop is dynamic, use a single parent Stoppable to manage - those objects. For example, make an HTTP server implementation a - Stoppable, rather than each of its active connections. - */ +public: + /** Create the Stoppable. */ + Stoppable (char const* name, Stoppable& parent); + + /** Destroy the Stoppable. */ virtual ~Stoppable (); - /** Notify a root stoppable and children to stop, and block until stopped. - Has no effect if the stoppable was already notified. - This blocks until the stoppable and all of its children have stopped. - @param stream An optional Journal::Stream on which to log progress. + /** Returns `true` if the stoppable should stop. */ + bool isStopping () const; - Thread safety: - Safe to call from any thread not associated with a Stoppable. - */ - void stop (Journal::Stream stream = Journal::Stream()); + /** Returns `true` if the requested stop has completed. */ + bool isStopped () const; - /** Notify a root stoppable and children to stop, without waiting. - Has no effect if the stoppable was already notified. + /** Returns `true` if all children have stopped. */ + bool areChildrenStopped () const; - Thread safety: - Safe to call from any thread at any time. - */ - void stopAsync (); - - /** Returns `true` if the stoppable should stop. - Call from the derived class to determine if a long-running operation - should be canceled. This is not appropriate for either threads, or - asynchronous I/O. For threads, use the thread-specific facilities - available to inform the thread that it should exit. For asynchronous - I/O, cancel all pending operations inside the onStop override. - @see onStop - - Thread safety: - Safe to call from any thread at any time. - */ - bool isStopping (); - - /** Returns `true` if the stoppable has completed its stop. - Thread safety: - Safe to call from any thread at any time. - */ - bool isStopped (); - - /** Returns `true` if all children have stopped. - For stoppables without children, this returns `true` immediately - after a stop notification is received. - - Thread safety: - Safe to call from any thread at any time. - */ - bool areChildrenStopped (); - - /** Called by derived classes to indicate that the stoppable has stopped. - The derived class must call this either after isStopping returns `true`, - or when onStop is called, or else the call to stop will never unblock. - - Thread safety: - Safe to call from any thread at any time. - */ + /** Called by derived classes to indicate that the stoppable has stopped. */ void stopped (); + /** Override called during preparation. + Since all other Stoppable objects in the tree have already been + constructed, this provides an opportunity to perform initialization which + depends on calling into other Stoppable objects. + This call is made on the same thread that called prepare(). + The default implementation does nothing. + Guaranteed to only be called once. + */ + virtual void onPrepare (Journal journal); + + /** Override called during start. */ + virtual void onStart (Journal journal); + /** Override called when the stop notification is issued. The call is made on an unspecified, implementation-specific thread. @@ -202,7 +211,7 @@ public: Guaranteed only to be called once. Must be safe to call from any thread at any time. */ - virtual void onStop (); + virtual void onStop (Journal journal); /** Override called when all children have stopped. @@ -222,9 +231,11 @@ public: Guaranteed only to be called once. Must be safe to call from any thread at any time. */ - virtual void onChildrenStopped (); + virtual void onChildrenStopped (Journal journal); private: + friend class RootStoppable; + struct Child; typedef LockFreeStack Children; @@ -237,28 +248,70 @@ private: Stoppable* stoppable; }; - void stopAsyncRecursive (); - void stopRecursive (Journal::Stream stream); + void prepareRecursive (Journal journal); + void startRecursive (Journal journal); + void stopAsyncRecursive (Journal journal); + void stopRecursive (Journal journal); +protected: char const* m_name; - bool m_root; + RootStoppable& m_root; Child m_child; - Children m_children; - - // Flag that we called stop. This is for diagnostics. - bool m_calledStop; - - // Atomic flag to make sure we only call stopAsync once. - Atomic m_calledStopAsync; - - // Flag that this service stopped. Never goes back to false. bool volatile m_stopped; - - // Flag that all children have stopped (recursive). Never goes back to false. bool volatile m_childrenStopped; - - // stop() blocks on this event until stopped() is called. + Children m_children; WaitableEvent m_stoppedEvent; }; +//------------------------------------------------------------------------------ + +class RootStoppable : public Stoppable +{ +public: + explicit RootStoppable (char const* name); + + ~RootStoppable (); + + bool isStopping() const; + + /** Prepare all contained Stoppable objects. + This calls onPrepare for all Stoppable objects in the tree. + Calls made after the first have no effect. + Thread safety: + May be called from any thread. + */ + void prepare (Journal journal = Journal()); + + /** Start all contained Stoppable objects. + The default implementation does nothing. + Calls made after the first have no effect. + Thread safety: + May be called from any thread. + */ + void start (Journal journal = Journal()); + + /** Notify a root stoppable and children to stop, and block until stopped. + Has no effect if the stoppable was already notified. + This blocks until the stoppable and all of its children have stopped. + Thread safety: + Safe to call from any thread not associated with a Stoppable. + */ + void stop (Journal journal = Journal()); + + /** Notify a root stoppable and children to stop, without waiting. + Has no effect if the stoppable was already notified. + + Thread safety: + Safe to call from any thread at any time. + */ + void stopAsync (Journal journal = Journal()); + +private: + Atomic m_prepared; + Atomic m_started; + Atomic m_calledStop; + Atomic m_calledStopAsync; +}; +/** @} */ + #endif diff --git a/scripts/compile.sh b/scripts/compile.sh index 7b4eb18a2b..bb1f618d3d 100755 --- a/scripts/compile.sh +++ b/scripts/compile.sh @@ -23,7 +23,7 @@ for f in $1/*/*.cpp do { echo "Compilng '$f'" - g++ -xc++ -I$1/../scripts/ "$f" -c -o /dev/null - g++ -xc++ -std=c++11 -I$1/../scripts/ "$f" -c -o /dev/null + g++ -xc++ -I$1/../config/ "$f" -c -o /dev/null + g++ -xc++ -std=c++11 -I$1/../config/ "$f" -c -o /dev/null } done