Ladybird: Allow posting events to the Qt event loop from other threads

Previously, a QTimer was used to start processing of our event queue in
the main Qt event loop. Unfortunately, QTimers are not thread-safe, and
disallow starting of a timer from a different thread than it was
created in.

Instead, use a dummy QObject to post a custom QEvent to the main loop
from whatever thread we like, and process our event queue when it is
received by our dummy object.
This commit is contained in:
Zaggy1024 2023-07-04 04:37:36 -05:00 committed by Andrew Kaster
parent 6131d879f7
commit 78e1defbfe
Notes: sideshowbarker 2024-07-17 03:14:39 +09:00
6 changed files with 72 additions and 7 deletions

View File

@ -84,6 +84,7 @@ set(SOURCES
BrowserWindow.cpp
ConsoleWidget.cpp
EventLoopImplementationQt.cpp
EventLoopImplementationQtEventTarget.cpp
HelperProcess.cpp
InspectorWidget.cpp
LocationEdit.cpp

View File

@ -5,6 +5,7 @@
*/
#include "EventLoopImplementationQt.h"
#include "EventLoopImplementationQtEventTarget.h"
#include <AK/IDAllocator.h>
#include <LibCore/Event.h>
#include <LibCore/Notifier.h>
@ -149,16 +150,21 @@ void EventLoopManagerQt::unregister_notifier(Core::Notifier& notifier)
void EventLoopManagerQt::did_post_event()
{
m_process_core_events_timer.start();
QCoreApplication::postEvent(m_main_thread_event_target.ptr(), new QtEventLoopManagerEvent(QtEventLoopManagerEvent::process_event_queue_event_type()));
}
bool EventLoopManagerQt::event_target_received_event(Badge<EventLoopImplementationQtEventTarget>, QEvent* event)
{
if (event->type() == QtEventLoopManagerEvent::process_event_queue_event_type()) {
Core::ThreadEventQueue::current().process();
return true;
}
return false;
}
EventLoopManagerQt::EventLoopManagerQt()
: m_main_thread_event_target(make<EventLoopImplementationQtEventTarget>())
{
m_process_core_events_timer.setSingleShot(true);
m_process_core_events_timer.setInterval(0);
QObject::connect(&m_process_core_events_timer, &QTimer::timeout, [] {
Core::ThreadEventQueue::current().process();
});
}
EventLoopManagerQt::~EventLoopManagerQt() = default;

View File

@ -6,16 +6,20 @@
#pragma once
#include <AK/Badge.h>
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <LibCore/EventLoopImplementation.h>
#include <QEvent>
#include <QEventLoop>
#include <QSocketNotifier>
#include <QTimer>
namespace Ladybird {
class EventLoopImplementationQtEventTarget;
class EventLoopManagerQt final : public Core::EventLoopManager {
public:
EventLoopManagerQt();
@ -29,13 +33,28 @@ public:
virtual void unregister_notifier(Core::Notifier&) override;
virtual void did_post_event() override;
static bool event_target_received_event(Badge<EventLoopImplementationQtEventTarget>, QEvent* event);
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
virtual int register_signal(int, Function<void(int)>) override { return 0; }
virtual void unregister_signal(int) override { }
private:
QTimer m_process_core_events_timer;
NonnullOwnPtr<EventLoopImplementationQtEventTarget> m_main_thread_event_target;
};
class QtEventLoopManagerEvent final : public QEvent {
public:
static QEvent::Type process_event_queue_event_type()
{
static auto const type = static_cast<QEvent::Type>(QEvent::registerEventType());
return type;
}
QtEventLoopManagerEvent(QEvent::Type type)
: QEvent(type)
{
}
};
class EventLoopImplementationQt final : public Core::EventLoopImplementation {

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "EventLoopImplementationQtEventTarget.h"
namespace Ladybird {
bool EventLoopImplementationQtEventTarget::event(QEvent* event)
{
return EventLoopManagerQt::event_target_received_event({}, event);
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <QEvent>
#include "EventLoopImplementationQt.h"
namespace Ladybird {
class EventLoopImplementationQtEventTarget final : public QObject {
Q_OBJECT
public:
virtual bool event(QEvent* event) override;
};
}

View File

@ -9,6 +9,7 @@ set(WEBCONTENT_SOURCES
../AudioCodecPluginQt.cpp
../AudioThread.cpp
../EventLoopImplementationQt.cpp
../EventLoopImplementationQtEventTarget.cpp
../FontPlugin.cpp
../HelperProcess.cpp
../ImageCodecPlugin.cpp