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_literals;
27#define LOCK_GUARD(M) std::lock_guard<std::mutex> lock(M)
50EM_ASYNC_JS(
void, qt_jspi_suspend_js, (), {
51 ++
Module.qtJspiSuspensionCounter;
53 await new Promise(resolve => {
54 Module.qtAsyncifyWakeUp.push(resolve);
58EM_JS(
bool, qt_jspi_resume_js, (), {
59 if (!
Module.qtJspiSuspensionCounter)
62 --
Module.qtJspiSuspensionCounter;
65 const wakeUp = (
Module.qtAsyncifyWakeUp ?? []).pop();
71EM_JS(
bool, qt_jspi_can_resume_js, (), {
72 return Module.qtJspiSuspensionCounter > 0;
75EM_JS(
void, init_jspi_support_js, (), {
76 Module.qtAsyncifyWakeUp = [];
77 Module.qtJspiSuspensionCounter = 0;
80void initJspiSupport() {
81 init_jspi_support_js();
87 if (
Module.qtSuspendId === undefined)
89 let sleepFn = (wakeUp) => {
90 Module.qtAsyncifyWakeUp = wakeUp;
93 return Asyncify.handleSleep(sleepFn);
97 let wakeUp =
Module.qtAsyncifyWakeUp;
98 if (wakeUp == undefined)
100 Module.qtAsyncifyWakeUp = undefined;
101 const suspendId =
Module.qtSuspendId;
107 if (
Module.qtSuspendId !== suspendId)
161Q_CONSTINIT std::mutex QEventDispatcherWasm::g_staticDataMutex;
162emscripten::ProxyingQueue QEventDispatcherWasm::g_proxyingQueue;
163pthread_t QEventDispatcherWasm::g_mainThread;
166std::multimap<int, QSocketNotifier *> QEventDispatcherWasm::g_socketNotifiers;
167std::map<int, QEventDispatcherWasm::SocketReadyState> QEventDispatcherWasm::g_socketState;
183 qCDebug(lcEventDispatcher) <<
"Creating QEventDispatcherWasm instance" <<
this
184 <<
"is main thread" << emscripten_is_main_runtime_thread();
186 if (emscripten_is_main_runtime_thread()) {
190 Q_ASSERT(g_mainThreadEventDispatcher ==
nullptr);
191 g_mainThreadEventDispatcher =
this;
193 g_mainThread = pthread_self();
197 std::lock_guard<std::mutex>
lock(g_staticDataMutex);
198 g_secondaryThreadEventDispatchers.append(
this);
205 qCDebug(lcEventDispatcher) <<
"Destroying QEventDispatcherWasm instance" <<
this;
210 if (isSecondaryThreadEventDispatcher()) {
211 std::lock_guard<std::mutex>
lock(g_staticDataMutex);
212 g_secondaryThreadEventDispatchers.remove(g_secondaryThreadEventDispatchers.indexOf(
this));
217 emscripten_clear_timeout(m_timerId);
218 if (!g_socketNotifiers.empty()) {
219 qWarning(
"QEventDispatcherWasm: main thread event dispatcher deleted with active socket notifiers");
220 clearEmscriptenSocketCallbacks();
221 g_socketNotifiers.clear();
223 g_mainThreadEventDispatcher =
nullptr;
224 if (!g_socketNotifiers.empty()) {
225 qWarning(
"QEventDispatcherWasm: main thread event dispatcher deleted with active socket notifiers");
226 clearEmscriptenSocketCallbacks();
227 g_socketNotifiers.clear();
230 g_socketState.clear();
234bool QEventDispatcherWasm::isMainThreadEventDispatcher()
236 return this == g_mainThreadEventDispatcher;
239bool QEventDispatcherWasm::isSecondaryThreadEventDispatcher()
241 return this != g_mainThreadEventDispatcher;
246 if (eventDispatcher == g_mainThreadEventDispatcher)
249 Q_ASSERT(!g_staticDataMutex.try_lock());
250 if (g_secondaryThreadEventDispatchers.contains(eventDispatcher))
258 qCDebug(lcEventDispatcher) <<
"QEventDispatcherWasm::processEvents flags" <<
flags;
262 if (isMainThreadEventDispatcher()) {
266 handleApplicationExec();
272 m_interrupted =
false;
279 if (m_processTimers) {
280 m_processTimers =
false;
291 bool wasEmpty = g_socketNotifiers.empty();
294 runOnMainThread([]{ setEmscriptenSocketCallbacks(); });
301 auto notifiers = g_socketNotifiers.equal_range(
notifier->socket());
302 for (
auto it = notifiers.first;
it != notifiers.second; ++
it) {
304 g_socketNotifiers.erase(
it);
309 if (g_socketNotifiers.empty())
310 runOnMainThread([]{ clearEmscriptenSocketCallbacks(); });
316 if (timerId < 1 || interval < 0 || !
object) {
317 qWarning(
"QEventDispatcherWasm::registerTimer: invalid arguments");
320 qWarning(
"QEventDispatcherWasm::registerTimer: timers cannot be started from another "
325 qCDebug(lcEventDispatcherTimers) <<
"registerTimer" << timerId << interval << timerType <<
object;
327 m_timerInfo->
registerTimer(timerId, interval, timerType,
object);
335 qWarning(
"QEventDispatcherWasm::unregisterTimer: invalid argument");
338 qWarning(
"QEventDispatcherWasm::unregisterTimer: timers cannot be stopped from another "
344 qCDebug(lcEventDispatcherTimers) <<
"unregisterTimer" << timerId;
355 qWarning(
"QEventDispatcherWasm::unregisterTimers: invalid argument");
358 qWarning(
"QEventDispatcherWasm::unregisterTimers: timers cannot be stopped from another "
364 qCDebug(lcEventDispatcherTimers) <<
"registerTimer" <<
object;
376 qWarning(
"QEventDispatcherWasm:registeredTimers: invalid argument");
391 m_interrupted =
true;
401 bool wasBlocked = wakeEventDispatcherThread();
402 if (!
wasBlocked && isMainThreadEventDispatcher()) {
405 if (m_pendingProcessEvents)
407 m_pendingProcessEvents =
true;
409 runOnMainThreadAsync([
this](){
410 QEventDispatcherWasm::callProcessPostedEvents(
this);
415void QEventDispatcherWasm::handleApplicationExec()
428 const bool simulateInfiniteLoop =
true;
429 emscripten_set_main_loop([](){
430 emscripten_pause_main_loop();
431 }, 0, simulateInfiniteLoop);
435void QEventDispatcherWasm::handleDialogExec()
438 qWarning() <<
"Warning: exec() is not supported on Qt for WebAssembly in this configuration. Please build"
439 <<
"with asyncify support, or use an asynchronous API like QDialog::open()";
449bool QEventDispatcherWasm::wait(
int timeout)
452 using namespace std::chrono_literals;
455 if (isSecondaryThreadEventDispatcher()) {
456 std::unique_lock<std::mutex>
lock(m_mutex);
458 m_wakeUpCalled =
false;
459 auto wait_time =
timeout > 0 ?
timeout * 1ms : std::chrono::duration<int, std::micro>::max();
460 bool wakeUpCalled = m_moreEvents.wait_for(
lock, wait_time, [=] {
return m_wakeUpCalled; });
464 Q_ASSERT(emscripten_is_main_runtime_thread());
465 Q_ASSERT(isMainThreadEventDispatcher());
468 qWarning() <<
"QEventDispatcherWasm asyncify wait with timeout is not supported; timeout will be ignored";
471 qt_jspi_suspend_js();
475 qWarning(
"QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
481 qWarning(
"QEventLoop::WaitForMoreEvents is not supported on the main thread without asyncify");
490bool QEventDispatcherWasm::wakeEventDispatcherThread()
493 if (isSecondaryThreadEventDispatcher()) {
494 std::lock_guard<std::mutex>
lock(m_mutex);
495 m_wakeUpCalled =
true;
496 m_moreEvents.notify_one();
500 Q_ASSERT(isMainThreadEventDispatcher());
502 if (!qt_jspi_can_resume_js())
504 runOnMainThread([]{ qt_jspi_resume_js(); });
516void QEventDispatcherWasm::callProcessPostedEvents(
void *
context)
518 Q_ASSERT(emscripten_is_main_runtime_thread());
521 if (!g_mainThreadEventDispatcher)
527 if (
context != g_mainThreadEventDispatcher)
531 LOCK_GUARD(g_mainThreadEventDispatcher->m_mutex);
532 g_mainThreadEventDispatcher->m_pendingProcessEvents =
false;
544void QEventDispatcherWasm::processTimers()
552void QEventDispatcherWasm::updateNativeTimer()
565 using namespace std::chrono;
566 auto timespecToMsec = [](timespec ts) -> milliseconds {
567 return duration_cast<milliseconds>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec});
570 bool hasTimer = m_timerInfo->
timerWait(toWait);
571 const milliseconds toWaitDuration = timespecToMsec(toWait);
572 const time_point newTargetTimePoint = m_timerInfo->
currentTime + toWaitDuration;
573 auto newTargetTime = duration_cast<milliseconds>(newTargetTimePoint.time_since_epoch());
574 auto maintainNativeTimer = [
this, hasTimer, toWaitDuration, newTargetTime]() {
575 Q_ASSERT(emscripten_is_main_runtime_thread());
579 emscripten_clear_timeout(m_timerId);
581 m_timerTargetTime = 0ms;
586 if (m_timerTargetTime != 0ms && newTargetTime >= m_timerTargetTime)
589 qCDebug(lcEventDispatcherTimers)
590 <<
"Created new native timer with wait" << toWaitDuration.count() <<
"ms"
591 <<
"timeout" << newTargetTime.count() <<
"ms";
592 emscripten_clear_timeout(m_timerId);
593 m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers,
594 toWaitDuration.count(),
this);
595 m_timerTargetTime = newTargetTime;
600 runOnMainThread([
this, maintainNativeTimer]() {
601 Q_ASSERT(emscripten_is_main_runtime_thread());
608 if (isValidEventDispatcherPointer(
this))
609 maintainNativeTimer();
616void QEventDispatcherWasm::callProcessTimers(
void *
context)
618 Q_ASSERT(emscripten_is_main_runtime_thread());
625 g_mainThreadEventDispatcher->m_timerTargetTime = 0ms;
626 g_mainThreadEventDispatcher->processTimers();
632 std::lock_guard<std::mutex>
lock(g_staticDataMutex);
633 if (g_secondaryThreadEventDispatchers.contains(
context)) {
635 eventDispatcher->m_timerTargetTime = 0ms;
636 eventDispatcher->m_processTimers =
true;
637 eventDispatcher->
wakeUp();
642void QEventDispatcherWasm::setEmscriptenSocketCallbacks()
644 qCDebug(lcEventDispatcher) <<
"setEmscriptenSocketCallbacks";
646 emscripten_set_socket_error_callback(
nullptr, QEventDispatcherWasm::socketError);
647 emscripten_set_socket_open_callback(
nullptr, QEventDispatcherWasm::socketOpen);
648 emscripten_set_socket_listen_callback(
nullptr, QEventDispatcherWasm::socketListen);
649 emscripten_set_socket_connection_callback(
nullptr, QEventDispatcherWasm::socketConnection);
650 emscripten_set_socket_message_callback(
nullptr, QEventDispatcherWasm::socketMessage);
651 emscripten_set_socket_close_callback(
nullptr, QEventDispatcherWasm::socketClose);
654void QEventDispatcherWasm::clearEmscriptenSocketCallbacks()
656 qCDebug(lcEventDispatcher) <<
"clearEmscriptenSocketCallbacks";
658 emscripten_set_socket_error_callback(
nullptr,
nullptr);
659 emscripten_set_socket_open_callback(
nullptr,
nullptr);
660 emscripten_set_socket_listen_callback(
nullptr,
nullptr);
661 emscripten_set_socket_connection_callback(
nullptr,
nullptr);
662 emscripten_set_socket_message_callback(
nullptr,
nullptr);
663 emscripten_set_socket_close_callback(
nullptr,
nullptr);
666void QEventDispatcherWasm::socketError(
int socket,
int err,
const char* msg,
void *
context)
681 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
682 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
683 for (
auto [_,
notifier]: notifiers) {
686 setSocketState(
socket,
true,
true);
690void QEventDispatcherWasm::socketOpen(
int socket,
void *
context)
695 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
696 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
697 for (
auto [_,
notifier]: notifiers) {
702 setSocketState(
socket,
false,
true);
706void QEventDispatcherWasm::socketListen(
int socket,
void *
context)
712void QEventDispatcherWasm::socketConnection(
int socket,
void *
context)
718void QEventDispatcherWasm::socketMessage(
int socket,
void *
context)
723 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
724 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
725 for (
auto [_,
notifier]: notifiers) {
730 setSocketState(
socket,
true,
false);
734void QEventDispatcherWasm::socketClose(
int socket,
void *
context)
744 auto notifiersRange = g_socketNotifiers.equal_range(
socket);
745 std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
749 setSocketState(
socket,
true,
true);
754void QEventDispatcherWasm::setSocketState(
int socket,
bool setReadyRead,
bool setReadyWrite)
761 state.readyRead |= setReadyRead;
762 state.readyWrite |= setReadyWrite;
767 if ((
state.readyRead &&
state.waitForReadyRead) || (
state.readyWrite &&
state.waitForReadyWrite))
768 waiter->wakeEventDispatcherThread();
771void QEventDispatcherWasm::clearSocketState(
int socket)
774 g_socketState.erase(
socket);
777void QEventDispatcherWasm::waitForSocketState(
int timeout,
int socket,
bool checkRead,
bool checkWrite,
778 bool *selectForRead,
bool *selectForWrite,
bool *socketDisconnect)
783 *selectForRead =
false;
784 *selectForWrite =
false;
793 qWarning() <<
"QEventDispatcherWasm::waitForSocketState: a thread is already waiting";
797 bool shouldWait =
true;
798 if (checkRead &&
state.readyRead) {
800 state.readyRead =
false;
801 *selectForRead =
true;
803 if (checkWrite &&
state.readyWrite) {
805 state.readyWrite =
false;
806 *selectForRead =
true;
812 state.waitForReadyRead = checkRead;
813 state.waitForReadyWrite = checkWrite;
816 bool didTimeOut = !wait(
timeout);
821 auto it = g_socketState.find(
socket);
822 if (
it == g_socketState.end()) {
823 *socketDisconnect =
true;
826 it->second.waiter =
nullptr;
827 it->second.waitForReadyRead =
false;
828 it->second.waitForReadyWrite =
false;
837 bool *selectForRead,
bool *selectForWrite,
bool *socketDisconnect)
842 if (!eventDispatcher) {
843 qWarning(
"QEventDispatcherWasm::socketSelect called without eventdispatcher instance");
848 selectForRead, selectForWrite, socketDisconnect);
852 void trampoline(
void *
context) {
854 auto async_fn = [](
void *
context){
855 std::function<
void(
void)> *fn =
reinterpret_cast<std::function<
void(
void)
> *>(
context);
860 emscripten_async_call(async_fn,
context, 0);
865void QEventDispatcherWasm::run(std::function<
void(
void)> fn)
871void QEventDispatcherWasm::runAsync(std::function<
void(
void)> fn)
873 trampoline(
new std::function<
void(
void)>(fn));
878void QEventDispatcherWasm::runOnMainThread(std::function<
void(
void)> fn)
881 if (!emscripten_is_main_runtime_thread()) {
882 void *
context =
new std::function<void(void)>(fn);
883 g_proxyingQueue.proxyAsync(g_mainThread, [
context]{
894void QEventDispatcherWasm::runOnMainThreadAsync(std::function<
void(
void)> fn)
896 void *
context =
new std::function<void(void)>(fn);
898 if (!emscripten_is_main_runtime_thread()) {
899 g_proxyingQueue.proxyAsync(g_mainThread, [
context]{
910#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.
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
void registerSocketNotifier(QSocketNotifier *notifier) override
Registers notifier with the event loop.
QList< QAbstractEventDispatcher::TimerInfo > registeredTimers(QObject *object) const override
Returns a list of registered timers for object.
void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) override
Register a timer with the specified timerId, interval, and timerType for the given object.
int remainingTime(int timerId) override
Returns the remaining time in milliseconds with the given timerId.
bool processEvents(QEventLoop::ProcessEventsFlags flags) override
Processes pending events that match flags until there are no more events to process.
bool unregisterTimers(QObject *object) override
Unregisters all the timers associated with the given object.
bool unregisterTimer(int timerId) override
Unregisters the timer with the given timerId.
void unregisterSocketNotifier(QSocketNotifier *notifier) override
Unregisters notifier from the event dispatcher.
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()
std::chrono::steady_clock::time_point currentTime
bool timerWait(timespec &)
qint64 timerRemainingTime(int timerId)
bool unregisterTimers(QObject *object)
void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
QList< QAbstractEventDispatcher::TimerInfo > registeredTimers(QObject *object) const
bool unregisterTimer(int timerId)
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(initializeStandardUserDefaults)
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
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLbitfield GLuint64 timeout
[4]
const bool wasBlocked
[52]
socketLayer waitForWrite()