mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
276 lines
9.6 KiB
C++
276 lines
9.6 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
Copyright (c) 2011-2013, OpenCoin, Inc.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef RIPPLE_BASICS_SERVICE_H_INCLUDED
|
|
#define RIPPLE_BASICS_SERVICE_H_INCLUDED
|
|
|
|
/** Abstraction for organizing partitioned support code.
|
|
|
|
The main thing a service can do, is to stop. Once it stops it cannot be
|
|
reused, it can only be destroyed. This interface is used to coordinate
|
|
the complex activities required for a clean exit in the presence of
|
|
pending asynchronous i/o and multiple theads.
|
|
|
|
This is the sequence of events involved in stopping a service:
|
|
|
|
1. serviceStopAsync() [optional]
|
|
|
|
This notifies the Service and all its children that a stop is requested.
|
|
|
|
2. serviceStop ()
|
|
|
|
This first calls serviceStopAsync(), and then blocks on each Service
|
|
in the tree from the bottom up, until the Service indicates it has
|
|
stopped. This will usually be called from the main thread of execution
|
|
when some external signal indicates that the process should stop.
|
|
FOr example, an RPC 'stop' command, or a SIGINT POSIX signal.
|
|
|
|
3. onServiceStop ()
|
|
|
|
This is called for the root Service and all its children when a stop
|
|
is requested. Derived classes should cancel pending I/O and timers,
|
|
signal that threads should exit, queue cleanup jobs, and perform any
|
|
other necessary clean up in preparation for exit.
|
|
|
|
4. onServiceChildrenStopped ()
|
|
|
|
When all the children of a service have stopped, this will be called.
|
|
This informs the Service that there should not be any more dependents
|
|
making calls into the derived class member functions. A Service that
|
|
has no children will have this function called immediately.
|
|
|
|
5. serviceStopped ()
|
|
|
|
The derived class calls this function to inform the Service API that
|
|
it has completed the stop. This unblocks the caller of serviceStop().
|
|
|
|
For services which are only considered stopped when all of their children
|
|
have stopped, and their own internal logic indicates a stop, it will be
|
|
necessary to perform special actions in onServiceChildrenStopped(). The
|
|
funtion areServiceChildrenStopped() can be used after children have
|
|
stopped, but before the Service logic itself has stopped, to determine
|
|
if the stopped service logic is a true stop.
|
|
|
|
Pseudo code for this process is as follows:
|
|
|
|
@code
|
|
|
|
// Returns `true` if derived logic has stopped.
|
|
//
|
|
// When the logic stops, logicProcessingStop() is no longer called.
|
|
// If children are still active we need to wait until we get a
|
|
// notification that the children have stopped.
|
|
//
|
|
bool logicHasStopped ();
|
|
|
|
// Called when children have stopped
|
|
void onServiceChildrenStopped ()
|
|
{
|
|
// We have stopped when the derived logic stops and children stop.
|
|
if (logicHasStopped)
|
|
serviceStopped();
|
|
}
|
|
|
|
// derived-specific logic that executes periodically
|
|
void logicProcessingStep ()
|
|
{
|
|
// do the step
|
|
// ...
|
|
|
|
// now see if we've stopped
|
|
if (logicHasStopped() && areServiceChildrenStopped())
|
|
serviceStopped();
|
|
}
|
|
|
|
@endcode
|
|
*/
|
|
class Service
|
|
{
|
|
public:
|
|
/** Create a service.
|
|
Services are always created in a non-stopped state.
|
|
A service without a parent is a root service.
|
|
*/
|
|
/** @{ */
|
|
explicit Service (char const* name);
|
|
Service (char const* name, Service* parent);
|
|
Service (char const* name, Service& parent);
|
|
/** @} */
|
|
|
|
/** Destroy the service.
|
|
Undefined behavior results if the service is not first stopped.
|
|
|
|
In general, services are not allowed to be created and destroyed
|
|
dynamically. The set of services should be static at some point
|
|
after the initialization of the process. If you need a dynamic
|
|
service, consider having a static Service which marshals service
|
|
calls to a second custom interface.
|
|
*/
|
|
virtual ~Service ();
|
|
|
|
/** Returns the name of the service. */
|
|
char const* serviceName () const;
|
|
|
|
/** Notify a root service and its children to stop, and block until stopped.
|
|
If the service was already notified, it is not notified again.
|
|
The call blocks until the service and all of its children have stopped.
|
|
|
|
Thread safety:
|
|
Safe to call from any thread not associated with a Service.
|
|
This function may only be called once.
|
|
|
|
@param stream An optional Journal stream on which to log progress.
|
|
*/
|
|
void serviceStop (Journal::Stream stream = Journal::Stream());
|
|
|
|
/** Notify a root service and children to stop, without waiting.
|
|
If the service was already notified, it is not notified again.
|
|
While this is safe to call more than once, only the first call
|
|
has any effect.
|
|
|
|
Thread safety:
|
|
Safe to call from any thread at any time.
|
|
*/
|
|
void serviceStopAsync ();
|
|
|
|
/** Returns `true` if the service should stop.
|
|
Call from the derived class to determine if a long-running
|
|
operation should be canceled.
|
|
|
|
Note that 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 exi. For asynchronous I/O, cancel
|
|
all pending operations inside the onServiceStop overide.
|
|
|
|
Thread safety:
|
|
Safe to call from any thread at any time.
|
|
|
|
@see onServiceStop
|
|
*/
|
|
bool isServiceStopping ();
|
|
|
|
/** Returns `true` if the service has stopped.
|
|
|
|
Thread safety:
|
|
Safe to call from any thread at any time.
|
|
*/
|
|
bool isServiceStopped ();
|
|
|
|
/** Returns `true` if all children have stopped.
|
|
Children of services with no children are considered stopped if
|
|
the service has been notified.
|
|
|
|
Thread safety:
|
|
Safe to call from any thread at any time.
|
|
*/
|
|
bool areServiceChildrenStopped ();
|
|
|
|
/** Called by derived classes to indicate that the service has stopped.
|
|
The derived class must call this either after isServiceStopping
|
|
returns `true`, or when onServiceStop is called, or else a call
|
|
to serviceStop will never return.
|
|
|
|
Thread safety:
|
|
Safe to call from any thread at any time.
|
|
*/
|
|
void serviceStopped ();
|
|
|
|
/** Called when the stop notification is issued.
|
|
|
|
The call is made on an unspecified, implementation-specific thread.
|
|
onServiceStop and onServiceChildrenStopped will never be called
|
|
concurrently, across all Service objects descended from the same root,
|
|
inclusive of the root.
|
|
|
|
It is safe to call isServiceStopping, isServiceStopped, and
|
|
areServiceChildrenStopped from within this function; The values
|
|
returned will always be valid and never change during the callback.
|
|
|
|
The default implementation simply calls serviceStopped(). This is
|
|
applicable when the Service has a trivial stop operation (or no
|
|
stop operation), and we are merely using the Service API to position
|
|
it as a dependency of some parent service.
|
|
|
|
Thread safety:
|
|
May not block for long periods.
|
|
Guaranteed only to be called once.
|
|
Must be safe to call from any thread at any time.
|
|
*/
|
|
virtual void onServiceStop ();
|
|
|
|
/** Called when all children of a service have stopped.
|
|
|
|
The call is made on an unspecified, implementation-specific thread.
|
|
onServiceStop and onServiceChildrenStopped will never be called
|
|
concurrently, across all Service objects descended from the same root,
|
|
inclusive of the root.
|
|
|
|
It is safe to call isServiceStopping, isServiceStopped, and
|
|
areServiceChildrenStopped from within this function; The values
|
|
returned will always be valid and never change during the callback.
|
|
|
|
Thread safety:
|
|
May not block for long periods.
|
|
Guaranteed only to be called once.
|
|
Must be safe to call from any thread at any time.
|
|
*/
|
|
virtual void onServiceChildrenStopped ();
|
|
|
|
private:
|
|
struct Child;
|
|
typedef LockFreeStack <Child> Children;
|
|
|
|
struct Child : Children::Node
|
|
{
|
|
Child (Service* service_) : service (service_)
|
|
{
|
|
}
|
|
|
|
Service* service;
|
|
};
|
|
|
|
void stopAsyncRecursive ();
|
|
void stopRecursive (Journal::Stream stream);
|
|
|
|
char const* m_name;
|
|
bool m_root;
|
|
Child m_child;
|
|
Children m_children;
|
|
|
|
// Flag that we called serviceStop. This is for diagnostics.
|
|
bool m_calledServiceStop;
|
|
|
|
// Atomic flag to make sure we only call serviceStopAsync once.
|
|
Atomic <int> 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;
|
|
|
|
// serviceStop() blocks on this event until serviceStopped() is called.
|
|
WaitableEvent m_stoppedEvent;
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** A root Service with a short scope.
|
|
This Service takes care of stopping automatically, no additional
|
|
action is required.
|
|
*/
|
|
class ScopedService : public Service
|
|
{
|
|
public:
|
|
explicit ScopedService (char const* name);
|
|
~ScopedService ();
|
|
|
|
void onServiceStop ();
|
|
void onServiceChildrenStopped ();
|
|
};
|
|
|
|
#endif
|