File indexing completed on 2024-12-08 07:21:05
0001 /* 0002 * Copyright (C) 2006 Justin Karneges <justin@affinix.com> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Lesser General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2.1 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Lesser General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Lesser General Public 0015 * License along with this library; if not, write to the Free Software 0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0017 * 02110-1301 USA 0018 * 0019 */ 0020 0021 #include "qca_support.h" 0022 0023 #include <QEventLoop> 0024 #include <QMetaMethod> 0025 #include <QMutexLocker> 0026 #include <QWaitCondition> 0027 0028 namespace QCA { 0029 0030 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0031 int methodReturnType(const QMetaObject *obj, const QByteArray &method, const QList<QByteArray> &argTypes) 0032 #else 0033 QByteArray methodReturnType( 0034 const QMetaObject *obj, 0035 const QByteArray &method, 0036 const QList<QByteArray> argTypes) // clazy:exclude=function-args-by-ref NOLINT(performance-unnecessary-value-param) 0037 // TODO make argTypes const & when we break ABI 0038 #endif 0039 { 0040 for (int n = 0; n < obj->methodCount(); ++n) { 0041 QMetaMethod m = obj->method(n); 0042 const QByteArray sig = m.methodSignature(); 0043 int offset = sig.indexOf('('); 0044 if (offset == -1) 0045 continue; 0046 const QByteArray name = sig.mid(0, offset); 0047 if (name != method) 0048 continue; 0049 if (m.parameterTypes() != argTypes) 0050 continue; 0051 0052 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0053 return m.returnType(); 0054 #else 0055 return m.typeName(); 0056 #endif 0057 } 0058 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0059 return QMetaType::UnknownType; 0060 #else 0061 return QByteArray(); 0062 #endif 0063 } 0064 0065 bool invokeMethodWithVariants(QObject *obj, 0066 const QByteArray &method, 0067 const QVariantList &args, 0068 QVariant *ret, 0069 Qt::ConnectionType type) 0070 { 0071 // QMetaObject::invokeMethod() has a 10 argument maximum 0072 if (args.count() > 10) 0073 return false; 0074 0075 QList<QByteArray> argTypes; 0076 for (int n = 0; n < args.count(); ++n) { 0077 argTypes += args[n].typeName(); 0078 } 0079 0080 // get return type 0081 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0082 const auto metatype = methodReturnType(obj->metaObject(), method, argTypes); 0083 if (metatype == QMetaType::UnknownType) { 0084 return false; 0085 } 0086 #else 0087 int metatype = QMetaType::Void; 0088 const QByteArray retTypeName = methodReturnType(obj->metaObject(), method, argTypes); 0089 if (!retTypeName.isEmpty() && retTypeName != "void") { 0090 metatype = QMetaType::type(retTypeName.data()); 0091 if (metatype == QMetaType::UnknownType) // lookup failed 0092 return false; 0093 } 0094 #endif 0095 0096 QGenericArgument arg[10]; 0097 for (int n = 0; n < args.count(); ++n) 0098 arg[n] = QGenericArgument(args[n].typeName(), args[n].constData()); 0099 0100 QGenericReturnArgument retarg; 0101 QVariant retval; 0102 0103 if (metatype != QMetaType::Void) { 0104 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0105 retval = QVariant(QMetaType {metatype}, (const void *)nullptr); 0106 #else 0107 retval = QVariant(metatype, (const void *)nullptr); 0108 #endif 0109 retarg = QGenericReturnArgument(retval.typeName(), retval.data()); 0110 } 0111 0112 if (!QMetaObject::invokeMethod(obj, 0113 method.data(), 0114 type, 0115 retarg, 0116 arg[0], 0117 arg[1], 0118 arg[2], 0119 arg[3], 0120 arg[4], 0121 arg[5], 0122 arg[6], 0123 arg[7], 0124 arg[8], 0125 arg[9])) 0126 return false; 0127 0128 if (retval.isValid() && ret) 0129 *ret = retval; 0130 return true; 0131 } 0132 0133 //---------------------------------------------------------------------------- 0134 // SyncThread 0135 //---------------------------------------------------------------------------- 0136 class SyncThreadAgent; 0137 0138 class SyncThread::Private : public QObject 0139 { 0140 Q_OBJECT 0141 public: 0142 SyncThread *q; 0143 QMutex m; 0144 QWaitCondition w; 0145 QEventLoop *loop; 0146 SyncThreadAgent *agent; 0147 bool last_success; 0148 QVariant last_ret; 0149 0150 Private(SyncThread *_q) 0151 : QObject(_q) 0152 , q(_q) 0153 { 0154 loop = nullptr; 0155 agent = nullptr; 0156 } 0157 0158 public Q_SLOTS: 0159 void agent_started(); 0160 void agent_call_ret(bool success, const QVariant &ret); 0161 }; 0162 0163 class SyncThreadAgent : public QObject 0164 { 0165 Q_OBJECT 0166 public: 0167 SyncThreadAgent(QObject *parent = nullptr) 0168 : QObject(parent) 0169 { 0170 QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection); 0171 } 0172 0173 Q_SIGNALS: 0174 void started(); 0175 void call_ret(bool success, const QVariant &ret); 0176 0177 public Q_SLOTS: 0178 void call_do(QObject *obj, const QByteArray &method, const QVariantList &args) 0179 { 0180 QVariant ret; 0181 bool ok = invokeMethodWithVariants(obj, method, args, &ret, Qt::DirectConnection); 0182 emit call_ret(ok, ret); 0183 } 0184 }; 0185 0186 SyncThread::SyncThread(QObject *parent) 0187 : QThread(parent) 0188 { 0189 d = new Private(this); 0190 qRegisterMetaType<QVariant>("QVariant"); 0191 qRegisterMetaType<QVariantList>("QVariantList"); 0192 } 0193 0194 SyncThread::~SyncThread() 0195 { 0196 stop(); 0197 delete d; 0198 } 0199 0200 void SyncThread::start() 0201 { 0202 QMutexLocker locker(&d->m); 0203 Q_ASSERT(!d->loop); 0204 QThread::start(); 0205 d->w.wait(&d->m); 0206 } 0207 0208 void SyncThread::stop() 0209 { 0210 QMutexLocker locker(&d->m); 0211 if (!d->loop) 0212 return; 0213 QMetaObject::invokeMethod(d->loop, "quit"); 0214 d->w.wait(&d->m); 0215 wait(); 0216 } 0217 0218 QVariant SyncThread::call(QObject *obj, const QByteArray &method, const QVariantList &args, bool *ok) 0219 { 0220 QMutexLocker locker(&d->m); 0221 bool ret; 0222 Q_UNUSED(ret); // In really ret is used. I use this hack to suppress a compiler warning 0223 // clang-format off 0224 // Otherwise the QObject* gets turned into Object * that is not normalized and is slightly slower 0225 ret = QMetaObject::invokeMethod(d->agent, "call_do", 0226 Qt::QueuedConnection, Q_ARG(QObject*, obj), 0227 Q_ARG(QByteArray, method), Q_ARG(QVariantList, args)); 0228 // clang-format on 0229 Q_ASSERT(ret); 0230 d->w.wait(&d->m); 0231 if (ok) 0232 *ok = d->last_success; 0233 QVariant v = d->last_ret; 0234 d->last_ret = QVariant(); 0235 return v; 0236 } 0237 0238 void SyncThread::run() 0239 { 0240 d->m.lock(); 0241 d->loop = new QEventLoop; 0242 d->agent = new SyncThreadAgent; 0243 connect(d->agent, &SyncThreadAgent::started, d, &Private::agent_started, Qt::DirectConnection); 0244 connect(d->agent, &SyncThreadAgent::call_ret, d, &Private::agent_call_ret, Qt::DirectConnection); 0245 d->loop->exec(); 0246 d->m.lock(); 0247 atEnd(); 0248 delete d->agent; 0249 delete d->loop; 0250 d->agent = nullptr; 0251 d->loop = nullptr; 0252 d->w.wakeOne(); 0253 d->m.unlock(); 0254 } 0255 0256 void SyncThread::Private::agent_started() 0257 { 0258 q->atStart(); 0259 w.wakeOne(); 0260 m.unlock(); 0261 } 0262 0263 void SyncThread::Private::agent_call_ret(bool success, const QVariant &ret) 0264 { 0265 QMutexLocker locker(&m); 0266 last_success = success; 0267 last_ret = ret; 0268 w.wakeOne(); 0269 } 0270 0271 } 0272 0273 #include "syncthread.moc"