Kea: state_model.cc Source File

| 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
12namespace isc {
13namespace util {
14
15/********************************** State *******************************/
16
17State::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
23State::~State() {
24}
25
26void
27State::run() {
28 (handler_)();
29}
30
31bool
32State::shouldPause() {
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
43StateSet::StateSet() {
44}
45
46StateSet::~StateSet() {
47}
48
49void
50StateSet::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
61const StatePtr
62StateSet::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
77const int StateModel::NEW_ST;
78const int StateModel::END_ST;
79
80const int StateModel::SM_DERIVED_STATE_MIN;
81
82// Common state model events
83const int StateModel::NOP_EVT;
84const int StateModel::START_EVT;
85const int StateModel::END_EVT;
86const int StateModel::FAIL_EVT;
87
88const int StateModel::SM_DERIVED_EVENT_MIN;
89
90StateModel::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
97StateModel::~StateModel(){
98}
99
100void
101StateModel::startModel(const int start_state) {
102 // Initialize dictionaries of events and states.
103 initDictionaries();
104
105 // Set the current state to starting state and enter the run loop.
106 setState(start_state);
107
108 // Start running the model.
109 runModel(START_EVT);
110}
111
112void
113StateModel::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
141void
142StateModel::nopStateHandler() {
143}
144
145void
146StateModel::initDictionaries() {
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
167void
168StateModel::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
183const EventPtr&
184StateModel::getEvent(unsigned int event_value) {
185 if (!events_.isDefined(event_value)) {
186 isc_throw(StateModelError,
187 "Event value is not defined:" << event_value);
188 }
189
190 return (events_.get(event_value));
191}
192
193void
194StateModel::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
210const StatePtr
211StateModel::getState(unsigned int state_value) {
212 if (!states_.isDefined(state_value)) {
213 isc_throw(StateModelError,
214 "State value is not defined:" << state_value);
215 }
216
217 return (states_.getState(state_value));
218}
219
220void
221StateModel::defineEvents() {
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
228void
229StateModel::verifyEvents() {
230 getEvent(NOP_EVT);
231 getEvent(START_EVT);
232 getEvent(END_EVT);
233 getEvent(FAIL_EVT);
234}
235
236void
237StateModel::defineStates() {
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
244void
245StateModel::verifyStates() {
246 getState(NEW_ST);
247 getState(END_ST);
248}
249
250void
251StateModel::onModelFailure(const std::string&) {
252 // Empty implementation to make deriving classes simpler.
253}
254
255void
256StateModel::transition(unsigned int state, unsigned int event) {
257 setState(state);
258 postNextEvent(event);
259}
260
261void
262StateModel::endModel() {
263 transition(END_ST, END_EVT);
264}
265
266void
267StateModel::unpauseModel() {
268 paused_ = false;
269}
270
271void
272StateModel::abortModel(const std::string& explanation) {
273 transition(END_ST, FAIL_EVT);
274
275 std::ostringstream stream ;
276 stream << explanation << " : " << getContextStr();
277 onModelFailure(stream.str());
278}
279
280void
281StateModel::setState(unsigned int state) {
282 if (state != END_ST && !states_.isDefined(state)) {
283 isc_throw(StateModelError,
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
303void
304StateModel::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)) {
308 isc_throw(StateModelError,
309 "Attempt to post an undefined event, value: " << event_value);
310 }
311
312 last_event_ = next_event_;
313 next_event_ = event_value;
314}
315
316bool
317StateModel::doOnEntry() {
318 bool ret = on_entry_flag_;
319 on_entry_flag_ = false;
320 return (ret);
321}
322
323bool
324StateModel::doOnExit() {
325 bool ret = on_exit_flag_;
326 on_exit_flag_ = false;
327 return (ret);
328}
329
330unsigned int
331StateModel::getCurrState() const {
332 return (curr_state_);
333}
334
335unsigned int
336StateModel::getPrevState() const {
337 return (prev_state_);
338}
339
340unsigned int
341StateModel::getLastEvent() const {
342 return (last_event_);
343}
344
345unsigned int
346StateModel::getNextEvent() const {
347 return (next_event_);
348}
349bool
350StateModel::isModelNew() const {
351 return (curr_state_ == NEW_ST);
352}
353
354bool
355StateModel::isModelRunning() const {
356 return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST));
357}
358
359bool
360StateModel::isModelWaiting() const {
361 return (isModelRunning() && (next_event_ == NOP_EVT));
362}
363
364bool
365StateModel::isModelDone() const {
366 return (curr_state_ == END_ST);
367}
368
369bool
370StateModel::didModelFail() const {
371 return (isModelDone() && (next_event_ == FAIL_EVT));
372}
373
374bool
375StateModel::isModelPaused() const {
376 return (paused_);
377}
378
379std::string
380StateModel::getStateLabel(const int state) const {
381 return (states_.getLabel(state));
382}
383
384std::string
385StateModel::getEventLabel(const int event) const {
386 return (events_.getLabel(event));
387}
388
389std::string
390StateModel::getContextStr() const {
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
399std::string
400StateModel::getPrevContextStr() const {
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::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::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::LabeledValueSet::get
const LabeledValuePtr & get(int value)
Fetches a pointer to the entry associated with value.
Definition: labeled_value.cc:88
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::LabeledValueSet::add
void add(LabeledValuePtr entry)
Adds the given entry to the set.
Definition: labeled_value.cc:67
isc::util::LabeledValue
Implements the concept of a constant value with a text label.
Definition: labeled_value.h:39
isc::util::StateModelError
Thrown if the state machine encounters a general error.
Definition: state_model.h:23
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::isModelWaiting
bool isModelWaiting() const
Returns whether or not the model is waiting.
Definition: state_model.cc:360
isc::util::StateModel::initDictionaries
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:146
isc::util::StateModel::endModel
void endModel()
Conducts a normal transition to the end of the model.
Definition: state_model.cc:262
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::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::unpauseModel
void unpauseModel()
Unpauses state model.
Definition: state_model.cc:267
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::isModelDone
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:365
isc::util::StateModel::runModel
virtual void runModel(unsigned int event)
Processes events through the state model.
Definition: state_model.cc:113
isc::util::StateModel::isModelPaused
bool isModelPaused() const
Returns whether or not the model is paused.
Definition: state_model.cc:375
isc::util::StateModel::defineEvents
virtual void defineEvents()
Populates the set of events.
Definition: state_model.cc:221
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::verifyStates
virtual void verifyStates()
Validates the contents of the set of states.
Definition: state_model.cc:245
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::getState
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:211
isc::util::StateModel::isModelRunning
bool isModelRunning() const
Returns whether or not the model is running.
Definition: state_model.cc:355
isc::util::StateModel::doOnExit
bool doOnExit()
Checks if on exit flag is true.
Definition: state_model.cc:324
isc::util::StateModel::StateModel
StateModel()
Constructor.
Definition: state_model.cc:90
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::StateModel::getNextEvent
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:346
isc::util::StateModel::nopStateHandler
void nopStateHandler()
An empty state handler.
Definition: state_model.cc:142
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::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::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::StateModel::verifyEvents
virtual void verifyEvents()
Validates the contents of the set of events.
Definition: state_model.cc:229
isc::util::StateModel::END_EVT
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:295
isc::util::StateModel::doOnEntry
bool doOnEntry()
Checks if on entry flag is true.
Definition: state_model.cc:317
isc::util::StateModel::NOP_EVT
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:289
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
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::isModelNew
bool isModelNew() const
Returns whether or not the model is new.
Definition: state_model.cc:350
isc::util::StateModel::startModel
void startModel(const int start_state)
Begins execution of the model.
Definition: state_model.cc:101
isc::util::StateModel::defineStates
virtual void defineStates()
Populates the set of states.
Definition: state_model.cc:237
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::StateModel::~StateModel
virtual ~StateModel()
Destructor.
Definition: state_model.cc:97
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::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::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::END_ST
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:279
isc::util::StateModel::getLastEvent
unsigned int getLastEvent() const
Fetches the model's last event.
Definition: state_model.cc:341
isc::util::StateModel::getPrevState
unsigned int getPrevState() const
Fetches the model's previous state.
Definition: state_model.cc:336
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::StateSet::getState
const StatePtr getState(int value)
Fetches a state for the given value.
Definition: state_model.cc:62
isc::util::StateSet::StateSet
StateSet()
Constructor.
Definition: state_model.cc:43
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::StateSet::~StateSet
virtual ~StateSet()
Destructor.
Definition: state_model.cc:46
isc::util::State
Defines a State within the State Model.
Definition: state_model.h:60
isc::util::State::~State
virtual ~State()
Destructor.
Definition: state_model.cc:23
isc::util::State::run
void run()
Invokes the State's handler.
Definition: state_model.cc:27
isc::util::State::shouldPause
bool shouldPause()
Indicates if the state model should pause upon entering this state.
Definition: state_model.cc:32
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_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
isc::util::StatePausing
StatePausing
State machine pausing modes.
Definition: state_model.h:44
isc::util::STATE_PAUSE_ALWAYS
@ STATE_PAUSE_ALWAYS
Definition: state_model.h:45
isc::util::STATE_PAUSE_ONCE
@ STATE_PAUSE_ONCE
Definition: state_model.h:47
isc::util::StatePtr
boost::shared_ptr< State > StatePtr
Defines a shared pointer to a State.
Definition: state_model.h:110
isc::util::EventPtr
LabeledValuePtr EventPtr
Define Event pointer.
Definition: state_model.h:33
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::LabeledValuePtr
boost::shared_ptr< LabeledValue > LabeledValuePtr
Defines a shared pointer to a LabeledValue instance.
Definition: labeled_value.h:92
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
state_model.h
This file defines the class StateModel.