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
11 namespace isc {
12 namespace util {
13
14 /********************************** State *******************************/
15
State(const int value,const std::string & label,StateHandler handler,const StatePausing & state_pausing)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
~State()22 State::~State() {
23 }
24
25 void
run()26 State::run() {
27 (handler_)();
28 }
29
30 bool
shouldPause()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
StateSet()42 StateSet::StateSet() {
43 }
44
~StateSet()45 StateSet::~StateSet() {
46 }
47
48 void
add(const int value,const std::string & label,StateHandler handler,const StatePausing & state_pausing)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
getState(int value)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
StateModel()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
~StateModel()96 StateModel::~StateModel(){
97 }
98
99 void
startModel(const int start_state)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
runModel(unsigned int run_event)112 StateModel::runModel(unsigned int run_event) {
113 /// If the dictionaries aren't built bail out.
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
nopStateHandler()140 StateModel::nopStateHandler() {
141 }
142
143 void
initDictionaries()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
defineEvent(unsigned int event_value,const std::string & label)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&
getEvent(unsigned int event_value)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
defineState(unsigned int state_value,const std::string & label,StateHandler handler,const StatePausing & state_pausing)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
getState(unsigned int state_value)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
getStateInternal(unsigned int state_value)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
defineEvents()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
verifyEvents()237 StateModel::verifyEvents() {
238 getEvent(NOP_EVT);
239 getEvent(START_EVT);
240 getEvent(END_EVT);
241 getEvent(FAIL_EVT);
242 }
243
244 void
defineStates()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
verifyStates()253 StateModel::verifyStates() {
254 getStateInternal(NEW_ST);
255 getStateInternal(END_ST);
256 }
257
258 void
onModelFailure(const std::string &)259 StateModel::onModelFailure(const std::string&) {
260 // Empty implementation to make deriving classes simpler.
261 }
262
263 void
transition(unsigned int state,unsigned int event)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
endModel()271 StateModel::endModel() {
272 transition(END_ST, END_EVT);
273 }
274
275 void
unpauseModel()276 StateModel::unpauseModel() {
277 std::lock_guard<std::mutex> lock(*mutex_);
278 paused_ = false;
279 }
280
281 void
abortModel(const std::string & explanation)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
setState(unsigned int state)291 StateModel::setState(unsigned int state) {
292 std::lock_guard<std::mutex> lock(*mutex_);
293 setStateInternal(state);
294 }
295
296 void
setStateInternal(unsigned int state)297 StateModel::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
postNextEvent(unsigned int event_value)320 StateModel::postNextEvent(unsigned int event_value) {
321 std::lock_guard<std::mutex> lock(*mutex_);
322 postNextEventInternal(event_value);
323 }
324
325 void
postNextEventInternal(unsigned int event_value)326 StateModel::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
doOnEntry()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
doOnExit()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
getCurrState() const355 StateModel::getCurrState() const {
356 std::lock_guard<std::mutex> lock(*mutex_);
357 return (curr_state_);
358 }
359
360 unsigned int
getPrevState() const361 StateModel::getPrevState() const {
362 std::lock_guard<std::mutex> lock(*mutex_);
363 return (prev_state_);
364 }
365
366 unsigned int
getLastEvent() const367 StateModel::getLastEvent() const {
368 std::lock_guard<std::mutex> lock(*mutex_);
369 return (last_event_);
370 }
371
372 unsigned int
getNextEvent() const373 StateModel::getNextEvent() const {
374 std::lock_guard<std::mutex> lock(*mutex_);
375 return (next_event_);
376 }
377
378 bool
isModelNew() const379 StateModel::isModelNew() const {
380 std::lock_guard<std::mutex> lock(*mutex_);
381 return isModelNewInternal();
382 }
383
384 bool
isModelNewInternal() const385 StateModel::isModelNewInternal() const {
386 return (curr_state_ == NEW_ST);
387 }
388
389 bool
isModelRunning() const390 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
isModelWaiting() const396 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
isModelDone() const403 StateModel::isModelDone() const {
404 std::lock_guard<std::mutex> lock(*mutex_);
405 return (curr_state_ == END_ST);
406 }
407
408 bool
didModelFail() const409 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
isModelPaused() const415 StateModel::isModelPaused() const {
416 std::lock_guard<std::mutex> lock(*mutex_);
417 return (paused_);
418 }
419
420 std::string
getStateLabel(const int state) const421 StateModel::getStateLabel(const int state) const {
422 std::lock_guard<std::mutex> lock(*mutex_);
423 return getStateLabelInternal(state);
424 }
425
426 std::string
getStateLabelInternal(const int state) const427 StateModel::getStateLabelInternal(const int state) const {
428 return (states_.getLabel(state));
429 }
430
431 std::string
getEventLabel(const int event) const432 StateModel::getEventLabel(const int event) const {
433 std::lock_guard<std::mutex> lock(*mutex_);
434 return getEventLabelInternal(event);
435 }
436
437 std::string
getEventLabelInternal(const int event) const438 StateModel::getEventLabelInternal(const int event) const {
439 return (events_.getLabel(event));
440 }
441
442 std::string
getContextStr() const443 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
453 std::string
getPrevContextStr() const454 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
466