1 // Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #ifndef STATE_MODEL_H
8 #define STATE_MODEL_H
9 
10 /// @file state_model.h This file defines the class StateModel.
11 
12 #include <exceptions/exceptions.h>
13 #include <util/labeled_value.h>
14 #include <boost/shared_ptr.hpp>
15 #include <functional>
16 #include <map>
17 #include <mutex>
18 #include <string>
19 
20 namespace isc {
21 namespace util {
22 
23 /// @brief Thrown if the state machine encounters a general error.
24 class StateModelError : public isc::Exception {
25 public:
StateModelError(const char * file,size_t line,const char * what)26     StateModelError(const char* file, size_t line, const char* what) :
27         isc::Exception(file, line, what) { };
28 };
29 
30 /// @brief Define an Event.
31 typedef LabeledValue Event;
32 
33 /// @brief Define Event pointer.
34 typedef LabeledValuePtr EventPtr;
35 
36 /// @brief Defines a pointer to an instance method for handling a state.
37 typedef std::function<void()> StateHandler;
38 
39 /// @brief State machine pausing modes.
40 ///
41 /// Supported modes are:
42 /// - always pause in the given state,
43 /// - never pause in the given state,
44 /// - pause upon first transition to the given state.
45 enum StatePausing {
46     STATE_PAUSE_ALWAYS,
47     STATE_PAUSE_NEVER,
48     STATE_PAUSE_ONCE
49 };
50 
51 /// @brief Defines a State within the State Model.
52 ///
53 /// This class provides the means to define a state within a set or dictionary
54 /// of states, and assign the state an handler method to execute the state's
55 /// actions.  It derives from LabeledValue which allows a set of states to be
56 /// keyed by integer constants.
57 ///
58 /// Because a state model can be paused in selected states, this class also
59 /// provides the means for specifying a pausing mode and for checking whether
60 /// the state model should be paused when entering this state.
61 class State : public LabeledValue {
62 public:
63     /// @brief Constructor
64     ///
65     /// @param value is the numeric value of the state
66     /// @param label is the text label to assign to the state
67     /// @param handler is the bound instance method which handles the state's
68     /// action.
69     /// @param state_pausing pausing mode selected for the given state. The
70     /// default value is @c STATE_PAUSE_NEVER.
71     ///
72     /// A typical invocation might look this:
73     ///
74     /// @code
75     ///     State(SOME_INT_VAL, "SOME_INT_VAL",
76     ///            std::bind(&StateModelDerivation::someHandler, this));
77     /// @endcode
78     ///
79     /// @throw StateModelError if label is null or blank.
80     State(const int value, const std::string& label, StateHandler handler,
81           const StatePausing& state_pausing = STATE_PAUSE_NEVER);
82 
83     /// @brief Destructor
84     virtual ~State();
85 
86     /// @brief Invokes the State's handler.
87     void run();
88 
89     /// @brief Indicates if the state model should pause upon entering
90     /// this state.
91     ///
92     /// It modifies the @c was_paused_ flag if the state model should
93     /// pause. That way, it keeps track of visits in this particular state,
94     /// making it possible to pause only upon the first transition to the
95     /// state when @c STATE_PAUSE_ONCE mode is used.
96     bool shouldPause();
97 
98 private:
99     /// @brief Bound instance method pointer to the state's handler method.
100     StateHandler handler_;
101 
102     /// @brief Specifies selected pausing mode for a state.
103     StatePausing pausing_;
104 
105     /// @brief Indicates if the state machine was already paused in this
106     /// state.
107     bool was_paused_;
108 };
109 
110 /// @brief Defines a shared pointer to a State.
111 typedef boost::shared_ptr<State> StatePtr;
112 
113 /// @brief Implements a unique set or dictionary of states.
114 ///
115 /// This class provides the means to construct and access a unique set of
116 /// states.  This provide the ability to validate state values, look up their
117 /// text labels, and their handlers.
118 class StateSet : public LabeledValueSet {
119 public:
120     /// @brief Constructor
121     StateSet();
122 
123     /// @brief Destructor
124     virtual ~StateSet();
125 
126     /// @brief Adds a state definition to the set of states.
127     ///
128     /// @param value is the numeric value of the state
129     /// @param label is the text label to assign to the state
130     /// @param handler is the bound instance method which handles the state's
131     /// @param state_pausing state pausing mode for the given state.
132     ///
133     /// @throw StateModelError if the value is already defined in the set, or
134     /// if the label is null or blank.
135     void add(const int value, const std::string& label, StateHandler handler,
136              const StatePausing& state_pausing);
137 
138     /// @brief Fetches a state for the given value.
139     ///
140     /// @param value the numeric value of the state desired
141     ///
142     /// @return A constant pointer the State found.
143     /// Note, this relies on dynamic cast and cannot return a pointer reference.
144     ///
145     /// @throw StateModelError if the value is undefined.
146     const StatePtr getState(int value);
147 };
148 
149 /// @brief Implements a finite state machine.
150 ///
151 /// StateModel is an abstract class that provides the structure and mechanics
152 /// of a basic finite state machine.
153 ///
154 /// The state model implementation used is a very basic approach. The model
155 /// uses numeric constants to identify events and states, and maintains
156 /// dictionaries of defined events and states.  Event and state definitions
157 /// include a text label for logging purposes.  Additionally, each state
158 /// definition includes a state handler. State handlers are methods which
159 /// implement the actions that need to occur when the model is "in" a given
160 /// state.  The implementation provides methods to add entries to and verify
161 /// the contents of both dictionaries.
162 ///
163 /// During model execution, the following context is tracked:
164 ///
165 /// * current state - The current state of the model
166 /// * previous state -  The state the model was in prior to the current state
167 /// * next event - The next event to be consumed
168 /// * last event - The event most recently consumed
169 ///
170 /// When invoked, a state handler determines what it should do based upon the
171 /// next event including what the next state and event should be. In other
172 /// words the state transition knowledge is distributed among the state
173 /// handlers rather than encapsulated in some form of state transition table.
174 ///
175 /// Events "posted" from within the state handlers are "internally" triggered
176 /// events.  Events "posted" from outside the state model, such as through
177 /// the invocation of a callback are "externally" triggered.
178 ///
179 /// StateModel defines two states:
180 ///
181 /// * NEW_ST - State that a model is in following instantiation. It remains in
182 /// this state until model execution has begun.
183 /// * END_ST - State that a model is in once it has reached its conclusion.
184 ///
185 /// and the following events:
186 ///
187 /// * START_EVT - Event used to start model execution.
188 /// * NOP_EVT - Event used to signify that the model stop and wait for an
189 /// external event, such as the completion of an asynchronous IO operation.
190 /// * END_EVT - Event used to trigger a normal conclusion of the model. This
191 /// means only that the model was traversed from start to finish, without any
192 /// model violations (i.e. invalid state, event, or transition) or uncaught
193 /// exceptions.
194 /// * FAIL_EVT - Event to trigger an abnormal conclusion of the model. This
195 /// event is posted internally when model execution fails due to a model
196 /// violation or uncaught exception.  It signifies that the model has reached
197 /// an inoperable condition.
198 ///
199 /// Derivations add their own states and events appropriate for their state
200 /// model.  Note that NEW_ST and END_ST do not support handlers.  No work can
201 /// be done (events consumed) prior to starting the model nor can work be done
202 /// once the model has ended.
203 ///
204 /// Model execution consists of iteratively invoking the state handler
205 /// indicated by the current state which should consume the next event. As the
206 /// handlers post events and/or change the state, the model is traversed. The
207 /// loop stops whenever the model cannot continue without an externally
208 /// triggered event or when it has reached its final state.  In the case of
209 /// the former, the loop may be re-entered upon arrival of the external event.
210 ///
211 /// This loop is implemented in the runModel method.  This method accepts an
212 /// event as argument which it "posts" as the next event.  It then retrieves the
213 /// handler for the current state from the handler map and invokes it. runModel
214 /// repeats this process until either a NOP_EVT posts or the state changes
215 /// to END_ST.  In other words each invocation of runModel causes the model to
216 /// be traversed from the current state until it must wait or ends.
217 ///
218 /// Re-entering the "loop" upon the occurrence of an external event is done by
219 /// invoking runModel with the appropriate event.  As before, runModel will
220 /// loop until either the NOP_EVT occurs or until the model reaches its end.
221 ///
222 /// A StateModel (derivation) is in the NEW_ST when constructed and remains
223 /// there until it has been "started".  Starting the model is done by invoking
224 /// the startModel method which accepts a state as a parameter.  This parameter
225 /// specifies the "start" state and it becomes the current state.
226 
227 /// The first task undertaken by startModel is to initialize and verify the
228 /// the event and state dictionaries.  The following virtual methods are
229 /// provided for this:
230 ///
231 /// * defineEvents - define events
232 /// * verifyEvents - verifies that the expected events are defined
233 /// * defineStates - defines states
234 /// * verifyStates - verifies that the expected states are defined
235 ///
236 /// The concept behind the verify methods is to provide an initial sanity
237 /// check of the dictionaries.  This should help avoid using undefined event
238 /// or state values accidentally.
239 ///
240 /// These methods are intended to be implemented by each "layer" in a StateModel
241 /// derivation hierarchy.  This allows each layer to define additional events
242 /// and states.
243 ///
244 /// Once the dictionaries have been properly initialized, the startModel method
245 /// invokes runModel with an event of START_EVT.  From this point forward and
246 /// until the model reaches the END_ST or fails, it is considered to be
247 /// "running".  If the model encounters a NOP_EVT then it is "running" and
248 /// "waiting".   If the model reaches END_ST with an END_EVT it is considered
249 /// "done".  If the  model fails (END_ST with a FAILED_EVT) it is considered
250 /// "done" and "failed".  There are several boolean status methods which may
251 /// be used to check these conditions.
252 /// Once the model has been started, defining new events or new states is
253 /// illegal. It is possible to call startModel only once.
254 ///
255 /// To progress from one state to the another, state handlers invoke use
256 /// the method, transition.  This method accepts a state and an event as
257 /// parameters.  These values become the current state and the next event
258 /// respectively.  This has the effect of entering the given state having posted
259 /// the given event.  The postEvent method may be used to post a new event
260 /// to the current state.
261 ///
262 /// Bringing the model to a normal end is done by invoking the endModel method
263 /// which transitions the model to END_ST with END_EVT.  Bringing the model to
264 /// an abnormal end is done via the abortModel method, which transitions the
265 /// model to END_ST with FAILED_EVT.
266 ///
267 /// The model can be paused in the selected states. The states in which the
268 /// state model should pause (always or only once) are determined within the
269 /// @c StateModel::defineStates method. The state handlers can check whether
270 /// the state machine is paused or not by calling @c StateModel::isModelPaused
271 /// and act accordingy. Typically, the state handler would simply post the
272 /// @c NOP_EVT when it finds that the state model is paused. The model
273 /// remains paused until @c StateModel::unpauseModel is called.
274 class StateModel {
275 public:
276 
277     //@{ States common to all models.
278     /// @brief State that a state model is in immediately after construction.
279     static const int NEW_ST = 0;
280 
281     /// @brief Final state, all the state model has reached its conclusion.
282     static const int END_ST = 1;
283 
284     /// @brief Value at which custom states in a derived class should begin.
285     static const int SM_DERIVED_STATE_MIN = 11;
286     //@}
287 
288     //@{ Events common to all state models.
289     /// @brief Signifies that no event has occurred.
290     /// This is event used to interrupt the event loop to allow waiting for
291     /// an IO event or when there is no more work to be done.
292     static const int NOP_EVT = 0;
293 
294     /// @brief Event issued to start the model execution.
295     static const int START_EVT = 1;
296 
297     /// @brief Event issued to end the model execution.
298     static const int END_EVT = 2;
299 
300     /// @brief Event issued to abort the model execution.
301     static const int FAIL_EVT = 3;
302 
303     /// @brief Value at which custom events in a derived class should begin.
304     static const int SM_DERIVED_EVENT_MIN = 11;
305     //@}
306 
307     /// @brief Constructor
308     StateModel();
309 
310     /// @brief Destructor
311     virtual ~StateModel();
312 
313     /// @brief Begins execution of the model.
314     ///
315     /// This method invokes initDictionaries method to initialize the event
316     /// and state dictionaries and then starts the model execution setting
317     /// the current state to the given start state, and the event to START_EVT.
318     /// This method can be called only once to start the state model.
319     ///
320     /// @param start_state is the state in which to begin execution.
321     ///
322     /// @throw StateModelError or others indirectly, as this method calls
323     /// dictionary define and verify methods.
324     void startModel(const int start_state);
325 
326     /// @brief Processes events through the state model
327     ///
328     /// This method implements the state model "execution loop".  It uses
329     /// the given event as the next event to process and begins invoking
330     /// the state handler for the current state.   As described above, the
331     /// invoked state handler consumes the next event and then determines the
332     /// next event and the current state as required to implement the business
333     /// logic. The method continues to loop until the next event posted is
334     /// NOP_EVT or the model ends.
335     ///
336     /// Any exception thrown during the loop is caught, logged, and the
337     /// model is aborted with a FAIL_EVT.  The derivation's state
338     /// model is expected to account for any possible errors so any that
339     /// escape are treated as unrecoverable.
340     ///
341     /// @note This method is made virtual for the unit tests which require
342     /// customizations allowing for more control over the state model
343     /// execution.
344     ///
345     /// @param event is the next event to process
346     ///
347     /// This method is guaranteed not to throw.
348     virtual void runModel(unsigned int event);
349 
350     /// @brief Conducts a normal transition to the end of the model.
351     ///
352     /// This method posts an END_EVT and sets the current state to END_ST.
353     /// It should be called by any state handler in the model from which
354     /// an exit leads to the model end.  In other words, if the transition
355     /// out of a particular state is to the end of the model, that state's
356     /// handler should call endModel.
357     void endModel();
358 
359     /// @brief Unpauses state model.
360     void unpauseModel();
361 
362     /// @brief An empty state handler.
363     ///
364     /// This method is primarily used to permit special states, NEW_ST and
365     /// END_ST to be included in the state dictionary.  Currently it is an
366     /// empty method.
367     void nopStateHandler();
368 
369 protected:
370 
371     /// @brief Initializes the event and state dictionaries.
372     ///
373     /// This method invokes the define and verify methods for both events and
374     /// states to initialize their respective dictionaries.
375     /// This method can be called only once to initialize the state model.
376     ///
377     /// @throw StateModelError or others indirectly, as this method calls
378     /// dictionary define and verify methods.
379     void initDictionaries();
380 
381     /// @brief Populates the set of events.
382     ///
383     /// This method is used to construct the set of valid events. Each class
384     /// within a StateModel derivation hierarchy uses this method to add any
385     /// events it defines to the set.  Each derivation's implementation must
386     /// also call its superclass's implementation.  This allows each class
387     /// within the hierarchy to make contributions to the set of defined
388     /// events. Implementations use the method, defineEvent(), to add event
389     /// definitions.  An example of the derivation's implementation follows:
390     ///
391     /// @code
392     /// void StateModelDerivation::defineEvents() {
393     ///     // Call the superclass implementation.
394     ///     StateModelDerivation::defineEvents();
395     ///
396     ///     // Add the events defined by the derivation.
397     ///     defineEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
398     ///     defineEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
399     ///     :
400     /// }
401     /// @endcode
402     ///
403     /// This method is called in a thread safe context from
404     /// @ref initDictionaries.
405     virtual void defineEvents();
406 
407     /// @brief Adds an event value and associated label to the set of events.
408     ///
409     /// This method is called in a thread safe context from @ref defineEvents.
410     ///
411     /// @param value is the numeric value of the event
412     /// @param label is the text label of the event used in log messages and
413     /// exceptions.
414     ///
415     /// @throw StateModelError if the model has already been started, if
416     /// the value is already defined, or if the label is empty.
417     void defineEvent(unsigned int value, const std::string& label);
418 
419     /// @brief Fetches the event referred to by value.
420     ///
421     /// This method is called in a thread safe context from @ref verifyEvents.
422     ///
423     /// @param value is the numeric value of the event desired.
424     ///
425     /// @return returns a constant pointer reference to the event if found
426     ///
427     /// @throw StateModelError if the event is not defined.
428     const EventPtr& getEvent(unsigned int value);
429 
430     /// @brief Validates the contents of the set of events.
431     ///
432     /// This method is invoked immediately after the defineEvents method and
433     /// is used to verify that all the required events are defined.  If the
434     /// event set is determined to be invalid this method should throw a
435     /// StateModelError.  As with the defineEvents method, each class within
436     /// a StateModel derivation hierarchy must supply an implementation
437     /// which calls its superclass's implementation as well as verifying any
438     /// events added by the derivation.  Validating an event is accomplished
439     /// by simply attempting to fetch an event by its value from the event set.
440     /// An example of the derivation's implementation follows:
441     ///
442     /// @code
443     /// void StateModelDerivation::verifyEvents() {
444     ///     // Call the superclass implementation.
445     ///     StateModelDerivation::verifyEvents();
446     ///
447     ///     // Verify the events defined by the derivation.
448     ///     getEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
449     ///     getEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
450     ///     :
451     /// }
452     /// @endcode
453     ///
454     /// This method is called in a thread safe context from
455     /// @ref initDictionaries.
456     virtual void verifyEvents();
457 
458     /// @brief Populates the set of states.
459     ///
460     /// This method is used to construct the set of valid states. Each class
461     /// within a StateModel derivation hierarchy uses this method to add any
462     /// states it defines to the set.  Each derivation's implementation must
463     /// also call its superclass's implementation.  This allows each class
464     /// within the hierarchy to make contributions to the set of defined
465     /// states. Implementations use the method, defineState(), to add state
466     /// definitions.  An example of the derivation's implementation follows:
467     ///
468     /// @code
469     /// void StateModelDerivation::defineStates() {
470     ///     // Call the superclass implementation.
471     ///     StateModelDerivation::defineStates();
472     ///
473     ///     // Add the states defined by the derivation.
474     ///     defineState(SOME_ST, "SOME_ST",
475     ///                 std::bind(&StateModelDerivation::someHandler, this));
476     ///     :
477     /// }
478     /// @endcode
479     ///
480     /// This method is called in a thread safe context from
481     /// @ref initDictionaries.
482     virtual void defineStates();
483 
484     /// @brief Adds an state value and associated label to the set of states.
485     ///
486     /// This method is called in a thread safe context from @ref defineStates.
487     ///
488     /// @param value is the numeric value of the state
489     /// @param label is the text label of the state used in log messages and
490     /// exceptions.
491     /// @param handler is the bound instance method which implements the state's
492     /// actions.
493     /// @param state_pausing pausing mode selected for the given state. The
494     /// default value is @c STATE_PAUSE_NEVER.
495     ///
496     /// @throw StateModelError if the model has already been started, if
497     /// the value is already defined, or if the label is empty.
498     void defineState(unsigned int value, const std::string& label,
499                      StateHandler handler,
500                      const StatePausing& state_pausing = STATE_PAUSE_NEVER);
501 
502     /// @brief Fetches the state referred to by value.
503     ///
504     /// @param value is the numeric value of the state desired.
505     ///
506     /// @return returns a constant pointer to the state if found
507     ///
508     /// @throw StateModelError if the state is not defined.
509     const StatePtr getState(unsigned int value);
510 
511     /// @brief Validates the contents of the set of states.
512     ///
513     /// This method is invoked immediately after the defineStates method and
514     /// is used to verify that all the required states are defined.  If the
515     /// state set is determined to be invalid this method should throw a
516     /// StateModelError.  As with the defineStates method, each class within
517     /// a StateModel derivation hierarchy must supply an implementation
518     /// which calls its superclass's implementation as well as verifying any
519     /// states added by the derivation.  Validating an state is accomplished
520     /// by simply attempting to fetch the state by its value from the state set.
521     /// An example of the derivation's implementation follows:
522     ///
523     /// @code
524     /// void StateModelDerivation::verifyStates() {
525     ///     // Call the superclass implementation.
526     ///     StateModelDerivation::verifyStates();
527     ///
528     ///     // Verify the states defined by the derivation.
529     ///     getState(SOME_CUSTOM_EVT_2);
530     ///     :
531     /// }
532     /// @endcode
533     ///
534     /// This method is called in a thread safe context from
535     /// @ref initDictionaries.
536     virtual void verifyStates();
537 
538     /// @brief Handler for fatal model execution errors.
539     ///
540     /// This method is called when an unexpected error renders during
541     /// model execution, such as a state handler throwing an exception.
542     /// It provides derivations an opportunity to act accordingly by setting
543     /// the appropriate status or taking other remedial action.   This allows
544     /// the model execution loop to remain exception safe.  This default
545     /// implementation does nothing.
546     ///
547     /// @param explanation text detailing the error and state machine context
548     virtual void onModelFailure(const std::string& explanation);
549 
550     /// @brief Sets up the model to transition into given state with a given
551     /// event.
552     ///
553     /// This updates the model's notion of the current state and the next
554     /// event to process.  State handlers use this method to move from one state
555     /// to the next.
556     ///
557     /// @param state the new value to assign to the current state.
558     /// @param event the new value to assign to the next event.
559     ///
560     /// @throw StateModelError if the state is invalid.
561     void transition(unsigned int state, unsigned int event);
562 
563     /// @brief Aborts model execution.
564     ///
565     /// This method posts a FAILED_EVT and sets the current state to END_ST.
566     /// It is called internally when a model violation occurs. Violations are
567     /// any sort of inconsistency such as attempting to reference an invalid
568     /// state, or if the next event is not valid for the current state, or a
569     /// state handler throws an uncaught exception.
570     ///
571     /// @param explanation is text detailing the reason for aborting.
572     void abortModel(const std::string& explanation);
573 
574     /// @brief Sets the current state to the given state value.
575     ///
576     /// This updates the model's notion of the current state and is the
577     /// state whose handler will be executed on the next iteration of the run
578     /// loop.  This is intended primarily for internal use and testing. It is
579     /// unlikely that transitioning to a new state without a new event is of
580     /// much use.
581     ///
582     /// @param state the new value to assign to the current state.
583     ///
584     /// @throw StateModelError if the state is invalid.
585     void setState(unsigned int state);
586 
587     /// @brief Sets the next event to the given event value.
588     ///
589     /// This updates the model's notion of the next event and is the
590     /// event that will be passed into the current state's handler on the next
591     /// iteration of the run loop.
592     ///
593     /// @param event the numeric event value to post as the next event.
594     ///
595     /// @throw StateModelError if the event is undefined
596     void postNextEvent(unsigned int event);
597 
598     /// @brief Checks if on entry flag is true.
599     ///
600     /// This method acts as a one-shot test of whether or not  the model is
601     /// transitioning into a new state.  It returns true if the on-entry flag
602     /// is true upon entering this method and will set the flag false prior
603     /// to exit.  It may be used within state handlers to perform steps that
604     /// should only occur upon entry into the state.
605     ///
606     /// @return true if the on entry flag is true, false otherwise.
607     bool doOnEntry();
608 
609     /// @brief Checks if on exit flag is true.
610     ///
611     /// This method acts as a one-shot test of whether or not the model is
612     /// transitioning out of the current state.  It returns true if the
613     /// on-exit flag is true upon entering this method and will set the flag
614     /// false prior to exiting.  It may be used within state handlers to perform
615     /// steps that should only occur upon exiting out of the current state.
616     ///
617     /// @return true if the on entry flag is true, false otherwise.
618     bool doOnExit();
619 
620 public:
621 
622     /// @brief Fetches the model's current state.
623     ///
624     /// This returns the model's notion of the current state. It is the
625     /// state whose handler will be executed on the next iteration of the run
626     /// loop.
627     ///
628     /// @return An unsigned int representing the current state.
629     unsigned int getCurrState() const;
630 
631     /// @brief Fetches the model's previous state.
632     ///
633     /// @return An unsigned int representing the previous state.
634     unsigned int getPrevState() const;
635 
636     /// @brief Fetches the model's last event.
637     ///
638     /// @return An unsigned int representing the last event.
639     unsigned int getLastEvent() const;
640 
641     /// @brief Fetches the model's next event.
642     ///
643     /// This returns the model's notion of the next event. It is the
644     /// event that will be passed into the current state's handler on the next
645     /// iteration of the run loop.
646     ///
647     /// @return An unsigned int representing the next event.
648     unsigned int getNextEvent() const;
649 
650     /// @brief Returns whether or not the model is new.
651     ///
652     /// @return Boolean true if the model has not been started.
653     bool isModelNew() const;
654 
655     /// @brief Returns whether or not the model is running.
656     ///
657     /// @return Boolean true if the model has been started but has not yet
658     /// ended.
659     bool isModelRunning() const;
660 
661     /// @brief Returns whether or not the model is waiting.
662     ///
663     /// @return Boolean true if the model is running but is waiting for an
664     /// external event for resumption.
665     bool isModelWaiting() const;
666 
667     /// @brief Returns whether or not the model has finished execution.
668     ///
669     /// @return Boolean true if the model has reached the END_ST.
670     bool isModelDone() const;
671 
672     /// @brief Returns whether or not the model is paused.
673     ///
674     /// @return Boolean true if the model is paused, false otherwise.
675     bool isModelPaused() const;
676 
677     /// @brief Returns whether or not the model failed.
678     ///
679     /// @return Boolean true if the model has reached the END_ST and the last
680     /// event indicates a model violation, FAILED_EVT.
681     bool didModelFail() const;
682 
683     /// @brief Fetches the label associated with an event value.
684     ///
685     /// @param event is the numeric event value for which the label is desired.
686     ///
687     /// @return Returns a string containing the event label or
688     /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
689     std::string getEventLabel(const int event) const;
690 
691     /// @brief Fetches the label associated with an state value.
692     ///
693     /// @param state is the numeric state value for which the label is desired.
694     ///
695     /// @return Returns a const char* containing the state label or
696     /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
697     std::string getStateLabel(const int state) const;
698 
699     /// @brief Convenience method which returns a string rendition of the
700     /// current state and next event.
701     ///
702     /// The string will be of the form:
703     ///
704     ///   current state: [ {state} {label} ] next event: [ {event} {label} ]
705     ///
706     /// @return Returns a std::string of the format described above.
707     std::string getContextStr() const;
708 
709     /// @brief Convenience method which returns a string rendition of the
710     /// previous state and last event.
711     ///
712     /// The string will be of the form:
713     ///
714     ///   previous state: [ {state} {label} ] last event: [ {event} {label} ]
715     ///
716     /// @return Returns a std::string of the format described above.
717     std::string getPrevContextStr() const;
718 
719 protected:
720 
721     /// @brief Fetches the state referred to by value.
722     ///
723     /// This method should be called in a thread safe context.
724     ///
725     /// @param value is the numeric value of the state desired.
726     ///
727     /// @return returns a constant pointer to the state if found
728     ///
729     /// @throw StateModelError if the state is not defined.
730     const StatePtr getStateInternal(unsigned int value);
731 
732 private:
733 
734     /// @brief Sets the current state to the given state value.
735     ///
736     /// This updates the model's notion of the current state and is the
737     /// state whose handler will be executed on the next iteration of the run
738     /// loop.  This is intended primarily for internal use and testing. It is
739     /// unlikely that transitioning to a new state without a new event is of
740     /// much use.
741     /// This method should be called in a thread safe context.
742     ///
743     /// @param state the new value to assign to the current state.
744     ///
745     /// @throw StateModelError if the state is invalid.
746     void setStateInternal(unsigned int state);
747 
748     /// @brief Sets the next event to the given event value.
749     ///
750     /// This updates the model's notion of the next event and is the
751     /// event that will be passed into the current state's handler on the next
752     /// iteration of the run loop.
753     /// This method should be called in a thread safe context.
754     ///
755     /// @param event the numeric event value to post as the next event.
756     ///
757     /// @throw StateModelError if the event is undefined
758     void postNextEventInternal(unsigned int event);
759 
760     /// @brief Returns whether or not the model is new.
761     ///
762     /// This method should be called in a thread safe context.
763     ///
764     /// @return Boolean true if the model has not been started.
765     bool isModelNewInternal() const;
766 
767     /// @brief Fetches the label associated with an event value.
768     ///
769     /// This method should be called in a thread safe context.
770     ///
771     /// @param event is the numeric event value for which the label is desired.
772     ///
773     /// @return Returns a string containing the event label or
774     /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
775     std::string getEventLabelInternal(const int event) const;
776 
777     /// @brief Fetches the label associated with an state value.
778     ///
779     /// This method should be called in a thread safe context.
780     ///
781     /// @param state is the numeric state value for which the label is desired.
782     ///
783     /// @return Returns a const char* containing the state label or
784     /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
785     std::string getStateLabelInternal(const int state) const;
786 
787     /// @brief Convenience method which returns a string rendition of the
788     /// current state and next event.
789     ///
790     /// The string will be of the form:
791     ///
792     ///   current state: [ {state} {label} ] next event: [ {event} {label} ]
793     ///
794     /// This method should be called in a thread safe context.
795     ///
796     /// @return Returns a std::string of the format described above.
797     std::string getContextStrInternal() const;
798 
799     /// @brief Convenience method which returns a string rendition of the
800     /// previous state and last event.
801     ///
802     /// The string will be of the form:
803     ///
804     ///   previous state: [ {state} {label} ] last event: [ {event} {label} ]
805     ///
806     /// This method should be called in a thread safe context.
807     ///
808     /// @return Returns a std::string of the format described above.
809     std::string getPrevContextStrInternal() const;
810 
811     /// @brief The dictionary of valid events.
812     LabeledValueSet events_;
813 
814     /// @brief The dictionary of valid states.
815     StateSet states_;
816 
817     /// @brief Indicates if the event and state dictionaries have been initted.
818     bool dictionaries_initted_;
819 
820     /// @brief The current state within the model's state model.
821     unsigned int curr_state_;
822 
823     /// @brief The previous state within the model's state model.
824     unsigned int prev_state_;
825 
826     /// @brief The event last processed by the model.
827     unsigned int last_event_;
828 
829     /// @brief The event the model should process next.
830     unsigned int next_event_;
831 
832     /// @brief Indicates if state entry logic should be executed.
833     bool on_entry_flag_;
834 
835     /// @brief Indicates if state exit logic should be executed.
836     bool on_exit_flag_;
837 
838     /// @brief Indicates if the state model is paused.
839     bool paused_;
840 
841     /// @brief Protects against concurrent transitions.
842     boost::shared_ptr<std::mutex> mutex_;
843 };
844 
845 /// @brief Defines a pointer to a StateModel.
846 typedef boost::shared_ptr<StateModel> StateModelPtr;
847 
848 } // namespace isc::util
849 } // namespace isc
850 #endif
851