Kea 2.7.1
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
11namespace isc {
12namespace util {
13
14/********************************** State *******************************/
15
16State::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
24
25void
27 (handler_)();
28}
29
30bool
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
44
47
48void
49StateSet::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
60const StatePtr
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
76const int StateModel::NEW_ST;
77const int StateModel::END_ST;
78
80
81// Common state model events
82const int StateModel::NOP_EVT;
83const int StateModel::START_EVT;
84const int StateModel::END_EVT;
85const int StateModel::FAIL_EVT;
86
88
89StateModel::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
98
99void
100StateModel::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
111void
112StateModel::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
139void
142
143void
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
169void
170StateModel::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
185const EventPtr&
186StateModel::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
195void
196StateModel::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
212const StatePtr
213StateModel::getState(unsigned int state_value) {
214 std::lock_guard<std::mutex> lock(*mutex_);
215 return getStateInternal(state_value);
216}
217
218const StatePtr
219StateModel::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
228void
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
236void
243
244void
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
252void
257
258void
259StateModel::onModelFailure(const std::string&) {
260 // Empty implementation to make deriving classes simpler.
261}
262
263void
264StateModel::transition(unsigned int state, unsigned int event) {
265 std::lock_guard<std::mutex> lock(*mutex_);
266 setStateInternal(state);
267 postNextEventInternal(event);
268}
269
270void
274
275void
277 std::lock_guard<std::mutex> lock(*mutex_);
278 paused_ = false;
279}
280
281void
282StateModel::abortModel(const std::string& explanation) {
284
285 std::ostringstream stream ;
286 stream << explanation << " : " << getContextStr();
287 onModelFailure(stream.str());
288}
289
290void
291StateModel::setState(unsigned int state) {
292 std::lock_guard<std::mutex> lock(*mutex_);
293 setStateInternal(state);
294}
295
296void
297StateModel::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
319void
320StateModel::postNextEvent(unsigned int event_value) {
321 std::lock_guard<std::mutex> lock(*mutex_);
322 postNextEventInternal(event_value);
323}
324
325void
326StateModel::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
338bool
340 std::lock_guard<std::mutex> lock(*mutex_);
341 bool ret = on_entry_flag_;
342 on_entry_flag_ = false;
343 return (ret);
344}
345
346bool
348 std::lock_guard<std::mutex> lock(*mutex_);
349 bool ret = on_exit_flag_;
350 on_exit_flag_ = false;
351 return (ret);
352}
353
354unsigned int
356 std::lock_guard<std::mutex> lock(*mutex_);
357 return (curr_state_);
358}
359
360unsigned int
362 std::lock_guard<std::mutex> lock(*mutex_);
363 return (prev_state_);
364}
365
366unsigned int
368 std::lock_guard<std::mutex> lock(*mutex_);
369 return (last_event_);
370}
371
372unsigned int
374 std::lock_guard<std::mutex> lock(*mutex_);
375 return (next_event_);
376}
377
378bool
380 std::lock_guard<std::mutex> lock(*mutex_);
381 return isModelNewInternal();
382}
383
384bool
385StateModel::isModelNewInternal() const {
386 return (curr_state_ == NEW_ST);
387}
388
389bool
391 std::lock_guard<std::mutex> lock(*mutex_);
392 return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST));
393}
394
395bool
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
402bool
404 std::lock_guard<std::mutex> lock(*mutex_);
405 return (curr_state_ == END_ST);
406}
407
408bool
410 std::lock_guard<std::mutex> lock(*mutex_);
411 return ((curr_state_ == END_ST) && (next_event_ == FAIL_EVT));
412}
413
414bool
416 std::lock_guard<std::mutex> lock(*mutex_);
417 return (paused_);
418}
419
420std::string
421StateModel::getStateLabel(const int state) const {
422 std::lock_guard<std::mutex> lock(*mutex_);
423 return getStateLabelInternal(state);
424}
425
426std::string
427StateModel::getStateLabelInternal(const int state) const {
428 return (states_.getLabel(state));
429}
430
431std::string
432StateModel::getEventLabel(const int event) const {
433 std::lock_guard<std::mutex> lock(*mutex_);
434 return getEventLabelInternal(event);
435}
436
437std::string
438StateModel::getEventLabelInternal(const int event) const {
439 return (events_.getLabel(event));
440}
441
442std::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
453std::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
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
bool isDefined(const int value) const
Tests if the set contains an entry for the given value.
const LabeledValuePtr & get(int value)
Fetches a pointer to the entry associated with value.
std::string getLabel(const int value) const
Fetches the label for the given value.
void add(LabeledValuePtr entry)
Adds the given entry to the set.
Implements the concept of a constant value with a text label.
Thrown if the state machine encounters a general error.
Definition state_model.h:24
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
bool isModelWaiting() const
Returns whether or not the model is waiting.
void initDictionaries()
Initializes the event and state dictionaries.
void endModel()
Conducts a normal transition to the end of the model.
static const int SM_DERIVED_STATE_MIN
Value at which custom states in a derived class should begin.
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
void unpauseModel()
Unpauses state model.
static const int FAIL_EVT
Event issued to abort the model execution.
bool isModelDone() const
Returns whether or not the model has finished execution.
virtual void runModel(unsigned int event)
Processes events through the state model.
bool isModelPaused() const
Returns whether or not the model is paused.
virtual void defineEvents()
Populates the set of events.
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
virtual void verifyStates()
Validates the contents of the set of states.
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.
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
bool isModelRunning() const
Returns whether or not the model is running.
bool doOnExit()
Checks if on exit flag is true.
StateModel()
Constructor.
static const int NEW_ST
State that a state model is in immediately after construction.
unsigned int getNextEvent() const
Fetches the model's next event.
void nopStateHandler()
An empty state handler.
virtual void onModelFailure(const std::string &explanation)
Handler for fatal model execution errors.
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
virtual void verifyEvents()
Validates the contents of the set of events.
static const int END_EVT
Event issued to end the model execution.
bool doOnEntry()
Checks if on entry flag is true.
static const int NOP_EVT
Signifies that no event has occurred.
static const int START_EVT
Event issued to start the model execution.
void abortModel(const std::string &explanation)
Aborts model execution.
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
bool isModelNew() const
Returns whether or not the model is new.
void startModel(const int start_state)
Begins execution of the model.
virtual void defineStates()
Populates the set of states.
const StatePtr getStateInternal(unsigned int value)
Fetches the state referred to by value.
std::string getPrevContextStr() const
Convenience method which returns a string rendition of the previous state and last event.
virtual ~StateModel()
Destructor.
void setState(unsigned int state)
Sets the current state to the given state value.
std::string getContextStr() const
Convenience method which returns a string rendition of the current state and next event.
static const int SM_DERIVED_EVENT_MIN
Value at which custom events in a derived class should begin.
static const int END_ST
Final state, all the state model has reached its conclusion.
unsigned int getLastEvent() const
Fetches the model's last event.
unsigned int getPrevState() const
Fetches the model's previous state.
bool didModelFail() const
Returns whether or not the model failed.
unsigned int getCurrState() const
Fetches the model's current state.
const StatePtr getState(int value)
Fetches a state for the given value.
StateSet()
Constructor.
void add(const int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing)
Adds a state definition to the set of states.
virtual ~StateSet()
Destructor.
Defines a State within the State Model.
Definition state_model.h:61
virtual ~State()
Destructor.
void run()
Invokes the State's handler.
bool shouldPause()
Indicates if the state model should pause upon entering this state.
State(const int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing=STATE_PAUSE_NEVER)
Constructor.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::function< void()> StateHandler
Defines a pointer to an instance method for handling a state.
Definition state_model.h:37
StatePausing
State machine pausing modes.
Definition state_model.h:45
@ STATE_PAUSE_ALWAYS
Definition state_model.h:46
@ STATE_PAUSE_ONCE
Definition state_model.h:48
boost::shared_ptr< State > StatePtr
Defines a shared pointer to a State.
LabeledValuePtr EventPtr
Define Event pointer.
Definition state_model.h:34
boost::shared_ptr< LabeledValue > LabeledValuePtr
Defines a shared pointer to a LabeledValue instance.
Defines the logger used by the top-level component of kea-lfc.
This file defines the class StateModel.