Kea  2.1.7-git
state_model.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2020 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 #include <config.h>
8 #include <util/state_model.h>
9 #include <string>
10 
11 namespace isc {
12 namespace util {
13 
14 /********************************** State *******************************/
15 
16 State::State(const int value, const std::string& label, StateHandler handler,
17  const StatePausing& state_pausing)
18  : LabeledValue(value, label), handler_(handler), pausing_(state_pausing),
19  was_paused_(false) {
20 }
21 
23 }
24 
25 void
27  (handler_)();
28 }
29 
30 bool
32  if ((pausing_ == STATE_PAUSE_ALWAYS) ||
33  ((pausing_ == STATE_PAUSE_ONCE) && (!was_paused_))) {
34  was_paused_ = true;
35  return (true);
36  }
37  return (false);
38 }
39 
40 /********************************** StateSet *******************************/
41 
43 }
44 
46 }
47 
48 void
49 StateSet::add(const int value, const std::string& label, StateHandler handler,
50  const StatePausing& state_pausing) {
51  try {
52  LabeledValueSet::add(LabeledValuePtr(new State(value, label, handler,
53  state_pausing)));
54  } catch (const std::exception& ex) {
55  isc_throw(StateModelError, "StateSet: cannot add state :" << ex.what());
56  }
57 
58 }
59 
60 const StatePtr
61 StateSet::getState(int value) {
62  if (!isDefined(value)) {
63  isc_throw(StateModelError," StateSet: state is undefined");
64  }
65 
66  // Since we have to use dynamic casting, to get a state pointer
67  // we can't return a reference.
68  StatePtr state = boost::dynamic_pointer_cast<State>(get(value));
69  return (state);
70 }
71 
72 /********************************** StateModel *******************************/
73 
74 
75 // Common state model states
76 const int StateModel::NEW_ST;
77 const int StateModel::END_ST;
78 
80 
81 // Common state model events
82 const int StateModel::NOP_EVT;
83 const int StateModel::START_EVT;
84 const int StateModel::END_EVT;
85 const int StateModel::FAIL_EVT;
86 
88 
89 StateModel::StateModel() : events_(), states_(), dictionaries_initted_(false),
90  curr_state_(NEW_ST), prev_state_(NEW_ST),
91  last_event_(NOP_EVT), next_event_(NOP_EVT),
92  on_entry_flag_(false), on_exit_flag_(false),
93  paused_(false), mutex_(new std::mutex) {
94 }
95 
97 }
98 
99 void
100 StateModel::startModel(const int start_state) {
101  // Initialize dictionaries of events and states.
103 
104  // Set the current state to starting state and enter the run loop.
105  setState(start_state);
106 
107  // Start running the model.
109 }
110 
111 void
112 StateModel::runModel(unsigned int run_event) {
114  if (!dictionaries_initted_) {
115  abortModel("runModel invoked before model has been initialized");
116  }
117 
118  try {
119  // Seed the loop with the given event as the next to process.
120  postNextEvent(run_event);
121  do {
122  // Invoke the current state's handler. It should consume the
123  // next event, then determine what happens next by setting
124  // current state and/or the next event.
125  getState(curr_state_)->run();
126 
127  // Keep going until a handler sets next event to a NOP_EVT.
128  } while (!isModelDone() && getNextEvent() != NOP_EVT);
129  } catch (const std::exception& ex) {
130  // The model has suffered an unexpected exception. This constitutes
131  // a model violation and indicates a programmatic shortcoming.
132  // In theory, the model should account for all error scenarios and
133  // deal with them accordingly. Transition to END_ST with FAILED_EVT
134  // via abortModel.
135  abortModel(ex.what());
136  }
137 }
138 
139 void
141 }
142 
143 void
145  std::lock_guard<std::mutex> lock(*mutex_);
146  if (dictionaries_initted_) {
147  isc_throw(StateModelError, "Dictionaries already initialized");
148  }
149  // First let's build and verify the dictionary of events.
150  try {
151  defineEvents();
152  verifyEvents();
153  } catch (const std::exception& ex) {
154  isc_throw(StateModelError, "Event set is invalid: " << ex.what());
155  }
156 
157  // Next let's build and verify the dictionary of states.
158  try {
159  defineStates();
160  verifyStates();
161  } catch (const std::exception& ex) {
162  isc_throw(StateModelError, "State set is invalid: " << ex.what());
163  }
164 
165  // Record that we are good to go.
166  dictionaries_initted_ = true;
167 }
168 
169 void
170 StateModel::defineEvent(unsigned int event_value, const std::string& label) {
171  if (!isModelNewInternal()) {
172  // Don't allow for self-modifying models.
173  isc_throw(StateModelError, "Events may only be added to a new model."
174  << event_value << " - " << label);
175  }
176 
177  // Attempt to add the event to the set.
178  try {
179  events_.add(event_value, label);
180  } catch (const std::exception& ex) {
181  isc_throw(StateModelError, "Error adding event: " << ex.what());
182  }
183 }
184 
185 const EventPtr&
186 StateModel::getEvent(unsigned int event_value) {
187  if (!events_.isDefined(event_value)) {
189  "Event value is not defined:" << event_value);
190  }
191 
192  return (events_.get(event_value));
193 }
194 
195 void
196 StateModel::defineState(unsigned int state_value, const std::string& label,
197  StateHandler handler, const StatePausing& state_pausing) {
198  if (!isModelNewInternal()) {
199  // Don't allow for self-modifying maps.
200  isc_throw(StateModelError, "States may only be added to a new model."
201  << state_value << " - " << label);
202  }
203 
204  // Attempt to add the state to the set.
205  try {
206  states_.add(state_value, label, handler, state_pausing);
207  } catch (const std::exception& ex) {
208  isc_throw(StateModelError, "Error adding state: " << ex.what());
209  }
210 }
211 
212 const StatePtr
213 StateModel::getState(unsigned int state_value) {
214  std::lock_guard<std::mutex> lock(*mutex_);
215  return getStateInternal(state_value);
216 }
217 
218 const StatePtr
219 StateModel::getStateInternal(unsigned int state_value) {
220  if (!states_.isDefined(state_value)) {
222  "State value is not defined:" << state_value);
223  }
224 
225  return (states_.getState(state_value));
226 }
227 
228 void
230  defineEvent(NOP_EVT, "NOP_EVT");
231  defineEvent(START_EVT, "START_EVT");
232  defineEvent(END_EVT, "END_EVT");
233  defineEvent(FAIL_EVT, "FAIL_EVT");
234 }
235 
236 void
238  getEvent(NOP_EVT);
240  getEvent(END_EVT);
242 }
243 
244 void
246  defineState(NEW_ST, "NEW_ST",
247  std::bind(&StateModel::nopStateHandler, this));
248  defineState(END_ST, "END_ST",
249  std::bind(&StateModel::nopStateHandler, this));
250 }
251 
252 void
256 }
257 
258 void
259 StateModel::onModelFailure(const std::string&) {
260  // Empty implementation to make deriving classes simpler.
261 }
262 
263 void
264 StateModel::transition(unsigned int state, unsigned int event) {
265  std::lock_guard<std::mutex> lock(*mutex_);
266  setStateInternal(state);
267  postNextEventInternal(event);
268 }
269 
270 void
273 }
274 
275 void
277  std::lock_guard<std::mutex> lock(*mutex_);
278  paused_ = false;
279 }
280 
281 void
282 StateModel::abortModel(const std::string& explanation) {
284 
285  std::ostringstream stream ;
286  stream << explanation << " : " << getContextStr();
287  onModelFailure(stream.str());
288 }
289 
290 void
291 StateModel::setState(unsigned int state) {
292  std::lock_guard<std::mutex> lock(*mutex_);
293  setStateInternal(state);
294 }
295 
296 void
297 StateModel::setStateInternal(unsigned int state) {
298  if (state != END_ST && !states_.isDefined(state)) {
300  "Attempt to set state to an undefined value: " << state );
301  }
302 
303  prev_state_ = curr_state_;
304  curr_state_ = state;
305 
306  // Set the "do" flags if we are transitioning.
307  on_entry_flag_ = ((state != END_ST) && (prev_state_ != curr_state_));
308 
309  // At this time they are calculated the same way.
310  on_exit_flag_ = on_entry_flag_;
311 
312  // If we're entering the new state we need to see if we should
313  // pause the state model in this state.
314  if (on_entry_flag_ && !paused_ && getStateInternal(state)->shouldPause()) {
315  paused_ = true;
316  }
317 }
318 
319 void
320 StateModel::postNextEvent(unsigned int event_value) {
321  std::lock_guard<std::mutex> lock(*mutex_);
322  postNextEventInternal(event_value);
323 }
324 
325 void
326 StateModel::postNextEventInternal(unsigned int event_value) {
327  // Check for FAIL_EVT as special case of model error before events are
328  // defined.
329  if (event_value != FAIL_EVT && !events_.isDefined(event_value)) {
331  "Attempt to post an undefined event, value: " << event_value);
332  }
333 
334  last_event_ = next_event_;
335  next_event_ = event_value;
336 }
337 
338 bool
340  std::lock_guard<std::mutex> lock(*mutex_);
341  bool ret = on_entry_flag_;
342  on_entry_flag_ = false;
343  return (ret);
344 }
345 
346 bool
348  std::lock_guard<std::mutex> lock(*mutex_);
349  bool ret = on_exit_flag_;
350  on_exit_flag_ = false;
351  return (ret);
352 }
353 
354 unsigned int
356  std::lock_guard<std::mutex> lock(*mutex_);
357  return (curr_state_);
358 }
359 
360 unsigned int
362  std::lock_guard<std::mutex> lock(*mutex_);
363  return (prev_state_);
364 }
365 
366 unsigned int
368  std::lock_guard<std::mutex> lock(*mutex_);
369  return (last_event_);
370 }
371 
372 unsigned int
374  std::lock_guard<std::mutex> lock(*mutex_);
375  return (next_event_);
376 }
377 
378 bool
380  std::lock_guard<std::mutex> lock(*mutex_);
381  return isModelNewInternal();
382 }
383 
384 bool
385 StateModel::isModelNewInternal() const {
386  return (curr_state_ == NEW_ST);
387 }
388 
389 bool
391  std::lock_guard<std::mutex> lock(*mutex_);
392  return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST));
393 }
394 
395 bool
397  std::lock_guard<std::mutex> lock(*mutex_);
398  return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST) &&
399  (next_event_ == NOP_EVT));
400 }
401 
402 bool
404  std::lock_guard<std::mutex> lock(*mutex_);
405  return (curr_state_ == END_ST);
406 }
407 
408 bool
410  std::lock_guard<std::mutex> lock(*mutex_);
411  return ((curr_state_ == END_ST) && (next_event_ == FAIL_EVT));
412 }
413 
414 bool
416  std::lock_guard<std::mutex> lock(*mutex_);
417  return (paused_);
418 }
419 
420 std::string
421 StateModel::getStateLabel(const int state) const {
422  std::lock_guard<std::mutex> lock(*mutex_);
423  return getStateLabelInternal(state);
424 }
425 
426 std::string
427 StateModel::getStateLabelInternal(const int state) const {
428  return (states_.getLabel(state));
429 }
430 
431 std::string
432 StateModel::getEventLabel(const int event) const {
433  std::lock_guard<std::mutex> lock(*mutex_);
434  return getEventLabelInternal(event);
435 }
436 
437 std::string
438 StateModel::getEventLabelInternal(const int event) const {
439  return (events_.getLabel(event));
440 }
441 
442 std::string
444  std::lock_guard<std::mutex> lock(*mutex_);
445  std::ostringstream stream;
446  stream << "current state: [ "
447  << curr_state_ << " " << getStateLabelInternal(curr_state_)
448  << " ] next event: [ "
449  << next_event_ << " " << getEventLabelInternal(next_event_) << " ]";
450  return (stream.str());
451 }
452 
453 std::string
455  std::lock_guard<std::mutex> lock(*mutex_);
456  std::ostringstream stream;
457  stream << "previous state: [ "
458  << prev_state_ << " " << getStateLabelInternal(prev_state_)
459  << " ] last event: [ "
460  << next_event_ << " " << getEventLabelInternal(last_event_) << " ]";
461  return (stream.str());
462 }
463 
464 } // namespace isc::util
465 } // namespace isc
void defineState(unsigned int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing=STATE_PAUSE_NEVER)
Adds an state value and associated label to the set of states.
Definition: state_model.cc:196
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:292
bool shouldPause()
Indicates if the state model should pause upon entering this state.
Definition: state_model.cc:31
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:403
static const int FAIL_EVT
Event issued to abort the model execution.
Definition: state_model.h:301
void add(const int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing)
Adds a state definition to the set of states.
Definition: state_model.cc:49
StatePausing
State machine pausing modes.
Definition: state_model.h:45
std::string getContextStr() const
Convenience method which returns a string rendition of the current state and next event...
Definition: state_model.cc:443
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
Definition: state_model.cc:170
bool doOnExit()
Checks if on exit flag is true.
Definition: state_model.cc:347
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:432
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:213
std::function< void()> StateHandler
Defines a pointer to an instance method for handling a state.
Definition: state_model.h:37
bool isModelNew() const
Returns whether or not the model is new.
Definition: state_model.cc:379
static const int NEW_ST
State that a state model is in immediately after construction.
Definition: state_model.h:279
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:282
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
bool isModelWaiting() const
Returns whether or not the model is waiting.
Definition: state_model.cc:396
State(const int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing=STATE_PAUSE_NEVER)
Constructor.
Definition: state_model.cc:16
StateSet()
Constructor.
Definition: state_model.cc:42
std::string getPrevContextStr() const
Convenience method which returns a string rendition of the previous state and last event...
Definition: state_model.cc:454
STL namespace.
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:291
void run()
Invokes the State&#39;s handler.
Definition: state_model.cc:26
virtual void onModelFailure(const std::string &explanation)
Handler for fatal model execution errors.
Definition: state_model.cc:259
void endModel()
Conducts a normal transition to the end of the model.
Definition: state_model.cc:271
bool isDefined(const int value) const
Tests if the set contains an entry for the given value.
LabeledValuePtr EventPtr
Define Event pointer.
Definition: state_model.h:34
bool didModelFail() const
Returns whether or not the model failed.
Definition: state_model.cc:409
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
virtual void verifyStates()
Validates the contents of the set of states.
Definition: state_model.cc:253
unsigned int getLastEvent() const
Fetches the model&#39;s last event.
Definition: state_model.cc:367
virtual void runModel(unsigned int event)
Processes events through the state model.
Definition: state_model.cc:112
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< State > StatePtr
Defines a shared pointer to a State.
Definition: state_model.h:111
std::string getLabel(const int value) const
Fetches the label for the given value.
bool doOnEntry()
Checks if on entry flag is true.
Definition: state_model.cc:339
boost::shared_ptr< LabeledValue > LabeledValuePtr
Defines a shared pointer to a LabeledValue instance.
Definition: labeled_value.h:92
virtual ~State()
Destructor.
Definition: state_model.cc:22
Implements the concept of a constant value with a text label.
Definition: labeled_value.h:39
void nopStateHandler()
An empty state handler.
Definition: state_model.cc:140
unsigned int getNextEvent() const
Fetches the model&#39;s next event.
Definition: state_model.cc:373
static const int SM_DERIVED_EVENT_MIN
Value at which custom events in a derived class should begin.
Definition: state_model.h:304
const StatePtr getState(int value)
Fetches a state for the given value.
Definition: state_model.cc:61
bool isModelRunning() const
Returns whether or not the model is running.
Definition: state_model.cc:390
static const int SM_DERIVED_STATE_MIN
Value at which custom states in a derived class should begin.
Definition: state_model.h:285
unsigned int getCurrState() const
Fetches the model&#39;s current state.
Definition: state_model.cc:355
const LabeledValuePtr & get(int value)
Fetches a pointer to the entry associated with value.
void startModel(const int start_state)
Begins execution of the model.
Definition: state_model.cc:100
Defines a State within the State Model.
Definition: state_model.h:61
virtual ~StateSet()
Destructor.
Definition: state_model.cc:45
void unpauseModel()
Unpauses state model.
Definition: state_model.cc:276
Defines the logger used by the top-level component of kea-lfc.
virtual void defineStates()
Populates the set of states.
Definition: state_model.cc:245
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
Definition: state_model.cc:264
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
Definition: state_model.cc:421
unsigned int getPrevState() const
Fetches the model&#39;s previous state.
Definition: state_model.cc:361
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:144
const StatePtr getStateInternal(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:219
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:282
This file defines the class StateModel.
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:186
bool isModelPaused() const
Returns whether or not the model is paused.
Definition: state_model.cc:415
Thrown if the state machine encounters a general error.
Definition: state_model.h:24
StateModel()
Constructor.
Definition: state_model.cc:89
virtual void verifyEvents()
Validates the contents of the set of events.
Definition: state_model.cc:237
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:298
virtual ~StateModel()
Destructor.
Definition: state_model.cc:96
virtual void defineEvents()
Populates the set of events.
Definition: state_model.cc:229
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
void add(LabeledValuePtr entry)
Adds the given entry to the set.