diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 89b20918d7..7d4fadab8e 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -355,10 +355,9 @@ void JSConsole::executeCommand(const QString& command) { ScriptValue result; auto scriptManager = weakScriptManager.lock(); if (scriptManager) { - BLOCKING_INVOKE_METHOD(scriptManager.get(), "evaluate", - Q_RETURN_ARG(ScriptValue, result), - Q_ARG(const QString&, command), - Q_ARG(const QString&, consoleFileName)); + BLOCKING_INVOKE_METHOD(scriptManager.get(), [&scriptManager, &consoleFileName, &command, &result]() -> void { + result = scriptManager->evaluate(command, consoleFileName); + }); } return result; }); diff --git a/libraries/shared/src/shared/QtHelpers.cpp b/libraries/shared/src/shared/QtHelpers.cpp index ed387a9763..477176ecb9 100644 --- a/libraries/shared/src/shared/QtHelpers.cpp +++ b/libraries/shared/src/shared/QtHelpers.cpp @@ -40,6 +40,16 @@ void addBlockingForbiddenThread(const QString& name, QThread* thread) { threadHash[thread] = name; } +QString isBlockingForbiddenThread(QThread* currentThread) { + QReadLocker locker(&threadHashLock); + for (const auto& thread : threadHash.keys()) { + if (currentThread == thread) { + return threadHash[thread]; + } + } + return QString(); +} + bool blockingInvokeMethod( const char* function, QObject *obj, const char *member, diff --git a/libraries/shared/src/shared/QtHelpers.h b/libraries/shared/src/shared/QtHelpers.h index 9a9d33a3ce..b87caf0185 100644 --- a/libraries/shared/src/shared/QtHelpers.h +++ b/libraries/shared/src/shared/QtHelpers.h @@ -11,14 +11,21 @@ #define hifi_Shared_QtHelpers_h #include +#include + +#include "../Profile.h" #if defined(Q_OS_WIN) // Enable event queue debugging #define DEBUG_EVENT_QUEUE #endif +class QLoggingCategory; +const QLoggingCategory& thread_safety(); + namespace hifi { namespace qt { void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr); +QString isBlockingForbiddenThread(QThread* currentThread); bool blockingInvokeMethod( const char* function, @@ -49,6 +56,43 @@ bool blockingInvokeMethod( QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()); +// handling unregistered functions +template +typename std::enable_if::value, bool>::type +blockingInvokeMethod(const char* callingFunction, QObject* context, Func function, ReturnType* retVal) { + auto currentThread = QThread::currentThread(); + if (currentThread == qApp->thread()) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << callingFunction; + return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection, retVal); + } + + QString forbiddenThread = isBlockingForbiddenThread(currentThread); + if (!forbiddenThread.isEmpty()) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << forbiddenThread; + } + + PROFILE_RANGE(app, callingFunction); + return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection, retVal); +} + +template +typename std::enable_if::value, bool>::type +blockingInvokeMethod(const char* callingFunction, QObject* context, Func function) { + auto currentThread = QThread::currentThread(); + if (currentThread == qApp->thread()) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << callingFunction; + return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection); + } + + QString forbiddenThread = isBlockingForbiddenThread(currentThread); + if (!forbiddenThread.isEmpty()) { + qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << forbiddenThread; + } + + PROFILE_RANGE(app, callingFunction); + return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection); +} + // Inspecting of the qt event queue // requres access to private Qt datastructures // Querying the event queue should be done with