Kea 2.5.8
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
23}
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
43}
44
46}
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
97}
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
141}
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
242}
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
256}
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
273}
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.
Definition: labeled_value.h:39
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.
Definition: state_model.cc:186
bool isModelWaiting() const
Returns whether or not the model is waiting.
Definition: state_model.cc:396
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:144
void endModel()
Conducts a normal transition to the end of the model.
Definition: state_model.cc:271
static const int SM_DERIVED_STATE_MIN
Value at which custom states in a derived class should begin.
Definition: state_model.h:285
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
Definition: state_model.cc:421
void unpauseModel()
Unpauses state model.
Definition: state_model.cc:276
static const int FAIL_EVT
Event issued to abort the model execution.
Definition: state_model.h:301
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:403
virtual void runModel(unsigned int event)
Processes events through the state model.
Definition: state_model.cc:112
bool isModelPaused() const
Returns whether or not the model is paused.
Definition: state_model.cc:415
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
virtual void verifyStates()
Validates the contents of the set of states.
Definition: state_model.cc:253
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
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:213
bool isModelRunning() const
Returns whether or not the model is running.
Definition: state_model.cc:390
bool doOnExit()
Checks if on exit flag is true.
Definition: state_model.cc:347
StateModel()
Constructor.
Definition: state_model.cc:89
static const int NEW_ST
State that a state model is in immediately after construction.
Definition: state_model.h:279
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
void nopStateHandler()
An empty state handler.
Definition: state_model.cc:140
virtual void onModelFailure(const std::string &explanation)
Handler for fatal model execution errors.
Definition: state_model.cc:259
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
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
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
bool doOnEntry()
Checks if on entry flag is true.
Definition: state_model.cc:339
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:292
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:282
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:432
bool isModelNew() const
Returns whether or not the model is new.
Definition: state_model.cc:379
void startModel(const int start_state)
Begins execution of the model.
Definition: state_model.cc:100
virtual void defineStates()
Populates the set of states.
Definition: state_model.cc:245
const StatePtr getStateInternal(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:219
std::string getPrevContextStr() const
Convenience method which returns a string rendition of the previous state and last event.
Definition: state_model.cc:454
virtual ~StateModel()
Destructor.
Definition: state_model.cc:96
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:291
std::string getContextStr() const
Convenience method which returns a string rendition of the current state and next event.
Definition: state_model.cc:443
static const int SM_DERIVED_EVENT_MIN
Value at which custom events in a derived class should begin.
Definition: state_model.h:304
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:282
unsigned int getLastEvent() const
Fetches the model's last event.
Definition: state_model.cc:367
unsigned int getPrevState() const
Fetches the model's previous state.
Definition: state_model.cc:361
bool didModelFail() const
Returns whether or not the model failed.
Definition: state_model.cc:409
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:355
const StatePtr getState(int value)
Fetches a state for the given value.
Definition: state_model.cc:61
StateSet()
Constructor.
Definition: state_model.cc:42
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
virtual ~StateSet()
Destructor.
Definition: state_model.cc:45
Defines a State within the State Model.
Definition: state_model.h:61
virtual ~State()
Destructor.
Definition: state_model.cc:22
void run()
Invokes the State's handler.
Definition: state_model.cc:26
bool shouldPause()
Indicates if the state model should pause upon entering this state.
Definition: state_model.cc:31
State(const int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing=STATE_PAUSE_NEVER)
Constructor.
Definition: state_model.cc:16
#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.
Definition: state_model.h:111
LabeledValuePtr EventPtr
Define Event pointer.
Definition: state_model.h:34
boost::shared_ptr< LabeledValue > LabeledValuePtr
Defines a shared pointer to a LabeledValue instance.
Definition: labeled_value.h:92
Defines the logger used by the top-level component of kea-lfc.
This file defines the class StateModel.