summaryrefslogtreecommitdiffstats
path: root/src/lib/util/state_model.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/state_model.h')
-rw-r--r--src/lib/util/state_model.h850
1 files changed, 850 insertions, 0 deletions
diff --git a/src/lib/util/state_model.h b/src/lib/util/state_model.h
new file mode 100644
index 0000000..6ef5238
--- /dev/null
+++ b/src/lib/util/state_model.h
@@ -0,0 +1,850 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef STATE_MODEL_H
+#define STATE_MODEL_H
+
+/// @file state_model.h This file defines the class StateModel.
+
+#include <exceptions/exceptions.h>
+#include <util/labeled_value.h>
+#include <boost/shared_ptr.hpp>
+#include <functional>
+#include <map>
+#include <mutex>
+#include <string>
+
+namespace isc {
+namespace util {
+
+/// @brief Thrown if the state machine encounters a general error.
+class StateModelError : public isc::Exception {
+public:
+ StateModelError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Define an Event.
+typedef LabeledValue Event;
+
+/// @brief Define Event pointer.
+typedef LabeledValuePtr EventPtr;
+
+/// @brief Defines a pointer to an instance method for handling a state.
+typedef std::function<void()> StateHandler;
+
+/// @brief State machine pausing modes.
+///
+/// Supported modes are:
+/// - always pause in the given state,
+/// - never pause in the given state,
+/// - pause upon first transition to the given state.
+enum StatePausing {
+ STATE_PAUSE_ALWAYS,
+ STATE_PAUSE_NEVER,
+ STATE_PAUSE_ONCE
+};
+
+/// @brief Defines a State within the State Model.
+///
+/// This class provides the means to define a state within a set or dictionary
+/// of states, and assign the state an handler method to execute the state's
+/// actions. It derives from LabeledValue which allows a set of states to be
+/// keyed by integer constants.
+///
+/// Because a state model can be paused in selected states, this class also
+/// provides the means for specifying a pausing mode and for checking whether
+/// the state model should be paused when entering this state.
+class State : public LabeledValue {
+public:
+ /// @brief Constructor
+ ///
+ /// @param value is the numeric value of the state
+ /// @param label is the text label to assign to the state
+ /// @param handler is the bound instance method which handles the state's
+ /// action.
+ /// @param state_pausing pausing mode selected for the given state. The
+ /// default value is @c STATE_PAUSE_NEVER.
+ ///
+ /// A typical invocation might look this:
+ ///
+ /// @code
+ /// State(SOME_INT_VAL, "SOME_INT_VAL",
+ /// std::bind(&StateModelDerivation::someHandler, this));
+ /// @endcode
+ ///
+ /// @throw StateModelError if label is null or blank.
+ State(const int value, const std::string& label, StateHandler handler,
+ const StatePausing& state_pausing = STATE_PAUSE_NEVER);
+
+ /// @brief Destructor
+ virtual ~State();
+
+ /// @brief Invokes the State's handler.
+ void run();
+
+ /// @brief Indicates if the state model should pause upon entering
+ /// this state.
+ ///
+ /// It modifies the @c was_paused_ flag if the state model should
+ /// pause. That way, it keeps track of visits in this particular state,
+ /// making it possible to pause only upon the first transition to the
+ /// state when @c STATE_PAUSE_ONCE mode is used.
+ bool shouldPause();
+
+private:
+ /// @brief Bound instance method pointer to the state's handler method.
+ StateHandler handler_;
+
+ /// @brief Specifies selected pausing mode for a state.
+ StatePausing pausing_;
+
+ /// @brief Indicates if the state machine was already paused in this
+ /// state.
+ bool was_paused_;
+};
+
+/// @brief Defines a shared pointer to a State.
+typedef boost::shared_ptr<State> StatePtr;
+
+/// @brief Implements a unique set or dictionary of states.
+///
+/// This class provides the means to construct and access a unique set of
+/// states. This provide the ability to validate state values, look up their
+/// text labels, and their handlers.
+class StateSet : public LabeledValueSet {
+public:
+ /// @brief Constructor
+ StateSet();
+
+ /// @brief Destructor
+ virtual ~StateSet();
+
+ /// @brief Adds a state definition to the set of states.
+ ///
+ /// @param value is the numeric value of the state
+ /// @param label is the text label to assign to the state
+ /// @param handler is the bound instance method which handles the state's
+ /// @param state_pausing state pausing mode for the given state.
+ ///
+ /// @throw StateModelError if the value is already defined in the set, or
+ /// if the label is null or blank.
+ void add(const int value, const std::string& label, StateHandler handler,
+ const StatePausing& state_pausing);
+
+ /// @brief Fetches a state for the given value.
+ ///
+ /// @param value the numeric value of the state desired
+ ///
+ /// @return A constant pointer the State found.
+ /// Note, this relies on dynamic cast and cannot return a pointer reference.
+ ///
+ /// @throw StateModelError if the value is undefined.
+ const StatePtr getState(int value);
+};
+
+/// @brief Implements a finite state machine.
+///
+/// StateModel is an abstract class that provides the structure and mechanics
+/// of a basic finite state machine.
+///
+/// The state model implementation used is a very basic approach. The model
+/// uses numeric constants to identify events and states, and maintains
+/// dictionaries of defined events and states. Event and state definitions
+/// include a text label for logging purposes. Additionally, each state
+/// definition includes a state handler. State handlers are methods which
+/// implement the actions that need to occur when the model is "in" a given
+/// state. The implementation provides methods to add entries to and verify
+/// the contents of both dictionaries.
+///
+/// During model execution, the following context is tracked:
+///
+/// * current state - The current state of the model
+/// * previous state - The state the model was in prior to the current state
+/// * next event - The next event to be consumed
+/// * last event - The event most recently consumed
+///
+/// When invoked, a state handler determines what it should do based upon the
+/// next event including what the next state and event should be. In other
+/// words the state transition knowledge is distributed among the state
+/// handlers rather than encapsulated in some form of state transition table.
+///
+/// Events "posted" from within the state handlers are "internally" triggered
+/// events. Events "posted" from outside the state model, such as through
+/// the invocation of a callback are "externally" triggered.
+///
+/// StateModel defines two states:
+///
+/// * NEW_ST - State that a model is in following instantiation. It remains in
+/// this state until model execution has begun.
+/// * END_ST - State that a model is in once it has reached its conclusion.
+///
+/// and the following events:
+///
+/// * START_EVT - Event used to start model execution.
+/// * NOP_EVT - Event used to signify that the model stop and wait for an
+/// external event, such as the completion of an asynchronous IO operation.
+/// * END_EVT - Event used to trigger a normal conclusion of the model. This
+/// means only that the model was traversed from start to finish, without any
+/// model violations (i.e. invalid state, event, or transition) or uncaught
+/// exceptions.
+/// * FAIL_EVT - Event to trigger an abnormal conclusion of the model. This
+/// event is posted internally when model execution fails due to a model
+/// violation or uncaught exception. It signifies that the model has reached
+/// an inoperable condition.
+///
+/// Derivations add their own states and events appropriate for their state
+/// model. Note that NEW_ST and END_ST do not support handlers. No work can
+/// be done (events consumed) prior to starting the model nor can work be done
+/// once the model has ended.
+///
+/// Model execution consists of iteratively invoking the state handler
+/// indicated by the current state which should consume the next event. As the
+/// handlers post events and/or change the state, the model is traversed. The
+/// loop stops whenever the model cannot continue without an externally
+/// triggered event or when it has reached its final state. In the case of
+/// the former, the loop may be re-entered upon arrival of the external event.
+///
+/// This loop is implemented in the runModel method. This method accepts an
+/// event as argument which it "posts" as the next event. It then retrieves the
+/// handler for the current state from the handler map and invokes it. runModel
+/// repeats this process until either a NOP_EVT posts or the state changes
+/// to END_ST. In other words each invocation of runModel causes the model to
+/// be traversed from the current state until it must wait or ends.
+///
+/// Re-entering the "loop" upon the occurrence of an external event is done by
+/// invoking runModel with the appropriate event. As before, runModel will
+/// loop until either the NOP_EVT occurs or until the model reaches its end.
+///
+/// A StateModel (derivation) is in the NEW_ST when constructed and remains
+/// there until it has been "started". Starting the model is done by invoking
+/// the startModel method which accepts a state as a parameter. This parameter
+/// specifies the "start" state and it becomes the current state.
+
+/// The first task undertaken by startModel is to initialize and verify the
+/// the event and state dictionaries. The following virtual methods are
+/// provided for this:
+///
+/// * defineEvents - define events
+/// * verifyEvents - verifies that the expected events are defined
+/// * defineStates - defines states
+/// * verifyStates - verifies that the expected states are defined
+///
+/// The concept behind the verify methods is to provide an initial sanity
+/// check of the dictionaries. This should help avoid using undefined event
+/// or state values accidentally.
+///
+/// These methods are intended to be implemented by each "layer" in a StateModel
+/// derivation hierarchy. This allows each layer to define additional events
+/// and states.
+///
+/// Once the dictionaries have been properly initialized, the startModel method
+/// invokes runModel with an event of START_EVT. From this point forward and
+/// until the model reaches the END_ST or fails, it is considered to be
+/// "running". If the model encounters a NOP_EVT then it is "running" and
+/// "waiting". If the model reaches END_ST with an END_EVT it is considered
+/// "done". If the model fails (END_ST with a FAILED_EVT) it is considered
+/// "done" and "failed". There are several boolean status methods which may
+/// be used to check these conditions.
+/// Once the model has been started, defining new events or new states is
+/// illegal. It is possible to call startModel only once.
+///
+/// To progress from one state to the another, state handlers invoke use
+/// the method, transition. This method accepts a state and an event as
+/// parameters. These values become the current state and the next event
+/// respectively. This has the effect of entering the given state having posted
+/// the given event. The postEvent method may be used to post a new event
+/// to the current state.
+///
+/// Bringing the model to a normal end is done by invoking the endModel method
+/// which transitions the model to END_ST with END_EVT. Bringing the model to
+/// an abnormal end is done via the abortModel method, which transitions the
+/// model to END_ST with FAILED_EVT.
+///
+/// The model can be paused in the selected states. The states in which the
+/// state model should pause (always or only once) are determined within the
+/// @c StateModel::defineStates method. The state handlers can check whether
+/// the state machine is paused or not by calling @c StateModel::isModelPaused
+/// and act accordingy. Typically, the state handler would simply post the
+/// @c NOP_EVT when it finds that the state model is paused. The model
+/// remains paused until @c StateModel::unpauseModel is called.
+class StateModel {
+public:
+
+ //@{ States common to all models.
+ /// @brief State that a state model is in immediately after construction.
+ static const int NEW_ST = 0;
+
+ /// @brief Final state, all the state model has reached its conclusion.
+ static const int END_ST = 1;
+
+ /// @brief Value at which custom states in a derived class should begin.
+ static const int SM_DERIVED_STATE_MIN = 11;
+ //@}
+
+ //@{ Events common to all state models.
+ /// @brief Signifies that no event has occurred.
+ /// This is event used to interrupt the event loop to allow waiting for
+ /// an IO event or when there is no more work to be done.
+ static const int NOP_EVT = 0;
+
+ /// @brief Event issued to start the model execution.
+ static const int START_EVT = 1;
+
+ /// @brief Event issued to end the model execution.
+ static const int END_EVT = 2;
+
+ /// @brief Event issued to abort the model execution.
+ static const int FAIL_EVT = 3;
+
+ /// @brief Value at which custom events in a derived class should begin.
+ static const int SM_DERIVED_EVENT_MIN = 11;
+ //@}
+
+ /// @brief Constructor
+ StateModel();
+
+ /// @brief Destructor
+ virtual ~StateModel();
+
+ /// @brief Begins execution of the model.
+ ///
+ /// This method invokes initDictionaries method to initialize the event
+ /// and state dictionaries and then starts the model execution setting
+ /// the current state to the given start state, and the event to START_EVT.
+ /// This method can be called only once to start the state model.
+ ///
+ /// @param start_state is the state in which to begin execution.
+ ///
+ /// @throw StateModelError or others indirectly, as this method calls
+ /// dictionary define and verify methods.
+ void startModel(const int start_state);
+
+ /// @brief Processes events through the state model
+ ///
+ /// This method implements the state model "execution loop". It uses
+ /// the given event as the next event to process and begins invoking
+ /// the state handler for the current state. As described above, the
+ /// invoked state handler consumes the next event and then determines the
+ /// next event and the current state as required to implement the business
+ /// logic. The method continues to loop until the next event posted is
+ /// NOP_EVT or the model ends.
+ ///
+ /// Any exception thrown during the loop is caught, logged, and the
+ /// model is aborted with a FAIL_EVT. The derivation's state
+ /// model is expected to account for any possible errors so any that
+ /// escape are treated as unrecoverable.
+ ///
+ /// @note This method is made virtual for the unit tests which require
+ /// customizations allowing for more control over the state model
+ /// execution.
+ ///
+ /// @param event is the next event to process
+ ///
+ /// This method is guaranteed not to throw.
+ virtual void runModel(unsigned int event);
+
+ /// @brief Conducts a normal transition to the end of the model.
+ ///
+ /// This method posts an END_EVT and sets the current state to END_ST.
+ /// It should be called by any state handler in the model from which
+ /// an exit leads to the model end. In other words, if the transition
+ /// out of a particular state is to the end of the model, that state's
+ /// handler should call endModel.
+ void endModel();
+
+ /// @brief Unpauses state model.
+ void unpauseModel();
+
+ /// @brief An empty state handler.
+ ///
+ /// This method is primarily used to permit special states, NEW_ST and
+ /// END_ST to be included in the state dictionary. Currently it is an
+ /// empty method.
+ void nopStateHandler();
+
+protected:
+
+ /// @brief Initializes the event and state dictionaries.
+ ///
+ /// This method invokes the define and verify methods for both events and
+ /// states to initialize their respective dictionaries.
+ /// This method can be called only once to initialize the state model.
+ ///
+ /// @throw StateModelError or others indirectly, as this method calls
+ /// dictionary define and verify methods.
+ void initDictionaries();
+
+ /// @brief Populates the set of events.
+ ///
+ /// This method is used to construct the set of valid events. Each class
+ /// within a StateModel derivation hierarchy uses this method to add any
+ /// events it defines to the set. Each derivation's implementation must
+ /// also call its superclass's implementation. This allows each class
+ /// within the hierarchy to make contributions to the set of defined
+ /// events. Implementations use the method, defineEvent(), to add event
+ /// definitions. An example of the derivation's implementation follows:
+ ///
+ /// @code
+ /// void StateModelDerivation::defineEvents() {
+ /// // Call the superclass implementation.
+ /// StateModelDerivation::defineEvents();
+ ///
+ /// // Add the events defined by the derivation.
+ /// defineEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
+ /// defineEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
+ /// :
+ /// }
+ /// @endcode
+ ///
+ /// This method is called in a thread safe context from
+ /// @ref initDictionaries.
+ virtual void defineEvents();
+
+ /// @brief Adds an event value and associated label to the set of events.
+ ///
+ /// This method is called in a thread safe context from @ref defineEvents.
+ ///
+ /// @param value is the numeric value of the event
+ /// @param label is the text label of the event used in log messages and
+ /// exceptions.
+ ///
+ /// @throw StateModelError if the model has already been started, if
+ /// the value is already defined, or if the label is empty.
+ void defineEvent(unsigned int value, const std::string& label);
+
+ /// @brief Fetches the event referred to by value.
+ ///
+ /// This method is called in a thread safe context from @ref verifyEvents.
+ ///
+ /// @param value is the numeric value of the event desired.
+ ///
+ /// @return returns a constant pointer reference to the event if found
+ ///
+ /// @throw StateModelError if the event is not defined.
+ const EventPtr& getEvent(unsigned int value);
+
+ /// @brief Validates the contents of the set of events.
+ ///
+ /// This method is invoked immediately after the defineEvents method and
+ /// is used to verify that all the required events are defined. If the
+ /// event set is determined to be invalid this method should throw a
+ /// StateModelError. As with the defineEvents method, each class within
+ /// a StateModel derivation hierarchy must supply an implementation
+ /// which calls its superclass's implementation as well as verifying any
+ /// events added by the derivation. Validating an event is accomplished
+ /// by simply attempting to fetch an event by its value from the event set.
+ /// An example of the derivation's implementation follows:
+ ///
+ /// @code
+ /// void StateModelDerivation::verifyEvents() {
+ /// // Call the superclass implementation.
+ /// StateModelDerivation::verifyEvents();
+ ///
+ /// // Verify the events defined by the derivation.
+ /// getEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
+ /// getEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
+ /// :
+ /// }
+ /// @endcode
+ ///
+ /// This method is called in a thread safe context from
+ /// @ref initDictionaries.
+ virtual void verifyEvents();
+
+ /// @brief Populates the set of states.
+ ///
+ /// This method is used to construct the set of valid states. Each class
+ /// within a StateModel derivation hierarchy uses this method to add any
+ /// states it defines to the set. Each derivation's implementation must
+ /// also call its superclass's implementation. This allows each class
+ /// within the hierarchy to make contributions to the set of defined
+ /// states. Implementations use the method, defineState(), to add state
+ /// definitions. An example of the derivation's implementation follows:
+ ///
+ /// @code
+ /// void StateModelDerivation::defineStates() {
+ /// // Call the superclass implementation.
+ /// StateModelDerivation::defineStates();
+ ///
+ /// // Add the states defined by the derivation.
+ /// defineState(SOME_ST, "SOME_ST",
+ /// std::bind(&StateModelDerivation::someHandler, this));
+ /// :
+ /// }
+ /// @endcode
+ ///
+ /// This method is called in a thread safe context from
+ /// @ref initDictionaries.
+ virtual void defineStates();
+
+ /// @brief Adds an state value and associated label to the set of states.
+ ///
+ /// This method is called in a thread safe context from @ref defineStates.
+ ///
+ /// @param value is the numeric value of the state
+ /// @param label is the text label of the state used in log messages and
+ /// exceptions.
+ /// @param handler is the bound instance method which implements the state's
+ /// actions.
+ /// @param state_pausing pausing mode selected for the given state. The
+ /// default value is @c STATE_PAUSE_NEVER.
+ ///
+ /// @throw StateModelError if the model has already been started, if
+ /// the value is already defined, or if the label is empty.
+ void defineState(unsigned int value, const std::string& label,
+ StateHandler handler,
+ const StatePausing& state_pausing = STATE_PAUSE_NEVER);
+
+ /// @brief Fetches the state referred to by value.
+ ///
+ /// @param value is the numeric value of the state desired.
+ ///
+ /// @return returns a constant pointer to the state if found
+ ///
+ /// @throw StateModelError if the state is not defined.
+ const StatePtr getState(unsigned int value);
+
+ /// @brief Validates the contents of the set of states.
+ ///
+ /// This method is invoked immediately after the defineStates method and
+ /// is used to verify that all the required states are defined. If the
+ /// state set is determined to be invalid this method should throw a
+ /// StateModelError. As with the defineStates method, each class within
+ /// a StateModel derivation hierarchy must supply an implementation
+ /// which calls its superclass's implementation as well as verifying any
+ /// states added by the derivation. Validating an state is accomplished
+ /// by simply attempting to fetch the state by its value from the state set.
+ /// An example of the derivation's implementation follows:
+ ///
+ /// @code
+ /// void StateModelDerivation::verifyStates() {
+ /// // Call the superclass implementation.
+ /// StateModelDerivation::verifyStates();
+ ///
+ /// // Verify the states defined by the derivation.
+ /// getState(SOME_CUSTOM_EVT_2);
+ /// :
+ /// }
+ /// @endcode
+ ///
+ /// This method is called in a thread safe context from
+ /// @ref initDictionaries.
+ virtual void verifyStates();
+
+ /// @brief Handler for fatal model execution errors.
+ ///
+ /// This method is called when an unexpected error renders during
+ /// model execution, such as a state handler throwing an exception.
+ /// It provides derivations an opportunity to act accordingly by setting
+ /// the appropriate status or taking other remedial action. This allows
+ /// the model execution loop to remain exception safe. This default
+ /// implementation does nothing.
+ ///
+ /// @param explanation text detailing the error and state machine context
+ virtual void onModelFailure(const std::string& explanation);
+
+ /// @brief Sets up the model to transition into given state with a given
+ /// event.
+ ///
+ /// This updates the model's notion of the current state and the next
+ /// event to process. State handlers use this method to move from one state
+ /// to the next.
+ ///
+ /// @param state the new value to assign to the current state.
+ /// @param event the new value to assign to the next event.
+ ///
+ /// @throw StateModelError if the state is invalid.
+ void transition(unsigned int state, unsigned int event);
+
+ /// @brief Aborts model execution.
+ ///
+ /// This method posts a FAILED_EVT and sets the current state to END_ST.
+ /// It is called internally when a model violation occurs. Violations are
+ /// any sort of inconsistency such as attempting to reference an invalid
+ /// state, or if the next event is not valid for the current state, or a
+ /// state handler throws an uncaught exception.
+ ///
+ /// @param explanation is text detailing the reason for aborting.
+ void abortModel(const std::string& explanation);
+
+ /// @brief Sets the current state to the given state value.
+ ///
+ /// This updates the model's notion of the current state and is the
+ /// state whose handler will be executed on the next iteration of the run
+ /// loop. This is intended primarily for internal use and testing. It is
+ /// unlikely that transitioning to a new state without a new event is of
+ /// much use.
+ ///
+ /// @param state the new value to assign to the current state.
+ ///
+ /// @throw StateModelError if the state is invalid.
+ void setState(unsigned int state);
+
+ /// @brief Sets the next event to the given event value.
+ ///
+ /// This updates the model's notion of the next event and is the
+ /// event that will be passed into the current state's handler on the next
+ /// iteration of the run loop.
+ ///
+ /// @param event the numeric event value to post as the next event.
+ ///
+ /// @throw StateModelError if the event is undefined
+ void postNextEvent(unsigned int event);
+
+ /// @brief Checks if on entry flag is true.
+ ///
+ /// This method acts as a one-shot test of whether or not the model is
+ /// transitioning into a new state. It returns true if the on-entry flag
+ /// is true upon entering this method and will set the flag false prior
+ /// to exit. It may be used within state handlers to perform steps that
+ /// should only occur upon entry into the state.
+ ///
+ /// @return true if the on entry flag is true, false otherwise.
+ bool doOnEntry();
+
+ /// @brief Checks if on exit flag is true.
+ ///
+ /// This method acts as a one-shot test of whether or not the model is
+ /// transitioning out of the current state. It returns true if the
+ /// on-exit flag is true upon entering this method and will set the flag
+ /// false prior to exiting. It may be used within state handlers to perform
+ /// steps that should only occur upon exiting out of the current state.
+ ///
+ /// @return true if the on entry flag is true, false otherwise.
+ bool doOnExit();
+
+public:
+
+ /// @brief Fetches the model's current state.
+ ///
+ /// This returns the model's notion of the current state. It is the
+ /// state whose handler will be executed on the next iteration of the run
+ /// loop.
+ ///
+ /// @return An unsigned int representing the current state.
+ unsigned int getCurrState() const;
+
+ /// @brief Fetches the model's previous state.
+ ///
+ /// @return An unsigned int representing the previous state.
+ unsigned int getPrevState() const;
+
+ /// @brief Fetches the model's last event.
+ ///
+ /// @return An unsigned int representing the last event.
+ unsigned int getLastEvent() const;
+
+ /// @brief Fetches the model's next event.
+ ///
+ /// This returns the model's notion of the next event. It is the
+ /// event that will be passed into the current state's handler on the next
+ /// iteration of the run loop.
+ ///
+ /// @return An unsigned int representing the next event.
+ unsigned int getNextEvent() const;
+
+ /// @brief Returns whether or not the model is new.
+ ///
+ /// @return Boolean true if the model has not been started.
+ bool isModelNew() const;
+
+ /// @brief Returns whether or not the model is running.
+ ///
+ /// @return Boolean true if the model has been started but has not yet
+ /// ended.
+ bool isModelRunning() const;
+
+ /// @brief Returns whether or not the model is waiting.
+ ///
+ /// @return Boolean true if the model is running but is waiting for an
+ /// external event for resumption.
+ bool isModelWaiting() const;
+
+ /// @brief Returns whether or not the model has finished execution.
+ ///
+ /// @return Boolean true if the model has reached the END_ST.
+ bool isModelDone() const;
+
+ /// @brief Returns whether or not the model is paused.
+ ///
+ /// @return Boolean true if the model is paused, false otherwise.
+ bool isModelPaused() const;
+
+ /// @brief Returns whether or not the model failed.
+ ///
+ /// @return Boolean true if the model has reached the END_ST and the last
+ /// event indicates a model violation, FAILED_EVT.
+ bool didModelFail() const;
+
+ /// @brief Fetches the label associated with an event value.
+ ///
+ /// @param event is the numeric event value for which the label is desired.
+ ///
+ /// @return Returns a string containing the event label or
+ /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+ std::string getEventLabel(const int event) const;
+
+ /// @brief Fetches the label associated with an state value.
+ ///
+ /// @param state is the numeric state value for which the label is desired.
+ ///
+ /// @return Returns a const char* containing the state label or
+ /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+ std::string getStateLabel(const int state) const;
+
+ /// @brief Convenience method which returns a string rendition of the
+ /// current state and next event.
+ ///
+ /// The string will be of the form:
+ ///
+ /// current state: [ {state} {label} ] next event: [ {event} {label} ]
+ ///
+ /// @return Returns a std::string of the format described above.
+ std::string getContextStr() const;
+
+ /// @brief Convenience method which returns a string rendition of the
+ /// previous state and last event.
+ ///
+ /// The string will be of the form:
+ ///
+ /// previous state: [ {state} {label} ] last event: [ {event} {label} ]
+ ///
+ /// @return Returns a std::string of the format described above.
+ std::string getPrevContextStr() const;
+
+protected:
+
+ /// @brief Fetches the state referred to by value.
+ ///
+ /// This method should be called in a thread safe context.
+ ///
+ /// @param value is the numeric value of the state desired.
+ ///
+ /// @return returns a constant pointer to the state if found
+ ///
+ /// @throw StateModelError if the state is not defined.
+ const StatePtr getStateInternal(unsigned int value);
+
+private:
+
+ /// @brief Sets the current state to the given state value.
+ ///
+ /// This updates the model's notion of the current state and is the
+ /// state whose handler will be executed on the next iteration of the run
+ /// loop. This is intended primarily for internal use and testing. It is
+ /// unlikely that transitioning to a new state without a new event is of
+ /// much use.
+ /// This method should be called in a thread safe context.
+ ///
+ /// @param state the new value to assign to the current state.
+ ///
+ /// @throw StateModelError if the state is invalid.
+ void setStateInternal(unsigned int state);
+
+ /// @brief Sets the next event to the given event value.
+ ///
+ /// This updates the model's notion of the next event and is the
+ /// event that will be passed into the current state's handler on the next
+ /// iteration of the run loop.
+ /// This method should be called in a thread safe context.
+ ///
+ /// @param event the numeric event value to post as the next event.
+ ///
+ /// @throw StateModelError if the event is undefined
+ void postNextEventInternal(unsigned int event);
+
+ /// @brief Returns whether or not the model is new.
+ ///
+ /// This method should be called in a thread safe context.
+ ///
+ /// @return Boolean true if the model has not been started.
+ bool isModelNewInternal() const;
+
+ /// @brief Fetches the label associated with an event value.
+ ///
+ /// This method should be called in a thread safe context.
+ ///
+ /// @param event is the numeric event value for which the label is desired.
+ ///
+ /// @return Returns a string containing the event label or
+ /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+ std::string getEventLabelInternal(const int event) const;
+
+ /// @brief Fetches the label associated with an state value.
+ ///
+ /// This method should be called in a thread safe context.
+ ///
+ /// @param state is the numeric state value for which the label is desired.
+ ///
+ /// @return Returns a const char* containing the state label or
+ /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
+ std::string getStateLabelInternal(const int state) const;
+
+ /// @brief Convenience method which returns a string rendition of the
+ /// current state and next event.
+ ///
+ /// The string will be of the form:
+ ///
+ /// current state: [ {state} {label} ] next event: [ {event} {label} ]
+ ///
+ /// This method should be called in a thread safe context.
+ ///
+ /// @return Returns a std::string of the format described above.
+ std::string getContextStrInternal() const;
+
+ /// @brief Convenience method which returns a string rendition of the
+ /// previous state and last event.
+ ///
+ /// The string will be of the form:
+ ///
+ /// previous state: [ {state} {label} ] last event: [ {event} {label} ]
+ ///
+ /// This method should be called in a thread safe context.
+ ///
+ /// @return Returns a std::string of the format described above.
+ std::string getPrevContextStrInternal() const;
+
+ /// @brief The dictionary of valid events.
+ LabeledValueSet events_;
+
+ /// @brief The dictionary of valid states.
+ StateSet states_;
+
+ /// @brief Indicates if the event and state dictionaries have been initted.
+ bool dictionaries_initted_;
+
+ /// @brief The current state within the model's state model.
+ unsigned int curr_state_;
+
+ /// @brief The previous state within the model's state model.
+ unsigned int prev_state_;
+
+ /// @brief The event last processed by the model.
+ unsigned int last_event_;
+
+ /// @brief The event the model should process next.
+ unsigned int next_event_;
+
+ /// @brief Indicates if state entry logic should be executed.
+ bool on_entry_flag_;
+
+ /// @brief Indicates if state exit logic should be executed.
+ bool on_exit_flag_;
+
+ /// @brief Indicates if the state model is paused.
+ bool paused_;
+
+ /// @brief Protects against concurrent transitions.
+ boost::shared_ptr<std::mutex> mutex_;
+};
+
+/// @brief Defines a pointer to a StateModel.
+typedef boost::shared_ptr<StateModel> StateModelPtr;
+
+} // namespace isc::util
+} // namespace isc
+#endif