• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

igor-krechetov / hsmcpp / 24051270655

06 Apr 2026 09:06PM UTC coverage: 91.441%. Remained the same
24051270655

push

github

igor-krechetov
[1.0.4][r] fix potential multithreading issues

Fix
- Fixed potential race-conditions in dispatchers
- Replaced copy with move operations in hsm

22 of 39 new or added lines in 7 files covered. (56.41%)

16 existing lines in 4 files now uncovered.

2329 of 2547 relevant lines covered (91.44%)

1622.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.64
/src/HsmEventDispatcherBase.cpp
1
// Copyright (C) 2021 Igor Krechetov
2
// Distributed under MIT license. See file LICENSE for details
3

4
#include "hsmcpp/HsmEventDispatcherBase.hpp"
5

6
#include "hsmcpp/logging.hpp"
7
#include "hsmcpp/os/CriticalSection.hpp"
8
#include "hsmcpp/os/LockGuard.hpp"
9

10
namespace hsmcpp {
11

12
constexpr const char* HSM_TRACE_CLASS = "HsmEventDispatcherBase";
13

14
HsmEventDispatcherBase::EnqueuedEventInfo::EnqueuedEventInfo(const HandlerID_t newHandlerID, const EventID_t newEventID)
10✔
15
    : handlerID(newHandlerID)
10✔
16
    , eventID(newEventID) {}
10✔
17

18
HsmEventDispatcherBase::HsmEventDispatcherBase(const size_t eventsCacheSize) {
170✔
19
    mEnqueuedEvents.reserve(eventsCacheSize);
170✔
20
}
170✔
21

22
void HsmEventDispatcherBase::handleDelete(HsmEventDispatcherBase* dispatcher) {
170✔
23
    if (nullptr != dispatcher) {
170✔
24
        dispatcher->stop();
170✔
25

26
        if (true == dispatcher->deleteSafe()) {
170✔
27
            // NOLINTNEXTLINE(cppcoreguidelines-owning-memory): this method is only used as deleter callback for std::shared_ptr
28
            delete dispatcher;
68✔
29
        }
30
    }
31
}
170✔
32

33
void HsmEventDispatcherBase::stop() {
424✔
34
    HSM_TRACE_CALL_DEBUG();
424✔
35
    mStopDispatcher = true;
424✔
36
    // NOTE: child classes must provide additional logic
37
}
424✔
38

39
HandlerID_t HsmEventDispatcherBase::registerEventHandler(const EventHandlerFunc_t& handler) {
1,340✔
40
    HSM_TRACE_CALL_DEBUG();
1,340✔
41
    HandlerID_t id = getNextHandlerID();
1,340✔
42
    LockGuard lck(mHandlersSync);
1,340✔
43

44
    mEventHandlers[id] = handler;
1,340✔
45

46
    return id;
1,340✔
47
}
1,340✔
48

49
void HsmEventDispatcherBase::unregisterEventHandler(const HandlerID_t handlerID) {
1,210✔
50
    HSM_TRACE_CALL_DEBUG_ARGS("handlerID=%d", handlerID);
1,210✔
51

52
    {
1,210✔
53
        LockGuard lck(mEmitSync);
1,210✔
54
        mPendingEvents.remove(handlerID);
1,210✔
55
    }
1,210✔
56
    {
1,210✔
57
        LockGuard lck(mHandlersSync);
1,210✔
58
        mEventHandlers.erase(handlerID);
1,210✔
59
    }
1,210✔
60
}
1,210✔
61

62
void HsmEventDispatcherBase::emitEvent(const HandlerID_t handlerID) {
4,657✔
63
    HSM_TRACE_CALL_DEBUG();
4,657✔
64

65
    {
4,657✔
66
        LockGuard lck(mEmitSync);
4,657✔
67
        mPendingEvents.emplace_back(handlerID);
4,657✔
68
    }
4,657✔
69

70
    notifyDispatcherAboutEvent();
4,657✔
71

72
    // NOTE: this is not a full implementation. child classes must implement additional logic
73
}
4,657✔
74

75
bool HsmEventDispatcherBase::enqueueEvent(const HandlerID_t handlerID, const EventID_t event) {
10✔
76
    bool wasAdded = false;
10✔
77

78
    if (mEnqueuedEvents.size() < mEnqueuedEvents.capacity()) {
10✔
79
        {
10✔
80
            CriticalSection cs(mEnqueuedEventsSync);
10✔
81
            mEnqueuedEvents.emplace_back(handlerID, event);
10✔
82
        }
10✔
83

84
        wasAdded = true;
10✔
85
        notifyDispatcherAboutEvent();
10✔
86
    }
87

88
    return wasAdded;
10✔
89
}
90

91
void HsmEventDispatcherBase::enqueueAction(ActionHandlerFunc_t actionCallback) {
100✔
92
    HSM_TRACE_CALL_DEBUG();
100✔
93

94
    {
100✔
95
        LockGuard lck(mEmitSync);
100✔
96
        mPendingActions.emplace_back(std::move(actionCallback));
100✔
97
    }
100✔
98

99
    notifyDispatcherAboutEvent();
100✔
100
}
100✔
101

102
HandlerID_t HsmEventDispatcherBase::registerEnqueuedEventHandler(const EnqueuedEventHandlerFunc_t& handler) {
1,220✔
103
    HSM_TRACE_CALL_DEBUG();
1,220✔
104
    HandlerID_t id = getNextHandlerID();
1,220✔
105
    LockGuard lck(mHandlersSync);
1,220✔
106

107
    mEnqueuedEventHandlers[id] = handler;
1,220✔
108

109
    return id;
1,220✔
110
}
1,220✔
111

112
void HsmEventDispatcherBase::unregisterEnqueuedEventHandler(const HandlerID_t handlerID) {
1,210✔
113
    HSM_TRACE_CALL_DEBUG_ARGS("handlerID=%d", handlerID);
1,210✔
114
    LockGuard lck(mHandlersSync);
1,210✔
115

116
    mEnqueuedEventHandlers.erase(handlerID);
1,210✔
117
}
1,210✔
118

119
HandlerID_t HsmEventDispatcherBase::registerTimerHandler(const TimerHandlerFunc_t& handler) {
1,220✔
120
    const HandlerID_t newID = getNextHandlerID();
1,220✔
121
    LockGuard lck(mHandlersSync);
1,220✔
122

123
    mTimerHandlers[newID] = handler;
1,220✔
124

125
    return newID;
1,220✔
126
}
1,220✔
127

128
void HsmEventDispatcherBase::unregisterTimerHandler(const HandlerID_t handlerID) {
1,210✔
129
    LockGuard lck(mHandlersSync);
1,210✔
130
    auto itHandler = mTimerHandlers.find(handlerID);
1,210✔
131

132
    if (mTimerHandlers.end() != itHandler) {
1,210✔
133
        for (auto itTimer = mActiveTimers.begin(); itTimer != mActiveTimers.end();) {
1,270✔
134
            if (handlerID == itTimer->second.handlerID) {
60✔
135
                stopTimerImpl(itTimer->first);
60✔
136
                itTimer = mActiveTimers.erase(itTimer);
60✔
137
            } else {
138
                ++itTimer;
×
139
            }
140
        }
141

142
        mTimerHandlers.erase(itHandler);
1,210✔
143
    }
144
}
1,210✔
145

146
void HsmEventDispatcherBase::startTimer(const HandlerID_t handlerID,
210✔
147
                                        const TimerID_t timerID,
148
                                        const unsigned int intervalMs,
149
                                        const bool isSingleShot) {
150
    HSM_TRACE_CALL_DEBUG_ARGS("handlerID=%d, timerID=%d, intervalMs=%u, isSingleShot=%d",
151
                              handlerID,
152
                              timerID,
153
                              intervalMs,
154
                              BOOL2INT(isSingleShot));
210✔
155
    if (intervalMs > 0u) {
210✔
156
        LockGuard lck(mHandlersSync);
210✔
157

158
        if (mTimerHandlers.find(handlerID) != mTimerHandlers.end()) {
420✔
159
            auto it = mActiveTimers.find(timerID);
210✔
160

161
            if (mActiveTimers.end() != it) {
210✔
162
                it->second.handlerID = handlerID;
×
163
                it->second.intervalMs = intervalMs;
×
164
                it->second.isSingleShot = isSingleShot;
×
165

166
                // restart timer
167
                stopTimerImpl(timerID);
×
168
                startTimerImpl(timerID, it->second.intervalMs, it->second.isSingleShot);
×
169
            } else {
170
                TimerInfo newTimer;
210✔
171

172
                newTimer.handlerID = handlerID;
210✔
173
                newTimer.intervalMs = intervalMs;
210✔
174
                newTimer.isSingleShot = isSingleShot;
210✔
175

176
                mActiveTimers[timerID] = newTimer;
210✔
177
                startTimerImpl(timerID, intervalMs, isSingleShot);
210✔
178
            }
179
        }
180
    } else {
210✔
181
        HSM_TRACE_WARNING("skip. intervalMs must be larger than zero");
210✔
182
    }
183
}
210✔
184

185
void HsmEventDispatcherBase::restartTimer(const TimerID_t timerID) {
20✔
186
    HSM_TRACE_CALL_DEBUG_ARGS("timerID=%d", SC2INT(timerID));
20✔
187
    LockGuard lck(mHandlersSync);
20✔
188
    auto it = mActiveTimers.find(timerID);
20✔
189

190
    if (mActiveTimers.end() != it) {
20✔
191
        stopTimerImpl(timerID);
20✔
192
        startTimerImpl(timerID, it->second.intervalMs, it->second.isSingleShot);
20✔
193
    }
194
}
20✔
195

196
void HsmEventDispatcherBase::stopTimer(const TimerID_t timerID) {
30✔
197
    HSM_TRACE_CALL_DEBUG_ARGS("timerID=%d", SC2INT(timerID));
30✔
198
    LockGuard lck(mHandlersSync);
30✔
199
    auto it = mActiveTimers.find(timerID);
30✔
200

201
    HSM_TRACE_DEBUG("mActiveTimers=%lu", mActiveTimers.size());
30✔
202

203
    if (mActiveTimers.end() != it) {
30✔
204
        stopTimerImpl(timerID);
30✔
205
        mActiveTimers.erase(it);
30✔
206
    }
207
}
30✔
208

209
bool HsmEventDispatcherBase::isTimerRunning(const TimerID_t timerID) {
60✔
210
    LockGuard lck(mHandlersSync);
60✔
211
    return (mActiveTimers.find(timerID) != mActiveTimers.end());
120✔
212
}
60✔
213

214
int HsmEventDispatcherBase::getNextHandlerID() {
3,780✔
215
    return mNextHandlerId++;
3,780✔
216
}
217

218
void HsmEventDispatcherBase::unregisterAllEventHandlers() {
424✔
219
    LockGuard lck(mHandlersSync);
424✔
220
    mEventHandlers.clear();
424✔
221
}
424✔
222

223
EnqueuedEventHandlerFunc_t HsmEventDispatcherBase::getEnqueuedEventHandlerFunc(const HandlerID_t handlerID) const {
10✔
224
    // cppcheck-suppress misra-c2012-17.7 ; false-positive. This a function pointer, not function call.
225
    EnqueuedEventHandlerFunc_t func;
10✔
226
    auto it = mEnqueuedEventHandlers.find(handlerID);
10✔
227

228
    if (mEnqueuedEventHandlers.end() != it) {
10✔
229
        func = it->second;
10✔
230
    }
231

232
    return func;
10✔
UNCOV
233
}
×
234

235
TimerHandlerFunc_t HsmEventDispatcherBase::getTimerHandlerFunc(const HandlerID_t handlerID) const {
208✔
236
    // cppcheck-suppress misra-c2012-17.7 ; false-positive. This a function pointer, not function call.
237
    TimerHandlerFunc_t func;
208✔
238
    auto it = mTimerHandlers.find(handlerID);
208✔
239

240
    if (mTimerHandlers.end() != it) {
208✔
241
        func = it->second;
208✔
242
    }
243

244
    return func;
208✔
UNCOV
245
}
×
246

UNCOV
247
void HsmEventDispatcherBase::startTimerImpl(const TimerID_t timerID, const unsigned int intervalMs, const bool isSingleShot) {
×
248
    // do nothing. must be implemented in platfrom specific dispatcher
UNCOV
249
}
×
250

UNCOV
251
void HsmEventDispatcherBase::stopTimerImpl(const TimerID_t timerID) {
×
252
    // do nothing. must be implemented in platfrom specific dispatcher
UNCOV
253
}
×
254

255
unsigned int HsmEventDispatcherBase::handleTimerEvent(const TimerID_t timerID) {
209✔
256
    HSM_TRACE_CALL_DEBUG_ARGS("timerID=%d", SC2INT(timerID));
209✔
257
    unsigned int nextIntervalMs = 0;
209✔
258

259
    if (INVALID_HSM_TIMER_ID != timerID) {
209✔
260
        // NOTE: should lock the whole block to prevent situation when timer handler is unregistered during handler execution
261
        LockGuard lck(mHandlersSync);
209✔
262

263
        auto itTimer = mActiveTimers.find(timerID);
209✔
264

265
        if (mActiveTimers.end() != itTimer) {
209✔
266
            HSM_TRACE_DEBUG("curTimer.handlerID=%d", itTimer->second.handlerID);
208✔
267

268
            if (INVALID_HSM_DISPATCHER_HANDLER_ID != itTimer->second.handlerID) {
208✔
269
                TimerHandlerFunc_t timerHandler = getTimerHandlerFunc(itTimer->second.handlerID);
208✔
270

271
                nextIntervalMs = ((true == itTimer->second.isSingleShot) ? 0 : itTimer->second.intervalMs);
208✔
272

273
                // Remove singleshot timer from active list
274
                if (0u == nextIntervalMs) {
88✔
275
                    mActiveTimers.erase(itTimer);
120✔
276
                }
277

278
                timerHandler(timerID);
416✔
279
            }
208✔
280
        }
281
    }
209✔
282

283
    return nextIntervalMs;
209✔
284
}
285

286
void HsmEventDispatcherBase::dispatchEnqueuedEvents() {
3,942✔
287
    if ((false == mStopDispatcher) && (false == mEnqueuedEvents.empty())) {
3,942✔
288
        HandlerID_t prevHandlerID = INVALID_HSM_DISPATCHER_HANDLER_ID;
10✔
289
        EnqueuedEventHandlerFunc_t callback;
10✔
290
        std::vector<EnqueuedEventInfo> currentEvents;
10✔
291

292
        {
10✔
293
            CriticalSection lck(mEnqueuedEventsSync);
10✔
294

295
            // copy instead of move. we want to keep memory allocated in mEnqueuedEvents
296
            currentEvents = mEnqueuedEvents;
10✔
297
            mEnqueuedEvents.clear();
10✔
298
        }
10✔
299

300
        // need to traverse events in reverse order
301
        for (auto it = currentEvents.rbegin(); (it != currentEvents.rend()) && (false == mStopDispatcher); ++it) {
20✔
302
            if (prevHandlerID != it->handlerID) {
10✔
303
                callback = getEnqueuedEventHandlerFunc(it->handlerID);
10✔
304
                prevHandlerID = it->handlerID;
10✔
305
            }
306

307
            callback(it->eventID);
20✔
308
        }
309
    }
20✔
310
}
3,942✔
311

312
void HsmEventDispatcherBase::dispatchPendingActions() {
3,942✔
313
    if (false == mPendingActions.empty()) {
3,942✔
314
        std::list<ActionHandlerFunc_t> actionsSnapshot;
100✔
315

316
        {
100✔
317
            LockGuard lck(mEmitSync);
100✔
318
            actionsSnapshot = std::move(mPendingActions);
100✔
319
        }
100✔
320

321
        for (const ActionHandlerFunc_t& actionCallback : actionsSnapshot) {
200✔
322
            actionCallback();
200✔
323
        }
324
    }
100✔
325
}
3,942✔
326

327
void HsmEventDispatcherBase::dispatchPendingEvents() {
3,942✔
328
    std::list<HandlerID_t> events;
3,942✔
329

330
    dispatchPendingActions();
3,942✔
331

332
    if (false == mPendingEvents.empty()) {
3,942✔
333
        LockGuard lck(mEmitSync);
2,978✔
334
        events = std::move(mPendingEvents);
2,978✔
335
    }
2,978✔
336

337
    dispatchPendingEventsImpl(events);
3,942✔
338
}
3,942✔
339

340
void HsmEventDispatcherBase::dispatchPendingEventsImpl(const std::list<HandlerID_t>& events) {
3,942✔
341
    dispatchEnqueuedEvents();
3,942✔
342

343
    if ((false == mStopDispatcher) && (false == events.empty())) {
3,942✔
344
        std::map<HandlerID_t, EventHandlerFunc_t> eventHandlersCopy;
2,978✔
345

346
        {
2,978✔
347
            // TODO: workaround to prevent recursive lock if registerEventHandler/unregisterEventHandler is called from handler
348
            // callback
349
            LockGuard lck(mHandlersSync);
2,978✔
350
            eventHandlersCopy = mEventHandlers;
2,978✔
351
        }
2,978✔
352

353
        for (auto it = events.begin(); (it != events.end()) && (false == mStopDispatcher); ++it) {
6,600✔
354
            auto itHandler = eventHandlersCopy.find(*it);
3,622✔
355

356
            if (itHandler != eventHandlersCopy.end()) {
3,622✔
357
                // NOTE: if callback returns FALSE it means the handler doesn't want to process more events
358
                if (false == itHandler->second()) {
7,024✔
359
                    eventHandlersCopy.erase(itHandler);
140✔
360
                }
361
            }
362
        }
363
    }
2,978✔
364
}
3,942✔
365

366
}  // namespace hsmcpp
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc