Zephyr C++ Toolkit
Loading...
Searching...
No Matches
EventThread.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <variant>
5
6#include <zephyr/kernel.h>
7#include <zephyr/logging/log.h>
8
9#include "Timer.hpp"
10#include "TimerManager.hpp"
11
12namespace zct {
13
14// Don't use constexpr here, it seg faults!
15// static constexpr int LOG_LEVEL = LOG_LEVEL_DBG;
16#define ZCT_EVENT_THREAD_LOG_LEVEL LOG_LEVEL_WRN
17
36template <typename EventType>
38
39public:
40
46 using MsgQueueItem = std::variant<EventType, std::function<void()>>;
47
63 const char* name,
64 k_thread_stack_t* threadStack,
65 size_t threadStackSize,
66 int threadPriority,
67 size_t eventQueueBufferNumItems
68 ) :
69 m_threadStack(threadStack),
70 m_threadStackSize(threadStackSize),
71 m_threadPriority(threadPriority),
73 {
74 LOG_MODULE_DECLARE(EventThread, ZCT_EVENT_THREAD_LOG_LEVEL);
75 LOG_DBG("EventThread constructor called.");
76 m_name = name;
77
78 // Create event queue buffer and then init queue with it
79 m_msgQueueBuffer = new MsgQueueItem[eventQueueBufferNumItems];
80 __ASSERT_NO_MSG(m_msgQueueBuffer != nullptr);
81 k_msgq_init(&m_threadMsgQueue, (char*)m_msgQueueBuffer, sizeof(MsgQueueItem), eventQueueBufferNumItems);
82 };
83
92 LOG_MODULE_DECLARE(EventThread, ZCT_EVENT_THREAD_LOG_LEVEL);
93 LOG_DBG("%s() called.", __FUNCTION__);
94 k_thread_join(&m_thread, K_FOREVER);
95 }
96
104 void start() {
105 LOG_MODULE_DECLARE(EventThread, ZCT_EVENT_THREAD_LOG_LEVEL);
106 LOG_DBG("EventThread start() called.");
107
108 // Start the thread
109 k_thread_create(
110 &m_thread,
114 this, // Pass in the instance of the class
115 NULL,
116 NULL,
118 0,
119 K_NO_WAIT);
120 // Name the thread for easier debugging/logging
121 k_thread_name_set(&m_thread, m_name);
122 }
123
135 void onExternalEvent(std::function<void(const EventType&)> callback) {
136 m_externalEventCallback = callback;
137 }
138
146 void sendEvent(const EventType& event) {
147 MsgQueueItem item = event;
148 k_msgq_put(&m_threadMsgQueue, &item, K_NO_WAIT);
149 }
150
166
178 m_exitEventLoop = true;
179 }
180
187 void runInLoop(std::function<void()> func) {
188 MsgQueueItem item = func;
189 k_msgq_put(&m_threadMsgQueue, &item, K_NO_WAIT);
190 }
191
192protected:
193
195 static void staticThreadFunction(void* arg1, void* arg2, void* arg3)
196 {
197 LOG_MODULE_DECLARE(EventThread, ZCT_EVENT_THREAD_LOG_LEVEL);
198 ARG_UNUSED(arg2);
199 ARG_UNUSED(arg3);
200
201 // First passed in argument is the instance of the class
202 EventThread* obj = static_cast<EventThread*>(arg1);
203 // Run the event loop. This should not return unless the user calls exitEventLoop().
204 obj->runEventLoop();
205 // If we get here, the user decided to exit the thread
206 }
207
219 LOG_MODULE_DECLARE(EventThread, ZCT_EVENT_THREAD_LOG_LEVEL);
220 LOG_DBG("%s() called.", __FUNCTION__);
221
222 while (!m_exitEventLoop) {
223 // Get the next timer to expire
224 auto nextTimerInfo = m_timerManager.getNextExpiringTimer();
225
226 // Check for expired timers and call their callbacks
227 while (nextTimerInfo.m_timer != nullptr && nextTimerInfo.m_durationToWaitUs == 0) {
228 LOG_DBG("Timer expired. Timer: %p.", nextTimerInfo.m_timer);
229 // Update the timer after expiry
230 nextTimerInfo.m_timer->updateAfterExpiry();
231
232 // Call the callback
233 const auto& callback = nextTimerInfo.m_timer->getExpiryCallback();
234 if (callback) {
235 callback();
236 // If the callback calls exitEventLoop(), we will exit the event loop
237 // and return from runEventLoop().
238 if (m_exitEventLoop) {
239 return;
240 }
241 }
242
243 // Check for the next expired timer
244 nextTimerInfo = m_timerManager.getNextExpiringTimer();
245 }
246
247 // If we get here, we have handled all expired timers.
248 k_timeout_t timeout;
249 if (nextTimerInfo.m_timer != nullptr) {
250 timeout = Z_TIMEOUT_US(nextTimerInfo.m_durationToWaitUs);
251 } else {
252 timeout = K_FOREVER;
253 }
254
255 // Block on message queue until next timer expiry
256 // Create storage in the case we receive an external event
257 MsgQueueItem msgQueueItem;
258 int queueRc = k_msgq_get(&m_threadMsgQueue, &msgQueueItem, timeout);
259 if (queueRc == 0) {
260 // We got a message from the queue, it will either be an event or a function to run in the context of the event thread.
261 if (std::holds_alternative<EventType>(msgQueueItem)) {
262 // It's an event, call the external event callback
263 const auto& event = std::get<EventType>(msgQueueItem);
266 // If the callback calls exitEventLoop(), we will exit the event loop
267 // and return from runEventLoop().
268 if (m_exitEventLoop) {
269 return;
270 }
271 } else {
272 LOG_WRN("Received external event in event thread \"%s\" but no external event callback is registered.", m_name);
273 }
274 } else {
275 // It's a function to run in the context of the event thread, run it
276 std::get<std::function<void()>>(msgQueueItem)();
277 }
278 continue;
279 } else if (queueRc == -EAGAIN) {
280 // Queue timed out, which means we need to handle the timer expiry,
281 // jump back to start of while loop to handle the timer expiry
282 LOG_DBG("Queue timed out, which means we need to handle timer expiry.");
283 continue;
284 } else if (queueRc == -ENOMSG) {
285 // This means the queue must have been purged
286 __ASSERT(false, "Got -ENOMSG from queue, was not expecting this.");
287 } else {
288 __ASSERT(false, "Got unexpected return code from queue: %d.", queueRc);
289 }
290 } // End of while (true) loop
291 }
292
293 const char* m_name = nullptr;
294 void* m_msgQueueBuffer = nullptr;
295
296 struct k_thread m_thread;
297 struct k_msgq m_threadMsgQueue;
298
299 k_thread_stack_t* m_threadStack;
302
304 std::function<void(const EventType&)> m_externalEventCallback = nullptr;
305
309 bool m_exitEventLoop = false;
310};
311
317} // namespace zct
#define ZCT_EVENT_THREAD_LOG_LEVEL
Definition EventThread.hpp:16
Use this class in your objects to create a thread that can wait for events.
Definition EventThread.hpp:37
const char * m_name
Definition EventThread.hpp:293
size_t m_threadStackSize
Definition EventThread.hpp:300
void onExternalEvent(std::function< void(const EventType &)> callback)
Definition EventThread.hpp:135
EventThread(const char *name, k_thread_stack_t *threadStack, size_t threadStackSize, int threadPriority, size_t eventQueueBufferNumItems)
Definition EventThread.hpp:62
void exitEventLoop()
Definition EventThread.hpp:177
int m_threadPriority
Definition EventThread.hpp:301
void sendEvent(const EventType &event)
Definition EventThread.hpp:146
std::variant< EventType, std::function< void()> > MsgQueueItem
Definition EventThread.hpp:46
void runEventLoop()
Definition EventThread.hpp:218
~EventThread()
Definition EventThread.hpp:91
void * m_msgQueueBuffer
Definition EventThread.hpp:294
TimerManager & timerManager()
Definition EventThread.hpp:163
void start()
Definition EventThread.hpp:104
static void staticThreadFunction(void *arg1, void *arg2, void *arg3)
Definition EventThread.hpp:195
struct k_thread m_thread
Definition EventThread.hpp:296
struct k_msgq m_threadMsgQueue
Definition EventThread.hpp:297
std::function< void(const EventType &)> m_externalEventCallback
Definition EventThread.hpp:304
TimerManager m_timerManager
Definition EventThread.hpp:303
void runInLoop(std::function< void()> func)
Definition EventThread.hpp:187
k_thread_stack_t * m_threadStack
Definition EventThread.hpp:299
bool m_exitEventLoop
Definition EventThread.hpp:309
Definition TimerManager.hpp:35
TimerExpiryInfo getNextExpiringTimer()
Definition TimerManager.hpp:94
Definition Mutex.hpp:6