From 306f3d07c97af5c1f21d360f3a300d2cf0445923 Mon Sep 17 00:00:00 2001 From: Heather Anderson <heath@odysseus.anderson.name> Date: Mon, 21 Mar 2022 00:24:27 -0700 Subject: [PATCH] remove requirement that ScriptManager::evaluate be meta-invokable --- interface/src/ui/JSConsole.cpp | 7 ++-- libraries/shared/src/shared/QtHelpers.cpp | 10 ++++++ libraries/shared/src/shared/QtHelpers.h | 44 +++++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) 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 <QtCore/QObject> +#include <QtCore/QLoggingCategory> + +#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 Func, typename ReturnType> +typename std::enable_if<!std::is_convertible<Func, const char*>::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 Func> +typename std::enable_if<!std::is_convertible<Func, const char*>::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