/src/kea/src/lib/util/state_model.cc
Line | Count | Source |
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 | 175k | : LabeledValue(value, label), handler_(handler), pausing_(state_pausing), |
19 | 175k | was_paused_(false) { |
20 | 175k | } |
21 | | |
22 | 175k | State::~State() { |
23 | 175k | } |
24 | | |
25 | | void |
26 | 41.4M | State::run() { |
27 | 41.4M | (handler_)(); |
28 | 41.4M | } |
29 | | |
30 | | bool |
31 | 3.25M | State::shouldPause() { |
32 | 3.25M | if ((pausing_ == STATE_PAUSE_ALWAYS) || |
33 | 3.25M | ((pausing_ == STATE_PAUSE_ONCE) && (!was_paused_))) { |
34 | 0 | was_paused_ = true; |
35 | 0 | return (true); |
36 | 0 | } |
37 | 3.25M | return (false); |
38 | 3.25M | } |
39 | | |
40 | | /********************************** StateSet *******************************/ |
41 | | |
42 | 10.5k | StateSet::StateSet() { |
43 | 10.5k | } |
44 | | |
45 | 10.5k | StateSet::~StateSet() { |
46 | 10.5k | } |
47 | | |
48 | | void |
49 | | StateSet::add(const int value, const std::string& label, StateHandler handler, |
50 | 175k | const StatePausing& state_pausing) { |
51 | 175k | try { |
52 | 175k | LabeledValueSet::add(LabeledValuePtr(new State(value, label, handler, |
53 | 175k | state_pausing))); |
54 | 175k | } catch (const std::exception& ex) { |
55 | 0 | isc_throw(StateModelError, "StateSet: cannot add state :" << ex.what()); |
56 | 0 | } |
57 | | |
58 | 175k | } |
59 | | |
60 | | const StatePtr |
61 | 44.6M | StateSet::getState(int value) { |
62 | 44.6M | if (!isDefined(value)) { |
63 | 0 | isc_throw(StateModelError," StateSet: state is undefined"); |
64 | 0 | } |
65 | | |
66 | | // Since we have to use dynamic casting, to get a state pointer |
67 | | // we can't return a reference. |
68 | 44.6M | StatePtr state = boost::dynamic_pointer_cast<State>(get(value)); |
69 | 44.6M | return (state); |
70 | 44.6M | } |
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 | 10.5k | StateModel::StateModel() : events_(), states_(), dictionaries_initted_(false), |
90 | 10.5k | curr_state_(NEW_ST), prev_state_(NEW_ST), |
91 | 10.5k | last_event_(NOP_EVT), next_event_(NOP_EVT), |
92 | 10.5k | on_entry_flag_(false), on_exit_flag_(false), |
93 | 10.5k | paused_(false), mutex_(new std::mutex) { |
94 | 10.5k | } |
95 | | |
96 | 10.5k | StateModel::~StateModel() { |
97 | 10.5k | } |
98 | | |
99 | | void |
100 | 0 | StateModel::startModel(const int start_state) { |
101 | | // Initialize dictionaries of events and states. |
102 | 0 | initDictionaries(); |
103 | | |
104 | | // Set the current state to starting state and enter the run loop. |
105 | 0 | setState(start_state); |
106 | | |
107 | | // Start running the model. |
108 | 0 | runModel(START_EVT); |
109 | 0 | } |
110 | | |
111 | | void |
112 | 0 | StateModel::runModel(unsigned int run_event) { |
113 | | /// If the dictionaries aren't built bail out. |
114 | 0 | if (!dictionaries_initted_) { |
115 | 0 | abortModel("runModel invoked before model has been initialized"); |
116 | 0 | } |
117 | |
|
118 | 0 | try { |
119 | | // Seed the loop with the given event as the next to process. |
120 | 0 | postNextEvent(run_event); |
121 | 0 | 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 | 0 | getState(curr_state_)->run(); |
126 | | |
127 | | // Keep going until a handler sets next event to a NOP_EVT. |
128 | 0 | } while (!isModelDone() && getNextEvent() != NOP_EVT); |
129 | 0 | } 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 | 0 | abortModel(ex.what()); |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | | void |
140 | 0 | StateModel::nopStateHandler() { |
141 | 0 | } |
142 | | |
143 | | void |
144 | 10.5k | StateModel::initDictionaries() { |
145 | 10.5k | std::lock_guard<std::mutex> lock(*mutex_); |
146 | 10.5k | if (dictionaries_initted_) { |
147 | 0 | isc_throw(StateModelError, "Dictionaries already initialized"); |
148 | 0 | } |
149 | | // First let's build and verify the dictionary of events. |
150 | 10.5k | try { |
151 | 10.5k | defineEvents(); |
152 | 10.5k | verifyEvents(); |
153 | 10.5k | } catch (const std::exception& ex) { |
154 | 0 | isc_throw(StateModelError, "Event set is invalid: " << ex.what()); |
155 | 0 | } |
156 | | |
157 | | // Next let's build and verify the dictionary of states. |
158 | 10.5k | try { |
159 | 10.5k | defineStates(); |
160 | 10.5k | verifyStates(); |
161 | 10.5k | } catch (const std::exception& ex) { |
162 | 0 | isc_throw(StateModelError, "State set is invalid: " << ex.what()); |
163 | 0 | } |
164 | | |
165 | | // Record that we are good to go. |
166 | 10.5k | dictionaries_initted_ = true; |
167 | 10.5k | } |
168 | | |
169 | | void |
170 | 95.3k | StateModel::defineEvent(unsigned int event_value, const std::string& label) { |
171 | 95.3k | if (!isModelNewInternal()) { |
172 | | // Don't allow for self-modifying models. |
173 | 0 | isc_throw(StateModelError, "Events may only be added to a new model." |
174 | 0 | << event_value << " - " << label); |
175 | 0 | } |
176 | | |
177 | | // Attempt to add the event to the set. |
178 | 95.3k | try { |
179 | 95.3k | events_.add(event_value, label); |
180 | 95.3k | } catch (const std::exception& ex) { |
181 | 0 | isc_throw(StateModelError, "Error adding event: " << ex.what()); |
182 | 0 | } |
183 | 95.3k | } |
184 | | |
185 | | const EventPtr& |
186 | 95.3k | StateModel::getEvent(unsigned int event_value) { |
187 | 95.3k | if (!events_.isDefined(event_value)) { |
188 | 0 | isc_throw(StateModelError, |
189 | 0 | "Event value is not defined:" << event_value); |
190 | 0 | } |
191 | | |
192 | 95.3k | return (events_.get(event_value)); |
193 | 95.3k | } |
194 | | |
195 | | void |
196 | | StateModel::defineState(unsigned int state_value, const std::string& label, |
197 | 175k | StateHandler handler, const StatePausing& state_pausing) { |
198 | 175k | if (!isModelNewInternal()) { |
199 | | // Don't allow for self-modifying maps. |
200 | 0 | isc_throw(StateModelError, "States may only be added to a new model." |
201 | 0 | << state_value << " - " << label); |
202 | 0 | } |
203 | | |
204 | | // Attempt to add the state to the set. |
205 | 175k | try { |
206 | 175k | states_.add(state_value, label, handler, state_pausing); |
207 | 175k | } catch (const std::exception& ex) { |
208 | 0 | isc_throw(StateModelError, "Error adding state: " << ex.what()); |
209 | 0 | } |
210 | 175k | } |
211 | | |
212 | | const StatePtr |
213 | 41.4M | StateModel::getState(unsigned int state_value) { |
214 | 41.4M | std::lock_guard<std::mutex> lock(*mutex_); |
215 | 41.4M | return getStateInternal(state_value); |
216 | 41.4M | } |
217 | | |
218 | | const StatePtr |
219 | 44.6M | StateModel::getStateInternal(unsigned int state_value) { |
220 | 44.6M | if (!states_.isDefined(state_value)) { |
221 | 0 | isc_throw(StateModelError, |
222 | 0 | "State value is not defined:" << state_value); |
223 | 0 | } |
224 | | |
225 | 44.6M | return (states_.getState(state_value)); |
226 | 44.6M | } |
227 | | |
228 | | void |
229 | 10.5k | StateModel::defineEvents() { |
230 | 10.5k | defineEvent(NOP_EVT, "NOP_EVT"); |
231 | 10.5k | defineEvent(START_EVT, "START_EVT"); |
232 | 10.5k | defineEvent(END_EVT, "END_EVT"); |
233 | 10.5k | defineEvent(FAIL_EVT, "FAIL_EVT"); |
234 | 10.5k | } |
235 | | |
236 | | void |
237 | 10.5k | StateModel::verifyEvents() { |
238 | 10.5k | getEvent(NOP_EVT); |
239 | 10.5k | getEvent(START_EVT); |
240 | 10.5k | getEvent(END_EVT); |
241 | 10.5k | getEvent(FAIL_EVT); |
242 | 10.5k | } |
243 | | |
244 | | void |
245 | 10.5k | StateModel::defineStates() { |
246 | 10.5k | defineState(NEW_ST, "NEW_ST", |
247 | 10.5k | std::bind(&StateModel::nopStateHandler, this)); |
248 | 10.5k | defineState(END_ST, "END_ST", |
249 | 10.5k | std::bind(&StateModel::nopStateHandler, this)); |
250 | 10.5k | } |
251 | | |
252 | | void |
253 | 10.5k | StateModel::verifyStates() { |
254 | 10.5k | getStateInternal(NEW_ST); |
255 | 10.5k | getStateInternal(END_ST); |
256 | 10.5k | } |
257 | | |
258 | | void |
259 | 0 | StateModel::onModelFailure(const std::string&) { |
260 | | // Empty implementation to make deriving classes simpler. |
261 | 0 | } |
262 | | |
263 | | void |
264 | 22.8M | StateModel::transition(unsigned int state, unsigned int event) { |
265 | 22.8M | std::lock_guard<std::mutex> lock(*mutex_); |
266 | 22.8M | setStateInternal(state); |
267 | 22.8M | postNextEventInternal(event); |
268 | 22.8M | } |
269 | | |
270 | | void |
271 | 0 | StateModel::endModel() { |
272 | 0 | transition(END_ST, END_EVT); |
273 | 0 | } |
274 | | |
275 | | void |
276 | 0 | StateModel::unpauseModel() { |
277 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
278 | 0 | paused_ = false; |
279 | 0 | } |
280 | | |
281 | | void |
282 | 1.35k | StateModel::abortModel(const std::string& explanation) { |
283 | 1.35k | transition(END_ST, FAIL_EVT); |
284 | | |
285 | 1.35k | std::ostringstream stream ; |
286 | 1.35k | stream << explanation << " : " << getContextStr(); |
287 | 1.35k | onModelFailure(stream.str()); |
288 | 1.35k | } |
289 | | |
290 | | void |
291 | 10.5k | StateModel::setState(unsigned int state) { |
292 | 10.5k | std::lock_guard<std::mutex> lock(*mutex_); |
293 | 10.5k | setStateInternal(state); |
294 | 10.5k | } |
295 | | |
296 | | void |
297 | 22.8M | StateModel::setStateInternal(unsigned int state) { |
298 | 22.8M | if (state != END_ST && !states_.isDefined(state)) { |
299 | 1.14k | isc_throw(StateModelError, |
300 | 1.14k | "Attempt to set state to an undefined value: " << state ); |
301 | 1.14k | } |
302 | | |
303 | 22.8M | prev_state_ = curr_state_; |
304 | 22.8M | curr_state_ = state; |
305 | | |
306 | | // Set the "do" flags if we are transitioning. |
307 | 22.8M | on_entry_flag_ = ((state != END_ST) && (prev_state_ != curr_state_)); |
308 | | |
309 | | // At this time they are calculated the same way. |
310 | 22.8M | 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 | 22.8M | if (on_entry_flag_ && !paused_ && getStateInternal(state)->shouldPause()) { |
315 | 0 | paused_ = true; |
316 | 0 | } |
317 | 22.8M | } |
318 | | |
319 | | void |
320 | 18.5M | StateModel::postNextEvent(unsigned int event_value) { |
321 | 18.5M | std::lock_guard<std::mutex> lock(*mutex_); |
322 | 18.5M | postNextEventInternal(event_value); |
323 | 18.5M | } |
324 | | |
325 | | void |
326 | 41.4M | StateModel::postNextEventInternal(unsigned int event_value) { |
327 | | // Check for FAIL_EVT as special case of model error before events are |
328 | | // defined. |
329 | 41.4M | if (event_value != FAIL_EVT && !events_.isDefined(event_value)) { |
330 | 0 | isc_throw(StateModelError, |
331 | 0 | "Attempt to post an undefined event, value: " << event_value); |
332 | 0 | } |
333 | | |
334 | 41.4M | last_event_ = next_event_; |
335 | 41.4M | next_event_ = event_value; |
336 | 41.4M | } |
337 | | |
338 | | bool |
339 | 0 | StateModel::doOnEntry() { |
340 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
341 | 0 | bool ret = on_entry_flag_; |
342 | 0 | on_entry_flag_ = false; |
343 | 0 | return (ret); |
344 | 0 | } |
345 | | |
346 | | bool |
347 | 0 | StateModel::doOnExit() { |
348 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
349 | 0 | bool ret = on_exit_flag_; |
350 | 0 | on_exit_flag_ = false; |
351 | 0 | return (ret); |
352 | 0 | } |
353 | | |
354 | | unsigned int |
355 | 60.9M | StateModel::getCurrState() const { |
356 | 60.9M | std::lock_guard<std::mutex> lock(*mutex_); |
357 | 60.9M | return (curr_state_); |
358 | 60.9M | } |
359 | | |
360 | | unsigned int |
361 | 0 | StateModel::getPrevState() const { |
362 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
363 | 0 | return (prev_state_); |
364 | 0 | } |
365 | | |
366 | | unsigned int |
367 | 7.03k | StateModel::getLastEvent() const { |
368 | 7.03k | std::lock_guard<std::mutex> lock(*mutex_); |
369 | 7.03k | return (last_event_); |
370 | 7.03k | } |
371 | | |
372 | | unsigned int |
373 | 165M | StateModel::getNextEvent() const { |
374 | 165M | std::lock_guard<std::mutex> lock(*mutex_); |
375 | 165M | return (next_event_); |
376 | 165M | } |
377 | | |
378 | | bool |
379 | 0 | StateModel::isModelNew() const { |
380 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
381 | 0 | return isModelNewInternal(); |
382 | 0 | } |
383 | | |
384 | | bool |
385 | 271k | StateModel::isModelNewInternal() const { |
386 | 271k | return (curr_state_ == NEW_ST); |
387 | 271k | } |
388 | | |
389 | | bool |
390 | 0 | StateModel::isModelRunning() const { |
391 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
392 | 0 | return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST)); |
393 | 0 | } |
394 | | |
395 | | bool |
396 | 0 | StateModel::isModelWaiting() const { |
397 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
398 | 0 | return ((curr_state_ != NEW_ST) && (curr_state_ != END_ST) && |
399 | 0 | (next_event_ == NOP_EVT)); |
400 | 0 | } |
401 | | |
402 | | bool |
403 | 41.4M | StateModel::isModelDone() const { |
404 | 41.4M | std::lock_guard<std::mutex> lock(*mutex_); |
405 | 41.4M | return (curr_state_ == END_ST); |
406 | 41.4M | } |
407 | | |
408 | | bool |
409 | 0 | StateModel::didModelFail() const { |
410 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
411 | 0 | return ((curr_state_ == END_ST) && (next_event_ == FAIL_EVT)); |
412 | 0 | } |
413 | | |
414 | | bool |
415 | 0 | StateModel::isModelPaused() const { |
416 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
417 | 0 | return (paused_); |
418 | 0 | } |
419 | | |
420 | | std::string |
421 | 0 | StateModel::getStateLabel(const int state) const { |
422 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
423 | 0 | return getStateLabelInternal(state); |
424 | 0 | } |
425 | | |
426 | | std::string |
427 | 1.35k | StateModel::getStateLabelInternal(const int state) const { |
428 | 1.35k | return (states_.getLabel(state)); |
429 | 1.35k | } |
430 | | |
431 | | std::string |
432 | 0 | StateModel::getEventLabel(const int event) const { |
433 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
434 | 0 | return getEventLabelInternal(event); |
435 | 0 | } |
436 | | |
437 | | std::string |
438 | 1.35k | StateModel::getEventLabelInternal(const int event) const { |
439 | 1.35k | return (events_.getLabel(event)); |
440 | 1.35k | } |
441 | | |
442 | | std::string |
443 | 1.35k | StateModel::getContextStr() const { |
444 | 1.35k | std::lock_guard<std::mutex> lock(*mutex_); |
445 | 1.35k | std::ostringstream stream; |
446 | 1.35k | stream << "current state: [ " |
447 | 1.35k | << curr_state_ << " " << getStateLabelInternal(curr_state_) |
448 | 1.35k | << " ] next event: [ " |
449 | 1.35k | << next_event_ << " " << getEventLabelInternal(next_event_) << " ]"; |
450 | 1.35k | return (stream.str()); |
451 | 1.35k | } |
452 | | |
453 | | std::string |
454 | 0 | StateModel::getPrevContextStr() const { |
455 | 0 | std::lock_guard<std::mutex> lock(*mutex_); |
456 | 0 | std::ostringstream stream; |
457 | 0 | stream << "previous state: [ " |
458 | 0 | << prev_state_ << " " << getStateLabelInternal(prev_state_) |
459 | 0 | << " ] last event: [ " |
460 | 0 | << next_event_ << " " << getEventLabelInternal(last_event_) << " ]"; |
461 | 0 | return (stream.str()); |
462 | 0 | } |
463 | | |
464 | | } // namespace isc::util |
465 | | } // namespace isc |