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

igor-krechetov / hsmcpp / 4957453907

pending completion
4957453907

push

github

igor-krechetov
[0.37.0][r] fix early delete issue

74 of 74 new or added lines in 5 files covered. (100.0%)

2291 of 2482 relevant lines covered (92.3%)

1739.84 hits per line

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

94.98
/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::HsmEventDispatcherBase(const size_t eventsCacheSize) {
170✔
15
    mEnqueuedEvents.reserve(eventsCacheSize);
170✔
16
}
170✔
17

18
void HsmEventDispatcherBase::handleDelete(HsmEventDispatcherBase* dispatcher) {
170✔
19
    if (nullptr != dispatcher) {
170✔
20
        dispatcher->stop();
170✔
21

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

29
void HsmEventDispatcherBase::stop() {
424✔
30
    HSM_TRACE_CALL_DEBUG();
424✔
31
    mStopDispatcher = true;
424✔
32
    // NOTE: child classes must provide additional logic
33
}
424✔
34

35
HandlerID_t HsmEventDispatcherBase::registerEventHandler(const EventHandlerFunc_t& handler) {
1,310✔
36
    HSM_TRACE_CALL_DEBUG();
1,310✔
37
    HandlerID_t id = getNextHandlerID();
1,310✔
38
    LockGuard lck(mHandlersSync);
1,310✔
39

40
    mEventHandlers.emplace(id, handler);
1,310✔
41

42
    return id;
1,310✔
43
}
1,310✔
44

45
void HsmEventDispatcherBase::unregisterEventHandler(const HandlerID_t handlerID) {
1,180✔
46
    HSM_TRACE_CALL_DEBUG_ARGS("handlerID=%d", handlerID);
1,180✔
47

48
    {
1,180✔
49
        LockGuard lck(mEmitSync);
1,180✔
50
        mPendingEvents.remove(handlerID);
1,180✔
51
    }
1,180✔
52
    {
1,180✔
53
        LockGuard lck(mHandlersSync);
1,180✔
54
        mEventHandlers.erase(handlerID);
1,180✔
55
    }
1,180✔
56
}
1,180✔
57

58
void HsmEventDispatcherBase::emitEvent(const HandlerID_t handlerID) {
4,559✔
59
    HSM_TRACE_CALL_DEBUG();
4,559✔
60

61
    {
4,559✔
62
        LockGuard lck(mEmitSync);
4,559✔
63
        mPendingEvents.push_back(handlerID);
4,559✔
64
    }
4,559✔
65

66
    notifyDispatcherAboutEvent();
4,559✔
67

68
    // NOTE: this is not a full implementation. child classes must implement additional logic
69
}
4,559✔
70

71
bool HsmEventDispatcherBase::enqueueEvent(const HandlerID_t handlerID, const EventID_t event) {
10✔
72
    bool wasAdded = false;
10✔
73

74
    if (mEnqueuedEvents.size() < mEnqueuedEvents.capacity()) {
10✔
75
        EnqueuedEventInfo newEvent = {0};
10✔
76

77
        newEvent.handlerID = handlerID;
10✔
78
        newEvent.eventID = event;
10✔
79

80
        {
10✔
81
            CriticalSection cs(mEnqueuedEventsSync);
10✔
82
            mEnqueuedEvents.push_back(newEvent);
10✔
83
        }
10✔
84

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

89
    return wasAdded;
10✔
90
}
91

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

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

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

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

108
    mEnqueuedEventHandlers.emplace(id, handler);
1,190✔
109

110
    return id;
1,190✔
111
}
1,190✔
112

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

117
    mEnqueuedEventHandlers.erase(handlerID);
1,180✔
118
}
1,180✔
119

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

124
    mTimerHandlers.emplace(newID, handler);
1,190✔
125

126
    return newID;
1,190✔
127
}
1,190✔
128

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

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

143
        mTimerHandlers.erase(itHandler);
1,180✔
144
    }
145
}
1,180✔
146

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

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

161
        if (mActiveTimers.end() != it) {
160✔
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;
160✔
171

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

176
            mActiveTimers.emplace(timerID, newTimer);
160✔
177
            startTimerImpl(timerID, intervalMs, isSingleShot);
160✔
178
        }
179
    }
180
}
160✔
181

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

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

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

198
    HSM_TRACE_DEBUG("mActiveTimers=%lu", mActiveTimers.size());
30✔
199

200
    if (mActiveTimers.end() != it) {
30✔
201
        stopTimerImpl(timerID);
30✔
202
        mActiveTimers.erase(it);
30✔
203
    }
204
}
30✔
205

206
bool HsmEventDispatcherBase::isTimerRunning(const TimerID_t timerID) {
60✔
207
    return (mActiveTimers.find(timerID) != mActiveTimers.end());
60✔
208
}
209

210
int HsmEventDispatcherBase::getNextHandlerID() {
3,690✔
211
    return mNextHandlerId++;
3,690✔
212
}
213

214
void HsmEventDispatcherBase::unregisterAllEventHandlers() {
424✔
215
    LockGuard lck(mHandlersSync);
424✔
216
    mEventHandlers.clear();
424✔
217
}
424✔
218

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

224
    if (mEnqueuedEventHandlers.end() != it) {
10✔
225
        func = it->second;
10✔
226
    }
227

228
    return func;
10✔
229
}
×
230

231
TimerHandlerFunc_t HsmEventDispatcherBase::getTimerHandlerFunc(const HandlerID_t handlerID) const {
140✔
232
    // cppcheck-suppress misra-c2012-17.7 ; false-positive. This a function pointer, not function call.
233
    TimerHandlerFunc_t func;
140✔
234
    auto it = mTimerHandlers.find(handlerID);
140✔
235

236
    if (mTimerHandlers.end() != it) {
140✔
237
        func = it->second;
140✔
238
    }
239

240
    return func;
140✔
241
}
×
242

243
void HsmEventDispatcherBase::startTimerImpl(const TimerID_t timerID, const unsigned int intervalMs, const bool isSingleShot) {
×
244
    // do nothing. must be implemented in platfrom specific dispatcher
245
}
×
246

247
void HsmEventDispatcherBase::stopTimerImpl(const TimerID_t timerID) {
×
248
    // do nothing. must be implemented in platfrom specific dispatcher
249
}
×
250

251
bool HsmEventDispatcherBase::handleTimerEvent(const TimerID_t timerID) {
140✔
252
    HSM_TRACE_CALL_DEBUG_ARGS("timerID=%d", SC2INT(timerID));
140✔
253
    bool restartTimer = false;
140✔
254

255
    if (INVALID_HSM_TIMER_ID != timerID) {
140✔
256
        // NOTE: should lock the whole block to prevent situation when timer handler is unregistered during handler execution
257
        LockGuard lck(mHandlersSync);
140✔
258

259
        auto itTimer = mActiveTimers.find(timerID);
140✔
260

261
        if (mActiveTimers.end() != itTimer) {
140✔
262
            HSM_TRACE_DEBUG("curTimer.handlerID=%d", itTimer->second.handlerID);
140✔
263

264
            if (INVALID_HSM_DISPATCHER_HANDLER_ID != itTimer->second.handlerID) {
140✔
265
                TimerHandlerFunc_t timerHandler = getTimerHandlerFunc(itTimer->second.handlerID);
140✔
266

267
                restartTimer = ((true == itTimer->second.isSingleShot) ? false : true);
140✔
268

269
                // Remove singleshot timer from active list
270
                if (false == restartTimer) {
140✔
271
                    mActiveTimers.erase(itTimer);
100✔
272
                }
273

274
                timerHandler(timerID);
280✔
275
            }
140✔
276
        }
277
    }
140✔
278

279
    return restartTimer;
140✔
280
}
281

282
void HsmEventDispatcherBase::dispatchEnqueuedEvents() {
3,848✔
283
    if ((false == mStopDispatcher) && (false == mEnqueuedEvents.empty())) {
3,848✔
284
        HandlerID_t prevHandlerID = INVALID_HSM_DISPATCHER_HANDLER_ID;
10✔
285
        EnqueuedEventHandlerFunc_t callback;
10✔
286
        std::vector<EnqueuedEventInfo> currentEvents;
10✔
287

288
        {
10✔
289
            CriticalSection lck(mEnqueuedEventsSync);
10✔
290

291
            // copy instead of move. we want to keep memory allocated in mEnqueuedEvents
292
            currentEvents = mEnqueuedEvents;
10✔
293
            mEnqueuedEvents.clear();
10✔
294
        }
10✔
295

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

303
            callback(it->eventID);
20✔
304
        }
305
    }
20✔
306
}
3,848✔
307

308
void HsmEventDispatcherBase::dispatchPendingActions() {
3,848✔
309
    if (false == mPendingActions.empty()) {
3,848✔
310
        std::list<ActionHandlerFunc_t> actionsSnapshot;
100✔
311

312
        {
100✔
313
            LockGuard lck(mEmitSync);
100✔
314
            actionsSnapshot = std::move(mPendingActions);
100✔
315
        }
100✔
316

317
        for (const ActionHandlerFunc_t& actionCallback: actionsSnapshot) {
200✔
318
            actionCallback();
200✔
319
        }
320
    }
100✔
321
}
3,848✔
322

323
void HsmEventDispatcherBase::dispatchPendingEvents() {
3,848✔
324
    std::list<HandlerID_t> events;
3,848✔
325

326
    dispatchPendingActions();
3,848✔
327

328
    if (false == mPendingEvents.empty()) {
3,848✔
329
        LockGuard lck(mEmitSync);
2,903✔
330
        events = std::move(mPendingEvents);
2,903✔
331
    }
2,903✔
332

333
    dispatchPendingEventsImpl(events);
3,848✔
334
}
3,848✔
335

336
void HsmEventDispatcherBase::dispatchPendingEventsImpl(const std::list<HandlerID_t>& events) {
3,848✔
337
    dispatchEnqueuedEvents();
3,848✔
338

339
    if ((false == mStopDispatcher) && (false == events.empty())) {
3,848✔
340
        std::map<HandlerID_t, EventHandlerFunc_t> eventHandlersCopy;
2,903✔
341

342
        {
2,903✔
343
            // TODO: workaround to prevent recursive lock if registerEventHandler/unregisterEventHandler is called from handler
344
            // callback
345
            LockGuard lck(mHandlersSync);
2,903✔
346
            eventHandlersCopy = mEventHandlers;
2,903✔
347
        }
2,903✔
348

349
        for (auto it = events.begin(); (it != events.end()) && (false == mStopDispatcher); ++it) {
6,428✔
350
            auto itHandler = eventHandlersCopy.find(*it);
3,525✔
351

352
            if (itHandler != eventHandlersCopy.end()) {
3,525✔
353
                // NOTE: if callback returns FALSE it means the handler doesn't want to process more events
354
                if (false == itHandler->second()) {
6,830✔
355
                    eventHandlersCopy.erase(itHandler);
140✔
356
                }
357
            }
358
        }
359
    }
2,903✔
360
}
3,848✔
361

362
}  // 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