6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qthread.h>
8#include <QtCore/qscopedvaluerollback.h>
9#include <QtCore/private/qobject_p.h>
10#include <QtCore/private/qwasmglobal_p.h>
11#include <QtCore/private/qstdweb_p.h>
12#include <QtCore/private/qwasmsocket_p.h>
14using namespace std::chrono;
15using namespace std::chrono_literals;
25#define LOCK_GUARD(M) std::lock_guard<std::mutex> lock(M)
32static bool useAsyncify()
34 return qstdweb::haveAsyncify();
49Q_CONSTINIT std::shared_ptr<QWasmSuspendResumeControl> QEventDispatcherWasm::g_mainThreadSuspendResumeControl;
52Q_CONSTINIT QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
53Q_CONSTINIT std::mutex QEventDispatcherWasm::g_staticDataMutex;
56QEventDispatcherWasm::QEventDispatcherWasm(std::shared_ptr<QWasmSuspendResumeControl> suspendResumeControl)
69 qCDebug(lcEventDispatcher) <<
"Creating QEventDispatcherWasm instance" <<
this
70 <<
"is main thread" << emscripten_is_main_runtime_thread();
72 if (emscripten_is_main_runtime_thread()) {
76 Q_ASSERT(g_mainThreadEventDispatcher ==
nullptr);
77 g_mainThreadEventDispatcher =
this;
79 if (suspendResumeControl) {
80 g_mainThreadSuspendResumeControl = suspendResumeControl;
82 g_mainThreadSuspendResumeControl = std::make_shared<QWasmSuspendResumeControl>();
86 m_wakeupTimer = std::make_unique<QWasmTimer>(g_mainThreadSuspendResumeControl.get(), [](){ onWakeup(); });
89 m_nativeTimer = std::make_unique<QWasmTimer>(g_mainThreadSuspendResumeControl.get(), []() { onTimer(); });
92 m_suspendTimer = std::make_unique<QWasmTimer>(g_mainThreadSuspendResumeControl.get(), []() { onProcessNativeEventsResume(); });
95 std::lock_guard<std::mutex> lock(g_staticDataMutex);
96 g_secondaryThreadEventDispatchers.append(
this);
100 m_timerInfo = std::make_unique<QTimerInfoList>();
103QEventDispatcherWasm::~QEventDispatcherWasm()
105 qCDebug(lcEventDispatcher) <<
"Destroying QEventDispatcherWasm instance" <<
this;
108 m_wakeupTimer.reset();
109 m_nativeTimer.reset();
110 m_suspendTimer.reset();
113 if (isSecondaryThreadEventDispatcher()) {
114 std::lock_guard<std::mutex> lock(g_staticDataMutex);
115 g_secondaryThreadEventDispatchers.remove(g_secondaryThreadEventDispatchers.indexOf(
this));
119 QWasmSocket::clearSocketNotifiers();
120 g_mainThreadEventDispatcher =
nullptr;
121 g_mainThreadSuspendResumeControl.reset();
125bool QEventDispatcherWasm::isMainThreadEventDispatcher()
127 return this == g_mainThreadEventDispatcher;
130bool QEventDispatcherWasm::isSecondaryThreadEventDispatcher()
132 return this != g_mainThreadEventDispatcher;
135bool QEventDispatcherWasm::isValidEventDispatcher()
137 return isValidEventDispatcherPointer(
this);
140bool QEventDispatcherWasm::isValidEventDispatcherPointer(QEventDispatcherWasm *eventDispatcher)
142 if (eventDispatcher == g_mainThreadEventDispatcher)
145 if (g_secondaryThreadEventDispatchers.contains(eventDispatcher))
151bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
155 if (!useAsyncify() && isMainThreadEventDispatcher())
156 handleNonAsyncifyErrorCases(flags);
158 bool didSendEvents = sendAllEvents(flags);
160 if (!isValidEventDispatcher())
164 m_interrupted =
false;
168 bool shouldWait = flags.testFlag(QEventLoop::WaitForMoreEvents);
169 if (!shouldWait || didSendEvents)
170 return didSendEvents;
174 return sendAllEvents(flags);
177bool QEventDispatcherWasm::sendAllEvents(QEventLoop::ProcessEventsFlags flags)
179 bool didSendEvents =
false;
181 didSendEvents |= sendPostedEvents();
182 if (!isValidEventDispatcher())
185 didSendEvents |= sendNativeEvents(flags);
186 if (!isValidEventDispatcher())
189 didSendEvents |= sendTimerEvents();
190 if (!isValidEventDispatcher())
193 return didSendEvents;
196bool QEventDispatcherWasm::sendNativeEvents(QEventLoop::ProcessEventsFlags flags)
201 if (!isMainThreadEventDispatcher())
209 int sentEventCount = 0;
210 sentEventCount += g_mainThreadSuspendResumeControl->sendPendingEvents();
215 if (flags.testFlag(QEventLoop::EventLoopExec))
216 return sentEventCount > 0;
228 m_wakeFromSuspendTimer =
false;
230 m_suspendTimer->setTimeout(0ms);
231 g_mainThreadSuspendResumeControl->suspend();
232 QScopedValueRollback scoped(m_isSendingNativeEvents,
true);
233 sentEventCount += g_mainThreadSuspendResumeControl->sendPendingEvents();
234 }
while (!m_wakeFromSuspendTimer);
236 return sentEventCount > 1;
239bool QEventDispatcherWasm::sendPostedEvents()
241 QCoreApplication::sendPostedEvents();
249 bool didWakeup = m_wakeup;
254bool QEventDispatcherWasm::sendTimerEvents()
256 int activatedTimers = m_timerInfo->activateTimers();
257 if (activatedTimers > 0)
259 return activatedTimers > 0;
262void QEventDispatcherWasm::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object)
265 if (qToUnderlying(timerId) < 1 || interval < 0ns || !object) {
266 qWarning(
"QEventDispatcherWasm::registerTimer: invalid arguments");
268 }
else if (object->thread() != thread() || thread() != QThread::currentThread()) {
269 qWarning(
"QEventDispatcherWasm::registerTimer: timers cannot be started from another "
274 qCDebug(lcEventDispatcherTimers) <<
"registerTimer" <<
int(timerId) << interval << timerType << object;
276 m_timerInfo->registerTimer(timerId, interval, timerType, object);
280bool QEventDispatcherWasm::unregisterTimer(Qt::TimerId timerId)
283 if (qToUnderlying(timerId) < 1) {
284 qWarning(
"QEventDispatcherWasm::unregisterTimer: invalid argument");
286 }
else if (thread() != QThread::currentThread()) {
287 qWarning(
"QEventDispatcherWasm::unregisterTimer: timers cannot be stopped from another "
293 qCDebug(lcEventDispatcherTimers) <<
"unregisterTimer" <<
int(timerId);
295 bool ans = m_timerInfo->unregisterTimer(timerId);
300bool QEventDispatcherWasm::unregisterTimers(QObject *object)
304 qWarning(
"QEventDispatcherWasm::unregisterTimers: invalid argument");
306 }
else if (object->thread() != thread() || thread() != QThread::currentThread()) {
307 qWarning(
"QEventDispatcherWasm::unregisterTimers: timers cannot be stopped from another "
313 qCDebug(lcEventDispatcherTimers) <<
"registerTimer" << object;
315 bool ans = m_timerInfo->unregisterTimers(object);
320QList<QAbstractEventDispatcher::TimerInfoV2>
321QEventDispatcherWasm::timersForObject(QObject *object)
const
325 qWarning(
"QEventDispatcherWasm:registeredTimers: invalid argument");
330 return m_timerInfo->registeredTimers(object);
333QEventDispatcherWasm::Duration QEventDispatcherWasm::remainingTime(Qt::TimerId timerId)
const
335 return m_timerInfo->remainingDuration(timerId);
338void QEventDispatcherWasm::interrupt()
340 m_interrupted =
true;
344void QEventDispatcherWasm::wakeUp()
348 if (isSecondaryThreadEventDispatcher()) {
349 std::lock_guard<std::mutex> lock(m_mutex);
350 m_wakeUpCalled =
true;
351 m_moreEvents.notify_one();
355 QEventDispatcherWasm *eventDispatcher =
this;
356 qwasmglobal::runOnMainThreadAsync([eventDispatcher]() {
357 if (isValidEventDispatcherPointer(eventDispatcher)) {
358 if (!eventDispatcher->m_wakeupTimer->hasTimeout())
359 eventDispatcher->m_wakeupTimer->setTimeout(0ms);
365void QEventDispatcherWasm::handleNonAsyncifyErrorCases(QEventLoop::ProcessEventsFlags flags)
367 Q_ASSERT(!useAsyncify());
369 if (flags & QEventLoop::ApplicationExec) {
374 const bool simulateInfiniteLoop =
true;
375 emscripten_set_main_loop([](){
376 emscripten_pause_main_loop();
377 }, 0, simulateInfiniteLoop);
378 }
else if (flags & QEventLoop::DialogExec) {
379 qFatal() <<
"Calling exec() is not supported on Qt for WebAssembly in this configuration. Please build"
380 <<
"with asyncify support, or use an asynchronous API like QDialog::open()";
381 }
else if (flags & QEventLoop::WaitForMoreEvents) {
382 qFatal(
"QEventLoop::WaitForMoreEvents is not supported on the main thread without asyncify");
389bool QEventDispatcherWasm::wait(
int timeout)
391 auto tim = timeout > 0 ? std::optional<std::chrono::milliseconds>(timeout) : std::nullopt;
392 if (isSecondaryThreadEventDispatcher())
393 return secondaryThreadWait(tim);
401void QEventDispatcherWasm::processEventsWait()
403 if (isMainThreadEventDispatcher()) {
404 asyncifyWait(std::nullopt);
406 auto nanoWait = m_timerInfo->timerWait();
407 std::optional<std::chrono::milliseconds> milliWait;
408 if (nanoWait.has_value())
409 milliWait = std::chrono::duration_cast<std::chrono::milliseconds>(*nanoWait);
410 secondaryThreadWait(milliWait);
414void QEventDispatcherWasm::asyncifyWait(std::optional<std::chrono::milliseconds> timeout)
416 Q_ASSERT(emscripten_is_main_runtime_thread());
417 Q_ASSERT(isMainThreadEventDispatcher());
418 Q_ASSERT(useAsyncify());
419 if (timeout.has_value())
420 m_suspendTimer->setTimeout(timeout.value());
421 g_mainThreadSuspendResumeControl->suspend();
424bool QEventDispatcherWasm::secondaryThreadWait(std::optional<std::chrono::milliseconds> timeout)
427 Q_ASSERT(QThread::currentThread() == thread());
428 using namespace std::chrono_literals;
429 std::unique_lock<std::mutex> lock(m_mutex);
434 if (m_wakeUpCalled) {
435 m_wakeUpCalled =
false;
439 auto waitTime = timeout.value_or(std::chrono::milliseconds::max());
440 bool wakeUpCalled = m_moreEvents.wait_for(lock, waitTime, [
this] {
return m_wakeUpCalled; });
441 m_wakeUpCalled =
false;
449void QEventDispatcherWasm::onTimer()
451 Q_ASSERT(emscripten_is_main_runtime_thread());
452 if (!g_mainThreadEventDispatcher)
460 g_mainThreadEventDispatcher->sendTimerEvents();
463void QEventDispatcherWasm::onWakeup()
465 Q_ASSERT(emscripten_is_main_runtime_thread());
466 if (!g_mainThreadEventDispatcher)
472 if (g_mainThreadEventDispatcher->m_isSendingNativeEvents)
475 g_mainThreadEventDispatcher->processEvents(QEventLoop::AllEvents);
478void QEventDispatcherWasm::onProcessNativeEventsResume()
480 Q_ASSERT(emscripten_is_main_runtime_thread());
481 if (!g_mainThreadEventDispatcher)
483 g_mainThreadEventDispatcher->m_wakeFromSuspendTimer =
true;
489void QEventDispatcherWasm::updateNativeTimer()
492 Q_ASSERT(QThread::currentThread() == thread());
497 if (!isMainThreadEventDispatcher())
501 const std::optional<std::chrono::nanoseconds> nanoWait = m_timerInfo->timerWait();
502 if (!nanoWait.has_value()) {
503 m_nativeTimer->clearTimeout();
507 auto milliWait = std::chrono::duration_cast<std::chrono::milliseconds>(*nanoWait);
508 const auto newTargetTime = m_timerInfo->currentTime + milliWait;
511 if (m_nativeTimer->hasTimeout() && newTargetTime == m_timerTargetTime)
515 qCDebug(lcEventDispatcherTimers)
516 <<
"Created new native timer timeout" << milliWait.count() <<
"ms"
517 <<
"previous target time" << m_timerTargetTime.time_since_epoch()
518 <<
"new target time" << newTargetTime.time_since_epoch();
519 m_nativeTimer->clearTimeout();
520 m_nativeTimer->setTimeout(milliWait);
521 m_timerTargetTime = newTargetTime;
525 int g_startupTasks = 0;
534void QEventDispatcherWasm::registerStartupTask()
539void QEventDispatcherWasm::completeStarupTask()
542 callOnLoadedIfRequired();
545void QEventDispatcherWasm::callOnLoadedIfRequired()
547 if (g_startupTasks > 0)
550 static bool qtLoadedCalled =
false;
553 qtLoadedCalled =
true;
556void QEventDispatcherWasm::onLoaded()
565void QEventDispatcherWasm::registerSocketNotifier(QSocketNotifier *notifier)
567 QWasmSocket::registerSocketNotifier(notifier);
570void QEventDispatcherWasm::unregisterSocketNotifier(QSocketNotifier *notifier)
572 QWasmSocket::unregisterSocketNotifier(notifier);
575void QEventDispatcherWasm::socketSelect(
int timeout,
int socket,
bool waitForRead,
bool waitForWrite,
576 bool *selectForRead,
bool *selectForWrite,
bool *socketDisconnect)
578 QEventDispatcherWasm *eventDispatcher =
static_cast<QEventDispatcherWasm *>(
579 QAbstractEventDispatcher::instance(QThread::currentThread()));
581 if (!eventDispatcher) {
582 qWarning(
"QEventDispatcherWasm::socketSelect called without eventdispatcher instance");
586 QWasmSocket::waitForSocketState(eventDispatcher, timeout, socket, waitForRead, waitForWrite,
587 selectForRead, selectForWrite, socketDisconnect);
592#include "moc_qeventdispatcher_wasm_p.cpp"
static bool useAsyncify()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")