| Kea 3.0.2-git

state_model.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2025 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
22 State::~State() {
23}
24
25 void
26 State::run() {
27 (handler_)();
28}
29
30 bool
31 State::shouldPause() {
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
42 StateSet::StateSet() {
43}
44
45 StateSet::~StateSet() {
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
79 const int StateModel::SM_DERIVED_STATE_MIN;
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
87 const int StateModel::SM_DERIVED_EVENT_MIN;
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
96 StateModel::~StateModel() {
97}
98
99 void
100 StateModel::startModel(const int start_state) {
101 // Initialize dictionaries of events and states.
102 initDictionaries();
103
104 // Set the current state to starting state and enter the run loop.
105 setState(start_state);
106
107 // Start running the model.
108 runModel(START_EVT);
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
140 StateModel::nopStateHandler() {
141}
142
143 void
144 StateModel::initDictionaries() {
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)) {
188 isc_throw(StateModelError,
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)) {
221 isc_throw(StateModelError,
222 "State value is not defined:" << state_value);
223 }
224
225 return (states_.getState(state_value));
226}
227
228 void
229 StateModel::defineEvents() {
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
237 StateModel::verifyEvents() {
238 getEvent(NOP_EVT);
239 getEvent(START_EVT);
240 getEvent(END_EVT);
241 getEvent(FAIL_EVT);
242}
243
244 void
245 StateModel::defineStates() {
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
253 StateModel::verifyStates() {
254 getStateInternal(NEW_ST);
255 getStateInternal(END_ST);
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
271 StateModel::endModel() {
272 transition(END_ST, END_EVT);
273}
274
275 void
276 StateModel::unpauseModel() {
277 std::lock_guard<std::mutex> lock(*mutex_);
278 paused_ = false;
279}
280
281 void
282 StateModel::abortModel(const std::string& explanation) {
283 transition(END_ST, FAIL_EVT);
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
297StateModel::setStateInternal(unsigned int state) {
298 if (state != END_ST && !states_.isDefined(state)) {
299 isc_throw(StateModelError,
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
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)) {
330 isc_throw(StateModelError,
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
339 StateModel::doOnEntry() {
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
347 StateModel::doOnExit() {
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
355 StateModel::getCurrState() const {
356 std::lock_guard<std::mutex> lock(*mutex_);
357 return (curr_state_);
358}
359
360 unsigned int
361 StateModel::getPrevState() const {
362 std::lock_guard<std::mutex> lock(*mutex_);
363 return (prev_state_);
364}
365
366 unsigned int
367 StateModel::getLastEvent() const {
368 std::lock_guard<std::mutex> lock(*mutex_);
369 return (last_event_);
370}
371
372 unsigned int
373 StateModel::getNextEvent() const {
374 std::lock_guard<std::mutex> lock(*mutex_);
375 return (next_event_);
376}
377
378 bool
379 StateModel::isModelNew() const {
380 std::lock_guard<std::mutex> lock(*mutex_);
381 return isModelNewInternal();
382}
383
384 bool
385StateModel::isModelNewInternal() const {
386 return (curr_state_ == NEW_ST);
387}
388
389 bool
390 StateModel::isModelRunning() const {
391 std::lock_guard<std::mutex> lock(*mutex_);
392 return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST));
393}
394
395 bool
396 StateModel::isModelWaiting() const {
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
403 StateModel::isModelDone() const {
404 std::lock_guard<std::mutex> lock(*mutex_);
405 return (curr_state_ == END_ST);
406}
407
408 bool
409 StateModel::didModelFail() const {
410 std::lock_guard<std::mutex> lock(*mutex_);
411 return ((curr_state_ == END_ST) && (next_event_ == FAIL_EVT));
412}
413
414 bool
415 StateModel::isModelPaused() const {
416 std::lock_guard<std::mutex> lock(*mutex_);
417 return (paused_);
418}
419
420std::string
421 StateModel::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
432 StateModel::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
443 StateModel::getContextStr() const {
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
454 StateModel::getPrevContextStr() const {
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
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::LabeledValue
LabeledValue(const int value, const std::string &label)
Constructor.
Definition labeled_value.cc:16
isc::util::StateModelError
Thrown if the state machine encounters a general error.
Definition state_model.h:24
isc::util::StateModel::getEvent
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition state_model.cc:186
isc::util::StateModel::isModelWaiting
bool isModelWaiting() const
Returns whether or not the model is waiting.
Definition state_model.cc:396
isc::util::StateModel::initDictionaries
void initDictionaries()
Initializes the event and state dictionaries.
Definition state_model.cc:144
isc::util::StateModel::endModel
void endModel()
Conducts a normal transition to the end of the model.
Definition state_model.cc:271
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:285
isc::util::StateModel::getStateLabel
std::string getStateLabel(const int state) const
Fetches the label associated with an state value.
Definition state_model.cc:421
isc::util::StateModel::unpauseModel
void unpauseModel()
Unpauses state model.
Definition state_model.cc:276
isc::util::StateModel::FAIL_EVT
static const int FAIL_EVT
Event issued to abort the model execution.
Definition state_model.h:301
isc::util::StateModel::isModelDone
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition state_model.cc:403
isc::util::StateModel::runModel
virtual void runModel(unsigned int event)
Processes events through the state model.
Definition state_model.cc:112
isc::util::StateModel::isModelPaused
bool isModelPaused() const
Returns whether or not the model is paused.
Definition state_model.cc:415
isc::util::StateModel::defineEvents
virtual void defineEvents()
Populates the set of events.
Definition state_model.cc:229
isc::util::StateModel::postNextEvent
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition state_model.cc:320
isc::util::StateModel::verifyStates
virtual void verifyStates()
Validates the contents of the set of states.
Definition state_model.cc:253
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:196
isc::util::StateModel::getState
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition state_model.cc:213
isc::util::StateModel::isModelRunning
bool isModelRunning() const
Returns whether or not the model is running.
Definition state_model.cc:390
isc::util::StateModel::doOnExit
bool doOnExit()
Checks if on exit flag is true.
Definition state_model.cc:347
isc::util::StateModel::StateModel
StateModel()
Constructor.
Definition state_model.cc:89
isc::util::StateModel::NEW_ST
static const int NEW_ST
State that a state model is in immediately after construction.
Definition state_model.h:279
isc::util::StateModel::getNextEvent
unsigned int getNextEvent() const
Fetches the model's next event.
Definition state_model.cc:373
isc::util::StateModel::nopStateHandler
void nopStateHandler()
An empty state handler.
Definition state_model.cc:140
isc::util::StateModel::onModelFailure
virtual void onModelFailure(const std::string &explanation)
Handler for fatal model execution errors.
Definition state_model.cc:259
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:170
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:264
isc::util::StateModel::verifyEvents
virtual void verifyEvents()
Validates the contents of the set of events.
Definition state_model.cc:237
isc::util::StateModel::END_EVT
static const int END_EVT
Event issued to end the model execution.
Definition state_model.h:298
isc::util::StateModel::doOnEntry
bool doOnEntry()
Checks if on entry flag is true.
Definition state_model.cc:339
isc::util::StateModel::NOP_EVT
static const int NOP_EVT
Signifies that no event has occurred.
Definition state_model.h:292
isc::util::StateModel::START_EVT
static const int START_EVT
Event issued to start the model execution.
Definition state_model.h:295
isc::util::StateModel::abortModel
void abortModel(const std::string &explanation)
Aborts model execution.
Definition state_model.cc:282
isc::util::StateModel::getEventLabel
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition state_model.cc:432
isc::util::StateModel::isModelNew
bool isModelNew() const
Returns whether or not the model is new.
Definition state_model.cc:379
isc::util::StateModel::startModel
void startModel(const int start_state)
Begins execution of the model.
Definition state_model.cc:100
isc::util::StateModel::defineStates
virtual void defineStates()
Populates the set of states.
Definition state_model.cc:245
isc::util::StateModel::getStateInternal
const StatePtr getStateInternal(unsigned int value)
Fetches the state referred to by value.
Definition state_model.cc:219
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:454
isc::util::StateModel::~StateModel
virtual ~StateModel()
Destructor.
Definition state_model.cc:96
isc::util::StateModel::setState
void setState(unsigned int state)
Sets the current state to the given state value.
Definition state_model.cc:291
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:443
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:304
isc::util::StateModel::END_ST
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition state_model.h:282
isc::util::StateModel::getLastEvent
unsigned int getLastEvent() const
Fetches the model's last event.
Definition state_model.cc:367
isc::util::StateModel::getPrevState
unsigned int getPrevState() const
Fetches the model's previous state.
Definition state_model.cc:361
isc::util::StateModel::didModelFail
bool didModelFail() const
Returns whether or not the model failed.
Definition state_model.cc:409
isc::util::StateModel::getCurrState
unsigned int getCurrState() const
Fetches the model's current state.
Definition state_model.cc:355
isc::util::StateSet::getState
const StatePtr getState(int value)
Fetches a state for the given value.
Definition state_model.cc:61
isc::util::StateSet::StateSet
StateSet()
Constructor.
Definition state_model.cc:42
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:49
isc::util::StateSet::~StateSet
virtual ~StateSet()
Destructor.
Definition state_model.cc:45
isc::util::State
Defines a State within the State Model.
Definition state_model.h:61
isc::util::State::~State
virtual ~State()
Destructor.
Definition state_model.cc:22
isc::util::State::run
void run()
Invokes the State's handler.
Definition state_model.cc:26
isc::util::State::shouldPause
bool shouldPause()
Indicates if the state model should pause upon entering this state.
Definition state_model.cc:31
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:16
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
isc::util
Definition time_utils.cc:43
isc::util::StateHandler
std::function< void()> StateHandler
Defines a pointer to an instance method for handling a state.
Definition state_model.h:37
isc::util::StatePausing
StatePausing
State machine pausing modes.
Definition state_model.h:45
isc::util::STATE_PAUSE_ALWAYS
@ STATE_PAUSE_ALWAYS
Definition state_model.h:46
isc::util::STATE_PAUSE_ONCE
@ STATE_PAUSE_ONCE
Definition state_model.h:48
isc::util::StatePtr
boost::shared_ptr< State > StatePtr
Defines a shared pointer to a State.
Definition state_model.h:111
isc::util::EventPtr
LabeledValuePtr EventPtr
Define Event pointer.
Definition state_model.h:34
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-lfc.
Definition agent_parser.cc:148
state_model.h
This file defines the class StateModel.