Coverage Report

Created: 2026-02-14 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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