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