Skip to content

Commit

Permalink
CoreApplication::processEvents: fix infinite loop
Browse files Browse the repository at this point in the history
We don't want to end up in an infinite loop if the processing
of an events ends up in the posting of another event.

Change-Id: I13ba00f41f5204881f6c2da41404b1e0eee9d7a5
Reviewed-on: https://codereview.kdab.com/c/kdab/kdutils/+/136259
Reviewed-by: Sean Harmer <[email protected]>
Tested-by: Continuous Integration <[email protected]>
  • Loading branch information
lemirep committed Jan 11, 2024
1 parent 72364fc commit e9d69ca
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/KDFoundation/core_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ void CoreApplication::sendEvent(EventReceiver *target, Event *event)
void CoreApplication::processEvents(int timeout)
{
// Deliver any events that have already been posted
while (auto postedEvent = m_eventQueue.tryPop()) {
for (size_t eventIndex = 0, eventCount = m_eventQueue.size(); eventIndex < eventCount; ++eventIndex) {
auto postedEvent = m_eventQueue.tryPop();
if (!postedEvent)
break;
const auto target = postedEvent->target();
const auto ev = postedEvent->wrappedEvent();

Expand Down
59 changes: 59 additions & 0 deletions tests/auto/foundation/core_application/tst_core_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <KDFoundation/config.h>
#include <KDFoundation/core_application.h>
#include <KDFoundation/timer.h>
#include <KDFoundation/event.h>
#include <KDUtils/logging.h>

#include <condition_variable>
Expand Down Expand Up @@ -74,6 +75,37 @@ class EventObject : public Object
bool m_userEventDelivered = false;
};

class RecursiveEventPosterObject : public Object
{
public:
RecursiveEventPosterObject()
: Object()
{
}

size_t eventsProcessed() const { return m_eventsProcessed; }

void requestUpdate()
{
CoreApplication::instance()->postEvent(this, std::make_unique<UpdateEvent>());
}

protected:
void event(EventReceiver *target, Event *ev) override
{
if (target == this && ev->type() == Event::Type::Update) {
++m_eventsProcessed;
ev->setAccepted(true);
requestUpdate();
}

Object::event(target, ev);
}

private:
size_t m_eventsProcessed{ 0 };
};

TEST_CASE("Creation")
{
SUBCASE("default construction")
Expand Down Expand Up @@ -127,6 +159,33 @@ TEST_CASE("Event handling")

app.processEvents();
}

SUBCASE("don't end up in infinite loop")
{
// GIVEN
CoreApplication app;
auto obj = std::make_unique<RecursiveEventPosterObject>();

// WHEN
obj->requestUpdate();
app.processEvents();

// THEN
CHECK(obj->eventsProcessed() == 1);

// WHEN
app.processEvents();

// THEN
CHECK(obj->eventsProcessed() == 2);

// WHEN
for (size_t i = 0; i < 10; ++i)
app.processEvents();

// THEN
CHECK(obj->eventsProcessed() == 12);
}
}

#ifndef KD_PLATFORM_MACOS
Expand Down

0 comments on commit e9d69ca

Please sign in to comment.