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