Skip to content

Commit 8b18f2b

Browse files
authored
Merge pull request #85 from KDAB/wip/eventLoop
2 parents 8ea96b3 + 98c1795 commit 8b18f2b

34 files changed

+1189
-209
lines changed

.github/workflows/linters.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ jobs:
7777
cppcheck --quiet --language=c++ --force --inline-suppr
7878
--enable=warning,performance,portability,information
7979
--disable=missingInclude --check-level=exhaustive
80-
--suppress="ctuOneDefinitionRuleViolation:tests/*"
8180
-i build --library=std.cfg --error-exitcode=42 .
8281
8382
if($LASTEXITCODE -eq 42) {

src/KDFoundation/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ find_package(Threads)
1212

1313
set(SOURCES
1414
core_application.cpp
15+
event_loop.cpp
1516
event_receiver.cpp
1617
event.cpp
1718
file_descriptor_notifier.cpp
@@ -25,6 +26,7 @@ set(HEADERS
2526
constexpr_sort.h
2627
core_application.h
2728
destruction_helpers.h
29+
event_loop.h
2830
event_queue.h
2931
event_receiver.h
3032
event.h

src/KDFoundation/core_application.cpp

Lines changed: 27 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,27 @@ using namespace KDFoundation;
2727

2828
CoreApplication *CoreApplication::ms_application = nullptr;
2929

30-
CoreApplication::CoreApplication(std::unique_ptr<AbstractPlatformIntegration> &&platformIntegration)
30+
namespace {
31+
std::unique_ptr<AbstractPlatformIntegration> createPlatformIntegration()
32+
{
33+
// Create a platform integration
34+
#if defined(PLATFORM_LINUX)
35+
return std::make_unique<LinuxPlatformIntegration>();
36+
#elif defined(PLATFORM_WIN32)
37+
return std::make_unique<Win32PlatformIntegration>();
38+
#elif defined(PLATFORM_MACOS)
39+
return std::make_unique<MacOSPlatformIntegration>();
40+
#else
41+
static_assert(false, "No valid platform integration could be found.");
42+
#endif
43+
}
44+
} // namespace
45+
46+
CoreApplication::CoreApplication(std::unique_ptr<AbstractPlatformIntegration> &&platformIntegration, std::unique_ptr<KDFoundation::AbstractPlatformEventLoop> &&platformEventLoop)
3147
: m_defaultLogger{ KDUtils::Logger::logger("default_log", spdlog::level::info) }
32-
, m_platformIntegration{ std::move(platformIntegration) }
48+
, m_platformIntegration{ platformIntegration ? std::move(platformIntegration) : createPlatformIntegration() }
3349
, m_logger{ KDUtils::Logger::logger("core_application") }
50+
, m_eventLoop{ platformEventLoop ? std::move(platformEventLoop) : m_platformIntegration->createPlatformEventLoop() }
3451
{
3552
assert(ms_application == nullptr);
3653
ms_application = this;
@@ -41,25 +58,7 @@ CoreApplication::CoreApplication(std::unique_ptr<AbstractPlatformIntegration> &&
4158
if (const char *display = std::getenv("DISPLAY")) // NOLINT(concurrency-mt-unsafe)
4259
SPDLOG_LOGGER_INFO(m_logger, "DISPLAY={}", display);
4360

44-
// Create a default postman object
45-
m_postman = std::make_unique<Postman>();
46-
47-
// Create a platform integration
48-
if (!m_platformIntegration) {
49-
#if defined(PLATFORM_LINUX)
50-
m_platformIntegration = std::make_unique<LinuxPlatformIntegration>();
51-
#elif defined(PLATFORM_WIN32)
52-
m_platformIntegration = std::make_unique<Win32PlatformIntegration>();
53-
#elif defined(PLATFORM_MACOS)
54-
m_platformIntegration = std::make_unique<MacOSPlatformIntegration>();
55-
#endif
56-
}
57-
58-
// Ask the platform integration to create us a suitable event loop
59-
assert(m_platformIntegration);
60-
m_platformEventLoop = m_platformIntegration->createPlatformEventLoop();
6161
m_platformIntegration->init();
62-
m_platformEventLoop->setPostman(m_postman.get());
6362
}
6463

6564
CoreApplication::~CoreApplication()
@@ -68,60 +67,34 @@ CoreApplication::~CoreApplication()
6867
// and immediately return after processing events (timeout = 0).
6968
processEvents(0);
7069

71-
// Destroy the event loop and platform integration before removing the app instance
72-
m_platformEventLoop = std::unique_ptr<AbstractPlatformEventLoop>();
73-
m_platformIntegration = std::unique_ptr<AbstractPlatformIntegration>();
70+
// Destroy the platform integration before removing the app instance
71+
m_platformIntegration.reset();
7472
ms_application = nullptr;
7573
}
7674

7775
std::shared_ptr<KDBindings::ConnectionEvaluator> CoreApplication::connectionEvaluator()
7876
{
79-
return eventLoop()->connectionEvaluator();
77+
return m_eventLoop.connectionEvaluator();
8078
}
8179

8280
void CoreApplication::postEvent(EventReceiver *target, std::unique_ptr<Event> &&event)
8381
{
84-
assert(target != nullptr);
85-
assert(event->type() != Event::Type::Invalid);
86-
m_eventQueue.push(target, std::forward<std::unique_ptr<Event>>(event));
87-
m_platformEventLoop->wakeUp();
82+
m_eventLoop.postEvent(target, std::forward<std::unique_ptr<Event>>(event));
8883
}
8984

9085
void CoreApplication::sendEvent(EventReceiver *target, Event *event)
9186
{
92-
SPDLOG_LOGGER_TRACE(m_logger, "{}()", __FUNCTION__);
93-
m_postman->deliverEvent(target, event);
87+
m_eventLoop.sendEvent(target, event);
9488
}
9589

9690
void CoreApplication::processEvents(int timeout)
9791
{
98-
// Deliver any events that have already been posted
99-
for (size_t eventIndex = 0, eventCount = m_eventQueue.size(); eventIndex < eventCount; ++eventIndex) {
100-
auto postedEvent = m_eventQueue.tryPop();
101-
if (!postedEvent)
102-
break;
103-
const auto target = postedEvent->target();
104-
const auto ev = postedEvent->wrappedEvent();
105-
106-
m_postman->deliverEvent(target, ev);
107-
}
108-
109-
// Poll/wait for new events
110-
if (!m_platformEventLoop)
111-
return;
112-
m_platformEventLoop->waitForEvents(timeout);
92+
m_eventLoop.processEvents(timeout);
11393
}
11494

11595
int CoreApplication::exec()
11696
{
117-
if (!m_platformEventLoop)
118-
return 1;
119-
120-
while (!m_quitRequested)
121-
processEvents(-1);
122-
m_quitRequested = false;
123-
124-
return 0;
97+
return m_eventLoop.exec();
12598
}
12699

127100
void CoreApplication::quit()
@@ -137,12 +110,7 @@ AbstractPlatformIntegration *CoreApplication::platformIntegration()
137110
void CoreApplication::event(EventReceiver *target, Event *event)
138111
{
139112
if (event->type() == Event::Type::Quit) {
140-
// After setting the quit flag to true, we must wake up the event loop once more,
141-
// because processEvents() goes back into waitForEvents() after processing the
142-
// queued events. Without the wakeUp() call it would wait until some other event
143-
// woke it up again. Let's kick it ourselves.
144-
m_quitRequested = true;
145-
m_platformEventLoop->wakeUp();
113+
m_eventLoop.quit();
146114
event->setAccepted(true);
147115
}
148116

src/KDFoundation/core_application.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <KDFoundation/kdfoundation_global.h>
1515
#include <KDFoundation/object.h>
16+
#include <KDFoundation/event_loop.h>
1617
#include <KDFoundation/event_queue.h>
1718
#include <KDFoundation/platform/abstract_platform_integration.h>
1819

@@ -37,19 +38,22 @@ class KDFOUNDATION_API CoreApplication : public Object
3738
KDBindings::Property<std::string> applicationName{};
3839
KDBindings::Property<std::string> organizationName{};
3940

40-
CoreApplication(std::unique_ptr<AbstractPlatformIntegration> &&platformIntegration = {});
41+
CoreApplication(std::unique_ptr<AbstractPlatformIntegration> &&platformIntegration = {}, std::unique_ptr<KDFoundation::AbstractPlatformEventLoop> &&platformEventLoop = {});
4142
~CoreApplication() override;
4243

4344
static inline CoreApplication *instance() { return ms_application; }
4445

45-
const AbstractPlatformEventLoop *eventLoop() const { return m_platformEventLoop.get(); }
46-
AbstractPlatformEventLoop *eventLoop() { return m_platformEventLoop.get(); }
46+
const EventLoop *eventLoop() const { return &m_eventLoop; }
47+
EventLoop *eventLoop() { return &m_eventLoop; }
4748

48-
Postman *postman() { return m_postman.get(); }
49+
const AbstractPlatformEventLoop *platformEventLoop() const { return m_eventLoop.platformEventLoop(); }
50+
AbstractPlatformEventLoop *platformEventLoop() { return m_eventLoop.platformEventLoop(); }
51+
52+
Postman *postman() { return m_eventLoop.postman(); }
4953

5054
void postEvent(EventReceiver *target, std::unique_ptr<Event> &&event);
51-
void removeAllEventsTargeting(EventReceiver &evReceiver) { m_eventQueue.removeAllEventsTargeting(evReceiver); }
52-
EventQueue::size_type eventQueueSize() const { return m_eventQueue.size(); }
55+
void removeAllEventsTargeting(EventReceiver &evReceiver) { m_eventLoop.removeAllEventsTargeting(evReceiver); }
56+
EventQueue::size_type eventQueueSize() const { return m_eventLoop.eventQueueSize(); }
5357

5458
void sendEvent(EventReceiver *target, Event *event);
5559

@@ -68,14 +72,10 @@ class KDFOUNDATION_API CoreApplication : public Object
6872
std::shared_ptr<spdlog::logger> m_defaultLogger;
6973

7074
private:
71-
void init();
7275
void event(EventReceiver *target, Event *event) override;
7376

74-
EventQueue m_eventQueue;
75-
bool m_quitRequested{ false };
7677
std::unique_ptr<AbstractPlatformIntegration> m_platformIntegration;
77-
std::unique_ptr<AbstractPlatformEventLoop> m_platformEventLoop;
78-
std::unique_ptr<Postman> m_postman;
78+
EventLoop m_eventLoop;
7979
std::shared_ptr<spdlog::logger> m_logger;
8080
};
8181

src/KDFoundation/event_loop.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* This file is part of KDUtils.
3+
*
4+
* SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5+
* Author: Anton Kreuzkamp <anton.kreuzkamp@kdab.com>
6+
* Author: Paul Lemire <paul.lemire@kdab.com>
7+
*
8+
* SPDX-License-Identifier: MIT
9+
*
10+
* Contact KDAB at <info@kdab.com> for commercial licensing options.
11+
*/
12+
13+
#include <KDFoundation/event_loop.h>
14+
#include <KDFoundation/core_application.h>
15+
#include <KDFoundation/platform/abstract_platform_event_loop.h>
16+
#include "postman.h"
17+
18+
#if defined(PLATFORM_LINUX)
19+
#include <KDFoundation/platform/linux/linux_platform_integration.h>
20+
#elif defined(PLATFORM_WIN32)
21+
#include <KDFoundation/platform/win32/win32_platform_integration.h>
22+
#elif defined(PLATFORM_MACOS)
23+
#include <KDFoundation/platform/macos/macos_platform_integration.h>
24+
#endif
25+
26+
#include <cassert>
27+
28+
using namespace KDFoundation;
29+
30+
namespace {
31+
thread_local EventLoop *s_eventLoopInstance = nullptr;
32+
}
33+
34+
EventLoop *EventLoop::instance()
35+
{
36+
return s_eventLoopInstance;
37+
}
38+
39+
namespace {
40+
std::unique_ptr<AbstractPlatformEventLoop> createPlatformEventLoop()
41+
{
42+
// Ask the platform integration to create us a suitable event loop
43+
CoreApplication *app = CoreApplication::instance();
44+
assert(app && "Cannot use EventLoop without a CoreApplication.");
45+
AbstractPlatformIntegration *platformIntegration = app->platformIntegration();
46+
assert(platformIntegration);
47+
return platformIntegration->createPlatformEventLoop();
48+
}
49+
} // namespace
50+
51+
EventLoop::EventLoop(std::unique_ptr<AbstractPlatformEventLoop> platformEventLoop)
52+
: m_platformEventLoop(platformEventLoop ? std::move(platformEventLoop) : createPlatformEventLoop())
53+
{
54+
// Create a default postman object
55+
m_postman = std::make_unique<Postman>();
56+
m_platformEventLoop->setPostman(m_postman.get());
57+
58+
assert(s_eventLoopInstance == nullptr && "Cannot have more than one event loop per thread.");
59+
s_eventLoopInstance = this;
60+
}
61+
62+
EventLoop::~EventLoop()
63+
{
64+
// Destroy the platform event loop before removing the event loop instance
65+
m_platformEventLoop.reset();
66+
s_eventLoopInstance = nullptr;
67+
}
68+
69+
std::shared_ptr<KDBindings::ConnectionEvaluator> EventLoop::connectionEvaluator()
70+
{
71+
return m_platformEventLoop->connectionEvaluator();
72+
}
73+
74+
void EventLoop::postEvent(EventReceiver *target, std::unique_ptr<Event> &&event)
75+
{
76+
assert(target != nullptr);
77+
assert(event->type() != Event::Type::Invalid);
78+
m_eventQueue.push(target, std::forward<std::unique_ptr<Event>>(event));
79+
m_platformEventLoop->wakeUp();
80+
}
81+
82+
void EventLoop::sendEvent(EventReceiver *target, Event *event)
83+
{
84+
m_postman->deliverEvent(target, event);
85+
}
86+
87+
void EventLoop::processEvents(int timeout)
88+
{
89+
// Deliver any events that have already been posted
90+
for (size_t eventIndex = 0, eventCount = m_eventQueue.size(); eventIndex < eventCount; ++eventIndex) {
91+
auto postedEvent = m_eventQueue.tryPop();
92+
if (!postedEvent)
93+
break;
94+
const auto target = postedEvent->target();
95+
const auto ev = postedEvent->wrappedEvent();
96+
97+
m_postman->deliverEvent(target, ev);
98+
}
99+
100+
// Poll/wait for new events
101+
if (!m_platformEventLoop)
102+
return;
103+
m_platformEventLoop->waitForEvents(timeout);
104+
}
105+
106+
int EventLoop::exec()
107+
{
108+
if (!m_platformEventLoop)
109+
return 1;
110+
111+
while (!m_quitRequested)
112+
processEvents(-1);
113+
m_quitRequested = false;
114+
115+
return 0;
116+
}
117+
118+
void EventLoop::quit()
119+
{
120+
// After setting the quit flag to true, we must wake up the event loop once more,
121+
// because processEvents() goes back into waitForEvents() after processing the
122+
// queued events. Without the wakeUp() call it would wait until some other event
123+
// woke it up again. Let's kick it ourselves.
124+
m_quitRequested = true;
125+
m_platformEventLoop->wakeUp();
126+
}

0 commit comments

Comments
 (0)