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