diff options
Diffstat (limited to 'src/lib/util/state_model.h')
-rw-r--r-- | src/lib/util/state_model.h | 850 |
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 |