diff options
-rw-r--r-- | src/network/kernel/qhostinfo.cpp | 50 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_p.h | 9 | ||||
-rw-r--r-- | tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp | 13 |
3 files changed, 62 insertions, 10 deletions
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index 597c616fe9..7c82637564 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -106,11 +106,39 @@ int get_signal_index() return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject); } -void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver, - QtPrivate::QSlotObjectBase *slotObj) +} + +/* + The calling thread is likely the one that executes the lookup via + QHostInfoRunnable. Unless we operate with a queued connection already, + posts the QHostInfo to a dedicated QHostInfoResult object that lives in + the same thread as the user-provided receiver, or (if there is none) in + the thread that made the call to lookupHost. That QHostInfoResult object + then calls the user code in the correct thread. + + The 'result' object deletes itself (via deleteLater) when the metacall + event is received. +*/ +void QHostInfoResult::postResultsReady(const QHostInfo &info) { + // queued connection will take care of dispatching to right thread + if (!slotObj) { + emitResultsReady(info); + return; + } static const int signal_index = get_signal_index(); + + // we used to have a context object, but it's already destroyed + if (withContextObject && !receiver) + return; + + /* QHostInfoResult c'tor moves the result object to the thread of receiver. + If we don't have a receiver, then the result object will not live in a + thread that runs an event loop - so move it to this' thread, which is the thread + that initiated the lookup, and required to have a running event loop. */ auto result = new QHostInfoResult(receiver, slotObj); + if (!receiver) + result->moveToThread(thread()); Q_CHECK_PTR(result); const int nargs = 2; auto types = reinterpret_cast<int *>(malloc(nargs * sizeof(int))); @@ -120,15 +148,13 @@ void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver, auto args = reinterpret_cast<void **>(malloc(nargs * sizeof(void *))); Q_CHECK_PTR(args); args[0] = 0; - args[1] = QMetaType::create(types[1], &hostInfo); + args[1] = QMetaType::create(types[1], &info); Q_CHECK_PTR(args[1]); auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs, types, args); Q_CHECK_PTR(metaCallEvent); qApp->postEvent(result, metaCallEvent); } -} - /*! \class QHostInfo \brief The QHostInfo class provides static functions for host name lookups. @@ -335,6 +361,10 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, ready, the \a functor is called with a QHostInfo argument. The QHostInfo object can then be inspected to get the results of the lookup. + + The \a functor will be run in the thread that makes the call to lookupHost; + that thread must have a running Qt event loop. + \note There is no guarantee on the order the signals will be emitted if you start multiple requests with lookupHost(). @@ -632,7 +662,8 @@ int QHostInfo::lookupHostImpl(const QString &name, QHostInfo hostInfo(id); hostInfo.setError(QHostInfo::HostNotFound); hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given")); - emit_results_ready(hostInfo, receiver, slotObj); + QHostInfoResult result(receiver, slotObj); + result.postResultsReady(hostInfo); return id; } @@ -646,7 +677,8 @@ int QHostInfo::lookupHostImpl(const QString &name, QHostInfo info = manager->cache.get(name, &valid); if (valid) { info.setLookupId(id); - emit_results_ready(info, receiver, slotObj); + QHostInfoResult result(receiver, slotObj); + result.postResultsReady(info); return id; } } @@ -707,7 +739,7 @@ void QHostInfoRunnable::run() // signal emission hostInfo.setLookupId(id); - resultEmitter.emitResultsReady(hostInfo); + resultEmitter.postResultsReady(hostInfo); #if QT_CONFIG(thread) // now also iterate through the postponed ones @@ -720,7 +752,7 @@ void QHostInfoRunnable::run() QHostInfoRunnable* postponed = *it; // we can now emit hostInfo.setLookupId(postponed->id); - postponed->resultEmitter.emitResultsReady(hostInfo); + postponed->resultEmitter.postResultsReady(hostInfo); delete postponed; } manager->postponedLookups.erase(partitionBegin, partitionEnd); diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index 8898d6ff50..bbf4cc36d1 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -84,12 +84,14 @@ class QHostInfoResult : public QObject QPointer<const QObject> receiver = nullptr; QtPrivate::QSlotObjectBase *slotObj = nullptr; + const bool withContextObject = false; public: QHostInfoResult() = default; QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) : receiver(receiver), - slotObj(slotObj) + slotObj(slotObj), + withContextObject(slotObj && receiver) { connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater); @@ -97,10 +99,15 @@ public: moveToThread(receiver->thread()); } + void postResultsReady(const QHostInfo &info); + public Q_SLOTS: inline void emitResultsReady(const QHostInfo &info) { if (slotObj) { + // we used to have a context object, but it's already destroyed + if (withContextObject && !receiver) + return; QHostInfo copy = info; void *args[2] = { 0, reinterpret_cast<void *>(©) }; slotObj->call(const_cast<QObject*>(receiver.data()), args); diff --git a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp index 0a130d363e..9905cfe075 100644 --- a/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp @@ -92,6 +92,7 @@ private slots: void lookupIPv6(); void lookupConnectToFunctionPointer_data(); void lookupConnectToFunctionPointer(); + void lookupConnectToFunctionPointerDeleted(); void lookupConnectToLambda_data(); void lookupConnectToLambda(); void reverseLookup_data(); @@ -361,6 +362,17 @@ void tst_QHostInfo::lookupConnectToFunctionPointer() QCOMPARE(tmp.join(' '), expected.join(' ')); } +void tst_QHostInfo::lookupConnectToFunctionPointerDeleted() +{ + { + QObject contextObject; + QHostInfo::lookupHost("localhost", &contextObject, [](const QHostInfo){ + QFAIL("This should never be called!"); + }); + } + QTestEventLoop::instance().enterLoop(3); +} + void tst_QHostInfo::lookupConnectToLambda_data() { lookupIPv4_data(); @@ -708,6 +720,7 @@ void tst_QHostInfo::cache() void tst_QHostInfo::resultsReady(const QHostInfo &hi) { + QVERIFY(QThread::currentThread() == thread()); lookupDone = true; lookupResults = hi; lookupsDoneCounter++; |