6#include <QtCore/private/qabstracteventdispatcher_p.h>
7#include <QtCore/qcoreapplication.h>
8#include <QtCore/qthread.h>
9#include <QtCore/qsocketnotifier.h>
10#include <QtCore/private/qstdweb_p.h>
12#include "emscripten.h"
13#include <emscripten/html5.h>
14#include <emscripten/threading.h>
15#include <emscripten/val.h>
17using namespace std::chrono;
18using namespace std::chrono_literals;
28#define LOCK_GUARD(M) std::lock_guard<std::mutex> lock(M)
53 ++
Module.qtJspiSuspensionCounter;
61 if (!
Module.qtJspiSuspensionCounter)
64 --
Module.qtJspiSuspensionCounter;
67 const wakeUp = (
Module.qtAsyncifyWakeUp ?? []).pop();
74 return Module.qtJspiSuspensionCounter > 0;
77EM_JS(
void, init_jspi_support_js, (), {
78 Module.qtAsyncifyWakeUp = [];
79 Module.qtJspiSuspensionCounter = 0;
83void initJspiSupport() {
84 init_jspi_support_js();
91 if (
Module.qtSuspendId === undefined)
93 let sleepFn = (wakeUp) => {
94 Module.qtAsyncifyWakeUp = wakeUp;
97 return Asyncify.handleSleep(sleepFn);
101 let wakeUp =
Module.qtAsyncifyWakeUp;
102 if (wakeUp == undefined)
104 Module.qtAsyncifyWakeUp = undefined;
105 const suspendId =
Module.qtSuspendId;
111 if (
Module.qtSuspendId !== suspendId)
187Q_CONSTINIT QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
188Q_CONSTINIT std::mutex QEventDispatcherWasm::g_staticDataMutex;
189emscripten::ProxyingQueue QEventDispatcherWasm::g_proxyingQueue;
190pthread_t QEventDispatcherWasm::g_mainThread;
193std::multimap<int, QSocketNotifier *> QEventDispatcherWasm::g_socketNotifiers;
194std::map<int, QEventDispatcherWasm::SocketReadyState> QEventDispatcherWasm::g_socketState;
209 qCDebug(lcEventDispatcher) <<
"Creating QEventDispatcherWasm instance" <<
this
210 <<
"is main thread" << emscripten_is_main_runtime_thread();
212 if (emscripten_is_main_runtime_thread()) {
216 Q_ASSERT(g_mainThreadEventDispatcher ==
nullptr);
217 g_mainThreadEventDispatcher =
this;
219 g_mainThread = pthread_self();
229 std::lock_guard<std::mutex>
lock(g_staticDataMutex);
230 g_secondaryThreadEventDispatchers.append(
this);
237 qCDebug(lcEventDispatcher) <<
"Destroying QEventDispatcherWasm instance" <<
this;
242 if (isSecondaryThreadEventDispatcher()) {
243 std::lock_guard<std::mutex>
lock(g_staticDataMutex);
244 g_secondaryThreadEventDispatchers.remove(g_secondaryThreadEventDispatchers.indexOf(
this));
249 emscripten_clear_timeout(m_timerId);
250 if (!g_socketNotifiers.empty()) {
251 qWarning(
"QEventDispatcherWasm: main thread event dispatcher deleted with active socket notifiers");
252 clearEmscriptenSocketCallbacks();
253 g_socketNotifiers.clear();
255 g_mainThreadEventDispatcher =
nullptr;
256 if (!g_socketNotifiers.empty()) {
257 qWarning(
"QEventDispatcherWasm: main thread event dispatcher deleted with active socket notifiers");
258 clearEmscriptenSocketCallbacks();
259 g_socketNotifiers.clear();
262 g_socketState.clear();
266bool QEventDispatcherWasm::isMainThreadEventDispatcher()
268 return this == g_mainThreadEventDispatcher;
271bool QEventDispatcherWasm::isSecondaryThreadEventDispatcher()
273 return this != g_mainThreadEventDispatcher;
278 if (eventDispatcher == g_mainThreadEventDispatcher)
281 if (g_secondaryThreadEventDispatchers.contains(eventDispatcher))
289 qCDebug(lcEventDispatcher) <<
"QEventDispatcherWasm::processEvents flags" <<
flags;
293 if (isMainThreadEventDispatcher()) {
297 handleApplicationExec();
304 std::unique_lock<std::mutex>
lock(m_mutex);
305 m_wakeUpCalled =
false;
313 if (!isValidEventDispatcherPointer(
this))
317 m_interrupted =
false;
324 if (m_processTimers) {
325 m_processTimers =
false;
336 bool wasEmpty = g_socketNotifiers.empty();
346 auto notifiers = g_socketNotifiers.equal_range(
notifier->socket());
347 for (
auto it = notifiers.first;
it != notifiers.second; ++
it) {
349 g_socketNotifiers.erase(
it);
354 if (g_socketNotifiers.empty())
362 qWarning(
"QEventDispatcherWasm::registerTimer: invalid arguments");
365 qWarning(
"QEventDispatcherWasm::registerTimer: timers cannot be started from another "
370 qCDebug(lcEventDispatcherTimers) <<
"registerTimer" << int(timerId) << interval << timerType <<
object;
372 m_timerInfo->
registerTimer(timerId, interval, timerType,
object);
380 qWarning(
"QEventDispatcherWasm::unregisterTimer: invalid argument");
383 qWarning(
"QEventDispatcherWasm::unregisterTimer: timers cannot be stopped from another "
389 qCDebug(lcEventDispatcherTimers) <<
"unregisterTimer" << int(timerId);
400 qWarning(
"QEventDispatcherWasm::unregisterTimers: invalid argument");
403 qWarning(
"QEventDispatcherWasm::unregisterTimers: timers cannot be stopped from another "
409 qCDebug(lcEventDispatcherTimers) <<
"registerTimer" <<
object;
416QList<QAbstractEventDispatcher::TimerInfoV2>
421 qWarning(
"QEventDispatcherWasm:registeredTimers: invalid argument");
436 m_interrupted =
true;
446 bool wasBlocked = wakeEventDispatcherThread();
452 if (m_pendingProcessEvents)
454 m_pendingProcessEvents =
true;
456 runOnMainThreadAsync([
this](){
457 QEventDispatcherWasm::callProcessPostedEvents(
this);
462void QEventDispatcherWasm::handleApplicationExec()
475 const bool simulateInfiniteLoop =
true;
476 emscripten_set_main_loop([](){
477 emscripten_pause_main_loop();
478 }, 0, simulateInfiniteLoop);
482void QEventDispatcherWasm::handleDialogExec()
485 qWarning() <<
"Warning: exec() is not supported on Qt for WebAssembly in this configuration. Please build"
486 <<
"with asyncify support, or use an asynchronous API like QDialog::open()";
496bool QEventDispatcherWasm::wait(
int timeout)
499 using namespace std::chrono_literals;
502 if (isSecondaryThreadEventDispatcher()) {
503 std::unique_lock<std::mutex>
lock(m_mutex);
511 auto wait_time =
timeout > 0 ?
timeout * 1ms : std::chrono::duration<int, std::micro>::max();
512 bool wakeUpCalled = m_moreEvents.wait_for(
lock, wait_time, [=] {
return m_wakeUpCalled; });
516 Q_ASSERT(emscripten_is_main_runtime_thread());
517 Q_ASSERT(isMainThreadEventDispatcher());
520 qWarning() <<
"QEventDispatcherWasm asyncify wait with timeout is not supported; timeout will be ignored";
527 qWarning(
"QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
533 qWarning(
"QEventLoop::WaitForMoreEvents is not supported on the main thread without asyncify");
542bool QEventDispatcherWasm::wakeEventDispatcherThread()
545 if (isSecondaryThreadEventDispatcher()) {
546 std::lock_guard<std::mutex>
lock(m_mutex);
547 m_wakeUpCalled =
true;
548 m_moreEvents.notify_one();
552 Q_ASSERT(isMainThreadEventDispatcher());
556 return qstdweb::runTaskOnMainThread<bool>(
559 return qstdweb::runTaskOnMainThread<bool>(
573void QEventDispatcherWasm::callProcessPostedEvents(
void *
context)
575 Q_ASSERT(emscripten_is_main_runtime_thread());
578 if (!g_mainThreadEventDispatcher)
584 if (
context != g_mainThreadEventDispatcher)
588 LOCK_GUARD(g_mainThreadEventDispatcher->m_mutex);
589 g_mainThreadEventDispatcher->m_pendingProcessEvents =
false;
592 g_mainThreadEventDispatcher->processPostedEvents();
601void QEventDispatcherWasm::processTimers()
609void QEventDispatcherWasm::updateNativeTimer()
622 const std::optional<std::chrono::nanoseconds> wait = m_timerInfo->
timerWait();
623 const auto toWaitDuration = duration_cast<milliseconds>(wait.value_or(0ms));
624 const auto newTargetTimePoint = m_timerInfo->
currentTime + toWaitDuration;
625 auto epochNsecs = newTargetTimePoint.time_since_epoch();
626 auto newTargetTime = std::chrono::duration_cast<std::chrono::milliseconds>(epochNsecs);
627 auto maintainNativeTimer = [
this, wait, toWaitDuration, newTargetTime]() {
628 Q_ASSERT(emscripten_is_main_runtime_thread());
632 emscripten_clear_timeout(m_timerId);
634 m_timerTargetTime = 0ms;
639 if (m_timerTargetTime != 0ms && newTargetTime >= m_timerTargetTime)
642 qCDebug(lcEventDispatcherTimers)
643 <<
"Created new native timer with wait" << toWaitDuration.count() <<
"ms"
644 <<
"timeout" << newTargetTime.count() <<
"ms";
645 emscripten_clear_timeout(m_timerId);
646 m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers,
647 toWaitDuration.count(),
this);
648 m_timerTargetTime = newTargetTime;
654 Q_ASSERT(emscripten_is_main_runtime_thread());
661 if (isValidEventDispatcherPointer(
this))
662 maintainNativeTimer();
669void QEventDispatcherWasm::callProcessTimers(
void *
context)
671 Q_ASSERT(emscripten_is_main_runtime_thread());
678 g_mainThreadEventDispatcher->m_timerTargetTime = 0ms;
679 g_mainThreadEventDispatcher->processTimers();
685 std::lock_guard<std::mutex>
lock(g_staticDataMutex);
686 if (g_secondaryThreadEventDispatchers.contains(
context)) {
688 eventDispatcher->m_timerTargetTime = 0ms;
689 eventDispatcher->m_processTimers =
true;
690 eventDispatcher->
wakeUp();
695void QEventDispatcherWasm::setEmscriptenSocketCallbacks()
697 qCDebug(lcEventDispatcher) <<
"setEmscriptenSocketCallbacks";
699 emscripten_set_socket_error_callback(
nullptr, QEventDispatcherWasm::socketError);
700 emscripten_set_socket_open_callback(
nullptr, QEventDispatcherWasm::socketOpen);
701 emscripten_set_socket_listen_callback(
nullptr, QEventDispatcherWasm::socketListen);
702 emscripten_set_socket_connection_callback(
nullptr, QEventDispatcherWasm::socketConnection);
703 emscripten_set_socket_message_callback(
nullptr, QEventDispatcherWasm::socketMessage);
704 emscripten_set_socket_close_callback(
nullptr, QEventDispatcherWasm::socketClose);
707void QEventDispatcherWasm::clearEmscriptenSocketCallbacks()
709 qCDebug(lcEventDispatcher) <<
"clearEmscriptenSocketCallbacks";
711 emscripten_set_socket_error_callback(
nullptr,
nullptr);
712 emscripten_set_socket_open_callback(
nullptr,
nullptr);
713 emscripten_set_socket_listen_callback(
nullptr,
nullptr);
714 emscripten_set_socket_connection_callback(
nullptr,
nullptr);
715 emscripten_set_socket_message_callback(
nullptr,
nullptr);
716 emscripten_set_socket_close_callback(
nullptr,
nullptr);
719void QEventDispatcherWasm::socketError(
int socket,
int err,
const char* msg,
void *
context)
734 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
735 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
736 for (
auto [_,
notifier]: notifiers) {
739 setSocketState(
socket,
true,
true);
743void QEventDispatcherWasm::socketOpen(
int socket,
void *
context)
748 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
749 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
750 for (
auto [_,
notifier]: notifiers) {
755 setSocketState(
socket,
false,
true);
759void QEventDispatcherWasm::socketListen(
int socket,
void *
context)
765void QEventDispatcherWasm::socketConnection(
int socket,
void *
context)
771void QEventDispatcherWasm::socketMessage(
int socket,
void *
context)
776 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
777 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
778 for (
auto [_,
notifier]: notifiers) {
783 setSocketState(
socket,
true,
false);
787void QEventDispatcherWasm::socketClose(
int socket,
void *
context)
797 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
798 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
802 setSocketState(
socket,
true,
true);
807void QEventDispatcherWasm::setSocketState(
int socket,
bool setReadyRead,
bool setReadyWrite)
814 state.readyRead |= setReadyRead;
815 state.readyWrite |= setReadyWrite;
820 if ((
state.readyRead &&
state.waitForReadyRead) || (
state.readyWrite &&
state.waitForReadyWrite))
821 waiter->wakeEventDispatcherThread();
824void QEventDispatcherWasm::clearSocketState(
int socket)
827 g_socketState.erase(
socket);
830void QEventDispatcherWasm::waitForSocketState(
int timeout,
int socket,
bool checkRead,
bool checkWrite,
831 bool *selectForRead,
bool *selectForWrite,
bool *socketDisconnect)
836 *selectForRead =
false;
837 *selectForWrite =
false;
846 qWarning() <<
"QEventDispatcherWasm::waitForSocketState: a thread is already waiting";
850 bool shouldWait =
true;
851 if (checkRead &&
state.readyRead) {
853 state.readyRead =
false;
854 *selectForRead =
true;
856 if (checkWrite &&
state.readyWrite) {
858 state.readyWrite =
false;
859 *selectForRead =
true;
865 state.waitForReadyRead = checkRead;
866 state.waitForReadyWrite = checkWrite;
869 bool didTimeOut = !wait(
timeout);
874 auto it = g_socketState.find(
socket);
875 if (
it == g_socketState.end()) {
876 *socketDisconnect =
true;
879 it->second.waiter =
nullptr;
880 it->second.waitForReadyRead =
false;
881 it->second.waitForReadyWrite =
false;
890 bool *selectForRead,
bool *selectForWrite,
bool *socketDisconnect)
895 if (!eventDispatcher) {
896 qWarning(
"QEventDispatcherWasm::socketSelect called without eventdispatcher instance");
901 selectForRead, selectForWrite, socketDisconnect);
905 int g_startupTasks = 0;
927 if (g_startupTasks > 0)
930 static bool qtLoadedCalled =
false;
933 qtLoadedCalled =
true;
935 Q_ASSERT(g_mainThreadEventDispatcher);
936 g_mainThreadEventDispatcher->onLoaded();
949 void trampoline(
void *
context) {
951 auto async_fn = [](
void *
context){
952 std::function<
void(
void)> *fn =
reinterpret_cast<std::function<
void(
void)
> *>(
context);
957 emscripten_async_call(async_fn,
context, 0);
962void QEventDispatcherWasm::run(std::function<
void(
void)> fn)
970 qstdweb::runTaskOnMainThread<void>(fn, &g_proxyingQueue);
972 qstdweb::runTaskOnMainThread<void>(fn);
977void QEventDispatcherWasm::runAsync(std::function<
void(
void)> fn)
979 trampoline(
new std::function<
void(
void)>(fn));
984void QEventDispatcherWasm::runOnMainThreadAsync(std::function<
void(
void)> fn)
986 void *
context =
new std::function<void(void)>(fn);
988 if (!emscripten_is_main_runtime_thread()) {
989 g_proxyingQueue.proxyAsync(g_mainThread, [
context]{
1000#include "moc_qeventdispatcher_wasm_p.cpp"
DarwinBluetooth::LECBManagerNotifier * notifier
static QAbstractEventDispatcher * instance(QThread *thread=nullptr)
Returns a pointer to the event dispatcher object for the specified thread.
std::chrono::nanoseconds Duration
A {std::chrono::duration} type that is used in various API in this class.
void awake()
This signal is emitted after the event loop returns from a function that could block.
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
static void sendPostedEvents(QObject *receiver=nullptr, int event_type=0)
Immediately dispatches all events which have been previously queued with QCoreApplication::postEvent(...
virtual bool processPostedEvents()
void interrupt() override
Interrupts event dispatching.
void wakeUp() override
\threadsafe
bool unregisterTimer(Qt::TimerId timerId) override final
void registerSocketNotifier(QSocketNotifier *notifier) override
Registers notifier with the event loop.
static void callOnLoadedIfRequired()
void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object) override final
Duration remainingTime(Qt::TimerId timerId) const override final
Returns the remaining time of the timer with the given timerId.
static void completeStarupTask()
bool processEvents(QEventLoop::ProcessEventsFlags flags) override
Processes pending events that match flags until there are no more events to process.
QList< TimerInfoV2 > timersForObject(QObject *object) const override final
bool unregisterTimers(QObject *object) override final
Unregisters all the timers associated with the given object.
static void runOnMainThread(std::function< void(void)> fn)
void unregisterSocketNotifier(QSocketNotifier *notifier) override
Unregisters notifier from the event dispatcher.
static void registerStartupTask()
static void socketSelect(int timeout, int socket, bool waitForRead, bool waitForWrite, bool *selectForRead, bool *selectForWrite, bool *socketDisconnect)
QThread * thread() const
Returns the thread in which the object lives.
static QThread * currentThread()
bool unregisterTimer(Qt::TimerId timerId)
std::chrono::steady_clock::time_point currentTime
Duration remainingDuration(Qt::TimerId timerId) const
bool unregisterTimers(QObject *object)
std::optional< Duration > timerWait()
void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object)
QList< TimerInfo > registeredTimers(QObject *object) const
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE bool await(IAsyncOperation< T > &&asyncInfo, T &result, uint timeout=0)
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static bool useAsyncify()
bool qt_asyncify_suspend()
void qt_asyncify_resume_js()
void qt_asyncify_resume()
void qt_asyncify_suspend_js()
static bool g_is_asyncify_suspended
bool qt_jspi_can_resume_js()
void qt_jspi_suspend_js()
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLbitfield GLuint64 timeout
[4]
static const QQmlJSScope * resolve(const QQmlJSScope *current, const QStringList &names)
QT_BEGIN_NAMESPACE constexpr std::underlying_type_t< Enum > qToUnderlying(Enum e) noexcept
const bool wasBlocked
[52]
socketLayer waitForWrite()