summaryrefslogtreecommitdiffstats
path: root/src/network/kernel/qhostinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/kernel/qhostinfo.cpp')
-rw-r--r--src/network/kernel/qhostinfo.cpp163
1 files changed, 98 insertions, 65 deletions
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 25ff873307..487cac6d90 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -47,6 +47,7 @@
#include <qabstracteventdispatcher.h>
#include <qcoreapplication.h>
#include <qmetaobject.h>
+#include <qscopeguard.h>
#include <qstringlist.h>
#include <qthread.h>
#include <qurl.h>
@@ -85,13 +86,6 @@ private:
QString m_toBeLookedUp;
};
-// ### C++11: remove once we can use std::any_of()
-template<class InputIt, class UnaryPredicate>
-bool any_of(InputIt first, InputIt last, UnaryPredicate p)
-{
- return std::find_if(first, last, p) != last;
-}
-
template <typename InputIt, typename OutputIt1, typename OutputIt2, typename UnaryPredicate>
std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputIt1 dest1, OutputIt2 dest2, UnaryPredicate p)
{
@@ -118,11 +112,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)));
@@ -132,15 +154,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.
@@ -328,6 +348,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().
@@ -398,7 +422,7 @@ QHostInfo QHostInfo::fromName(const QString &name)
#endif
QHostInfo hostInfo = QHostInfoAgent::fromName(name);
- QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
manager->cache.put(name, hostInfo);
return hostInfo;
}
@@ -411,7 +435,7 @@ QHostInfo QHostInfoPrivate::fromName(const QString &name, QSharedPointer<QNetwor
#endif
QHostInfo hostInfo = QHostInfoAgent::fromName(name, session);
- QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
manager->cache.put(name, hostInfo);
return hostInfo;
}
@@ -600,8 +624,9 @@ QHostInfo QHostInfoAgent::lookup(const QString &hostName)
\sa lookupId()
*/
QHostInfo::QHostInfo(int id)
- : d(new QHostInfoPrivate)
+ : d_ptr(new QHostInfoPrivate)
{
+ Q_D(QHostInfo);
d->lookupId = id;
}
@@ -609,17 +634,30 @@ QHostInfo::QHostInfo(int id)
Constructs a copy of \a other.
*/
QHostInfo::QHostInfo(const QHostInfo &other)
- : d(new QHostInfoPrivate(*other.d.data()))
+ : d_ptr(new QHostInfoPrivate(*other.d_ptr))
{
}
/*!
+ Move-constucts a new QHostInfo from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+
+ \since 5.14
+*/
+
+/*!
Assigns the data of the \a other object to this host info object,
and returns a reference to it.
*/
QHostInfo &QHostInfo::operator=(const QHostInfo &other)
{
- *d.data() = *other.d.data();
+ if (d_ptr)
+ *d_ptr = *other.d_ptr;
+ else
+ d_ptr = new QHostInfoPrivate(*other.d_ptr);
return *this;
}
@@ -628,6 +666,7 @@ QHostInfo &QHostInfo::operator=(const QHostInfo &other)
*/
QHostInfo::~QHostInfo()
{
+ delete d_ptr;
}
/*!
@@ -642,6 +681,7 @@ QHostInfo::~QHostInfo()
*/
QList<QHostAddress> QHostInfo::addresses() const
{
+ Q_D(const QHostInfo);
return d->addrs;
}
@@ -652,6 +692,7 @@ QList<QHostAddress> QHostInfo::addresses() const
*/
void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
{
+ Q_D(QHostInfo);
d->addrs = addresses;
}
@@ -662,6 +703,7 @@ void QHostInfo::setAddresses(const QList<QHostAddress> &addresses)
*/
QString QHostInfo::hostName() const
{
+ Q_D(const QHostInfo);
return d->hostName;
}
@@ -672,6 +714,7 @@ QString QHostInfo::hostName() const
*/
void QHostInfo::setHostName(const QString &hostName)
{
+ Q_D(QHostInfo);
d->hostName = hostName;
}
@@ -683,6 +726,7 @@ void QHostInfo::setHostName(const QString &hostName)
*/
QHostInfo::HostInfoError QHostInfo::error() const
{
+ Q_D(const QHostInfo);
return d->err;
}
@@ -693,6 +737,7 @@ QHostInfo::HostInfoError QHostInfo::error() const
*/
void QHostInfo::setError(HostInfoError error)
{
+ Q_D(QHostInfo);
d->err = error;
}
@@ -703,6 +748,7 @@ void QHostInfo::setError(HostInfoError error)
*/
int QHostInfo::lookupId() const
{
+ Q_D(const QHostInfo);
return d->lookupId;
}
@@ -713,6 +759,7 @@ int QHostInfo::lookupId() const
*/
void QHostInfo::setLookupId(int id)
{
+ Q_D(QHostInfo);
d->lookupId = id;
}
@@ -724,6 +771,7 @@ void QHostInfo::setLookupId(int id)
*/
QString QHostInfo::errorString() const
{
+ Q_D(const QHostInfo);
return d->errorStr;
}
@@ -735,6 +783,7 @@ QString QHostInfo::errorString() const
*/
void QHostInfo::setErrorString(const QString &str)
{
+ Q_D(QHostInfo);
d->errorStr = str;
}
@@ -791,7 +840,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;
}
@@ -805,7 +855,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;
}
}
@@ -833,11 +884,10 @@ QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *re
void QHostInfoRunnable::run()
{
QHostInfoLookupManager *manager = theHostInfoLookupManager();
+ const auto sg = qScopeGuard([&] { manager->lookupFinished(this); });
// check aborted
- if (manager->wasAborted(id)) {
- manager->lookupFinished(this);
+ if (manager->wasAborted(id))
return;
- }
QHostInfo hostInfo;
@@ -859,14 +909,12 @@ void QHostInfoRunnable::run()
}
// check aborted again
- if (manager->wasAborted(id)) {
- manager->lookupFinished(this);
+ if (manager->wasAborted(id))
return;
- }
// signal emission
hostInfo.setLookupId(id);
- resultEmitter.emitResultsReady(hostInfo);
+ resultEmitter.postResultsReady(hostInfo);
#if QT_CONFIG(thread)
// now also iterate through the postponed ones
@@ -879,30 +927,31 @@ 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);
}
#endif
- manager->lookupFinished(this);
-
// thread goes back to QThreadPool
}
QHostInfoLookupManager::QHostInfoLookupManager() : wasDeleted(false)
{
- moveToThread(QCoreApplicationPrivate::mainThread());
#if QT_CONFIG(thread)
- connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection);
+ QObject::connect(QCoreApplication::instance(), &QObject::destroyed,
+ &threadPool, [&](QObject *) { threadPool.waitForDone(); },
+ Qt::DirectConnection);
threadPool.setMaxThreadCount(20); // do up to 20 DNS lookups in parallel
#endif
}
QHostInfoLookupManager::~QHostInfoLookupManager()
{
+ QMutexLocker locker(&mutex);
wasDeleted = true;
+ locker.unlock();
// don't qDeleteAll currentLookups, the QThreadPool has ownership
clear();
@@ -928,7 +977,8 @@ void QHostInfoLookupManager::clear()
cache.clear();
}
-void QHostInfoLookupManager::work()
+// assumes mutex is locked by caller
+void QHostInfoLookupManager::rescheduleWithMutexHeld()
{
if (wasDeleted)
return;
@@ -937,8 +987,6 @@ void QHostInfoLookupManager::work()
// - launch new lookups via the thread pool
// - make sure only one lookup per host/IP is in progress
- QMutexLocker locker(&mutex);
-
if (!finishedLookups.isEmpty()) {
// remove ID from aborted if it is in there
for (int i = 0; i < finishedLookups.length(); i++) {
@@ -950,7 +998,7 @@ void QHostInfoLookupManager::work()
#if QT_CONFIG(thread)
auto isAlreadyRunning = [this](QHostInfoRunnable *lookup) {
- return any_of(currentLookups.cbegin(), currentLookups.cend(), ToBeLookedUpEquals(lookup->toBeLookedUp));
+ return std::any_of(currentLookups.cbegin(), currentLookups.cend(), ToBeLookedUpEquals(lookup->toBeLookedUp));
};
// Transfer any postponed lookups that aren't currently running to the scheduled list, keeping already-running lookups:
@@ -990,22 +1038,23 @@ void QHostInfoLookupManager::work()
// called by QHostInfo
void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return;
- QMutexLocker locker(&this->mutex);
scheduledLookups.enqueue(r);
- work();
+ rescheduleWithMutexHeld();
}
// called by QHostInfo
void QHostInfoLookupManager::abortLookup(int id)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return;
- QMutexLocker locker(&this->mutex);
-
#if QT_CONFIG(thread)
// is postponed? delete and return
for (int i = 0; i < postponedLookups.length(); i++) {
@@ -1031,25 +1080,27 @@ void QHostInfoLookupManager::abortLookup(int id)
// called from QHostInfoRunnable
bool QHostInfoLookupManager::wasAborted(int id)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return true;
- QMutexLocker locker(&this->mutex);
return abortedLookups.contains(id);
}
// called from QHostInfoRunnable
void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
{
+ QMutexLocker locker(&this->mutex);
+
if (wasDeleted)
return;
- QMutexLocker locker(&this->mutex);
#if QT_CONFIG(thread)
currentLookups.removeOne(r);
#endif
finishedLookups.append(r);
- work();
+ rescheduleWithMutexHeld();
}
// This function returns immediately when we had a result in the cache, else it will later emit a signal
@@ -1059,7 +1110,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char
*id = -1;
// check cache
- QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
if (manager && manager->cache.isEnabled()) {
QHostInfo info = manager->cache.get(name, valid);
if (*valid) {
@@ -1076,7 +1127,7 @@ QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char
void qt_qhostinfo_clear_cache()
{
- QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
if (manager) {
manager->clear();
}
@@ -1085,7 +1136,7 @@ void qt_qhostinfo_clear_cache()
#ifdef QT_BUILD_INTERNAL
void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
{
- QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
if (manager) {
manager->cache.setEnabled(e);
}
@@ -1093,7 +1144,7 @@ void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e)
void qt_qhostinfo_cache_inject(const QString &hostname, const QHostInfo &resolution)
{
- QAbstractHostInfoLookupManager* manager = theHostInfoLookupManager();
+ QHostInfoLookupManager* manager = theHostInfoLookupManager();
if (!manager || !manager->cache.isEnabled())
return;
@@ -1106,23 +1157,10 @@ void qt_qhostinfo_cache_inject(const QString &hostname, const QHostInfo &resolut
QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(128)
{
#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT
- enabled = false;
+ enabled.store(false, std::memory_order_relaxed);
#endif
}
-bool QHostInfoCache::isEnabled()
-{
- return enabled;
-}
-
-// this function is currently only used for the auto tests
-// and not usable by public API
-void QHostInfoCache::setEnabled(bool e)
-{
- enabled = e;
-}
-
-
QHostInfo QHostInfoCache::get(const QString &name, bool *valid)
{
QMutexLocker locker(&this->mutex);
@@ -1162,9 +1200,4 @@ void QHostInfoCache::clear()
cache.clear();
}
-QAbstractHostInfoLookupManager* QAbstractHostInfoLookupManager::globalInstance()
-{
- return theHostInfoLookupManager();
-}
-
QT_END_NAMESPACE