Overte C++ Documentation
QtHelpers.h
1 //
2 // Created by Bradley Austin Davis on 2017/06/29
3 // Copyright 2013-2015 High Fidelity, Inc.
4 // Copyright 2023 Overte e.V.
5 //
6 // Distributed under the Apache License, Version 2.0.
7 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
8 // SPDX-License-Identifier: Apache-2.0
9 //
10 
11 #pragma once
12 #ifndef hifi_Shared_QtHelpers_h
13 #define hifi_Shared_QtHelpers_h
14 
15 #include <QtCore/QObject>
16 #include <QtCore/QLoggingCategory>
17 #include <QtCore/QThread>
18 
19 #include "../Profile.h"
20 
21 #if defined(Q_OS_WIN)
22 // Enable event queue debugging
23 #define DEBUG_EVENT_QUEUE
24 #endif
25 
26 class QLoggingCategory;
27 const QLoggingCategory& thread_safety();
28 
29 namespace hifi { namespace qt {
30 void addBlockingForbiddenThread(const QString& name, QThread* thread = nullptr);
31 QString isBlockingForbiddenThread(QThread* currentThread);
32 
33 bool blockingInvokeMethod(
34  const char* function,
35  QObject *obj, const char *member,
36  QGenericReturnArgument ret,
37  QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
38  QGenericArgument val1 = QGenericArgument(),
39  QGenericArgument val2 = QGenericArgument(),
40  QGenericArgument val3 = QGenericArgument(),
41  QGenericArgument val4 = QGenericArgument(),
42  QGenericArgument val5 = QGenericArgument(),
43  QGenericArgument val6 = QGenericArgument(),
44  QGenericArgument val7 = QGenericArgument(),
45  QGenericArgument val8 = QGenericArgument(),
46  QGenericArgument val9 = QGenericArgument());
47 
48 bool blockingInvokeMethod(
49  const char* function,
50  QObject *obj, const char *member,
51  QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
52  QGenericArgument val1 = QGenericArgument(),
53  QGenericArgument val2 = QGenericArgument(),
54  QGenericArgument val3 = QGenericArgument(),
55  QGenericArgument val4 = QGenericArgument(),
56  QGenericArgument val5 = QGenericArgument(),
57  QGenericArgument val6 = QGenericArgument(),
58  QGenericArgument val7 = QGenericArgument(),
59  QGenericArgument val8 = QGenericArgument(),
60  QGenericArgument val9 = QGenericArgument());
61 
62 // handling unregistered functions
63 template <typename Func, typename ReturnType>
64 typename std::enable_if<!std::is_convertible<Func, const char*>::value, bool>::type
65 blockingInvokeMethod(const char* callingFunction, QObject* context, Func function, ReturnType* retVal) {
66  auto currentThread = QThread::currentThread();
67  if (currentThread == qApp->thread()) {
68  qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << callingFunction;
69  return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection, retVal);
70  }
71 
72  QString forbiddenThread = isBlockingForbiddenThread(currentThread);
73  if (!forbiddenThread.isEmpty()) {
74  qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << forbiddenThread;
75  }
76 
77  PROFILE_RANGE(app, callingFunction);
78  return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection, retVal);
79 }
80 
81 template <typename Func>
82 typename std::enable_if<!std::is_convertible<Func, const char*>::value, bool>::type
83 blockingInvokeMethod(const char* callingFunction, QObject* context, Func function) {
84  auto currentThread = QThread::currentThread();
85  if (currentThread == qApp->thread()) {
86  qCWarning(thread_safety) << "BlockingQueuedConnection invoked on main thread from " << callingFunction;
87  return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection);
88  }
89 
90  QString forbiddenThread = isBlockingForbiddenThread(currentThread);
91  if (!forbiddenThread.isEmpty()) {
92  qCWarning(thread_safety) << "BlockingQueuedConnection invoked on forbidden thread " << forbiddenThread;
93  }
94 
95  PROFILE_RANGE(app, callingFunction);
96  return QMetaObject::invokeMethod(context, function, Qt::BlockingQueuedConnection);
97 }
98 
99 // Inspecting of the qt event queue
100 // requres access to private Qt datastructures
101 // Querying the event queue should be done with
102 // care as it could lock the threadData->postEventList.mutex
103 // The code uses a tryLock to avoid the possability of a
104 // deadlock during a call to this code, although that is unlikely
105 // When getEventQueueSize fails to get the lock, it returns -1
106 #ifdef DEBUG_EVENT_QUEUE
107  int getEventQueueSize(QThread* thread);
108  void dumpEventQueue(QThread* thread);
109 #endif // DEBUG_EVENT_QUEUE
110 
111 } }
112 
113 #define BLOCKING_INVOKE_METHOD(obj, member, ...) \
114  ::hifi::qt::blockingInvokeMethod(__FUNCTION__, obj, member, ##__VA_ARGS__)
115 
116 #endif