summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Astals Cid <tsdgeos@terra.es>2011-08-22 10:50:59 (GMT)
committerAlbert Astals Cid <tsdgeos@terra.es>2011-08-22 10:52:35 (GMT)
commita4871052d09eecd928666d482427abebb5f39c56 (patch)
tree7a1ed9ddaab0dd6a8d969fd8fffda9ee424b83dc
parent24498bb761630e24e39df1ea5c614136eff7310c (diff)
Do not terminate threads when doing dns lookups with timeout
patch based on dfaure and thiago's suggestions: Thiago and I came up with a design like this: * a single thread that makes async lookups. It's never blocked, it can always almost immediately read new requests from a queue and start them. * each request is encapsulated in a class that has the QString but also a QSemaphore. * the main thread can block using a timeout (QSemaphore::tryAcquire(timeout)) * the request class is stored using a shared pointer, so that it's only deleted when neither the main thread nor the worker thread need it anymore [otherwise the deletion is a problem, in the timeout case vs the normal case] REVIEW: 102179
-rw-r--r--kio/kio/hostinfo.cpp167
1 files changed, 124 insertions, 43 deletions
diff --git a/kio/kio/hostinfo.cpp b/kio/kio/hostinfo.cpp
index 344b1d8..0592b91 100644
--- a/kio/kio/hostinfo.cpp
+++ b/kio/kio/hostinfo.cpp
@@ -31,6 +31,8 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include <QtCore/QPair>
#include <QtCore/QThread>
#include <QtCore/QFutureWatcher>
+#include <QtCore/QSemaphore>
+#include <QtCore/QSharedPointer>
#include <QtCore/QtConcurrentRun>
#include <QtNetwork/QHostInfo>
#include "kdebug.h"
@@ -113,61 +115,127 @@ namespace KIO
QString m_hostName;
};
- class NameLookUpThread : public QThread
+ class NameLookupThreadRequest
{
public:
- NameLookUpThread (const QString& name)
- :QThread (0), m_hostName(name), m_started(false)
+ NameLookupThreadRequest(const QString& hostName) : m_hostName(hostName)
+ {
+ }
+
+ QSemaphore *semaphore()
{
+ return &m_semaphore;
}
QHostInfo result() const
{
- return m_hostInfo;
+ return m_hostInfo;
}
- bool wasStarted() const
+ void setResult(const QHostInfo& hostInfo)
{
- return m_started;
+ m_hostInfo = hostInfo;
}
- void run()
+ QString hostName() const
{
- m_started = true;
- m_hostInfo = QHostInfo();
-
- // Do not perform a reverse lookup here...
- QHostAddress address (m_hostName);
- if (!address.isNull()) {
- QList<QHostAddress> addressList;
- addressList << address;
- m_hostInfo.setAddresses(addressList);
- return;
- }
+ return m_hostName;
+ }
- // Look up the name in the KIO/KHTML DNS cache...
- m_hostInfo = HostInfo::lookupCachedHostInfoFor(m_hostName);
- if (!m_hostInfo.hostName().isEmpty() && m_hostInfo.error() == QHostInfo::NoError) {
- return;
- }
+ int lookupId() const
+ {
+ return m_lookupId;
+ }
- // Failing all of the above, do the lookup...
- m_hostInfo = QHostInfo::fromName(m_hostName);
- if (!m_hostInfo.hostName().isEmpty() && m_hostInfo.error() == QHostInfo::NoError) {
- HostInfo::cacheLookup(m_hostInfo); // cache the look up...
- }
+ void setLookupId(int id)
+ {
+ m_lookupId = id;
}
private:
- QHostInfo m_hostInfo;
+ Q_DISABLE_COPY(NameLookupThreadRequest);
QString m_hostName;
- bool m_started;
+ QSemaphore m_semaphore;
+ QHostInfo m_hostInfo;
+ int m_lookupId;
+ };
+
+ class NameLookUpThreadWorker : public QObject
+ {
+ Q_OBJECT
+ public slots:
+ void lookupHost(const QSharedPointer<NameLookupThreadRequest>& request)
+ {
+ const QString hostName = request->hostName();
+ const int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo)));
+ request->setLookupId(lookupId);
+ m_lookups.insert(lookupId, request);
+ }
+
+ void abortLookup(const QSharedPointer<NameLookupThreadRequest>& request)
+ {
+ QHostInfo::abortHostLookup(request->lookupId());
+ m_lookups.remove(request->lookupId());
+ }
+
+ void lookupFinished(const QHostInfo &hostInfo)
+ {
+ QMap<int, QSharedPointer<NameLookupThreadRequest> >::iterator it = m_lookups.find(hostInfo.lookupId());
+ if (it != m_lookups.end()) {
+ (*it)->setResult(hostInfo);
+ (*it)->semaphore()->release();
+ m_lookups.erase(it);
+ }
+ }
+
+ private:
+ QMap<int, QSharedPointer<NameLookupThreadRequest> > m_lookups;
+ };
+
+ class NameLookUpThread : public QThread
+ {
+ Q_OBJECT
+ public:
+ NameLookUpThread () : m_worker(0)
+ {
+ qRegisterMetaType< QSharedPointer<NameLookupThreadRequest> > ("QSharedPointer<NameLookupThreadRequest>");
+ start();
+ }
+
+ ~NameLookUpThread ()
+ {
+ quit();
+ wait();
+ }
+
+ NameLookUpThreadWorker *worker()
+ {
+ return m_worker;
+ }
+
+ QSemaphore *semaphore()
+ {
+ return &m_semaphore;
+ }
+
+ void run()
+ {
+ NameLookUpThreadWorker worker;
+ m_worker = &worker;
+ m_semaphore.release();
+ exec();
+ }
+
+ private:
+ NameLookUpThreadWorker *m_worker;
+ QSemaphore m_semaphore;
};
}
using namespace KIO;
K_GLOBAL_STATIC(HostInfoAgentPrivate, hostInfoAgentPrivate)
+K_GLOBAL_STATIC(NameLookUpThread, nameLookUpThread)
void HostInfo::lookupHost(const QString& hostName, QObject* receiver,
const char* member)
@@ -177,25 +245,38 @@ void HostInfo::lookupHost(const QString& hostName, QObject* receiver,
QHostInfo HostInfo::lookupHost(const QString& hostName, unsigned long timeout)
{
- NameLookUpThread lookupThread (hostName);
- lookupThread.start();
+ // Do not perform a reverse lookup here...
+ QHostAddress address (hostName);
+ QHostInfo hostInfo;
+ if (!address.isNull()) {
+ QList<QHostAddress> addressList;
+ addressList << address;
+ hostInfo.setAddresses(addressList);
+ return hostInfo;
+ }
- // Wait for it to start...
- while (!lookupThread.wasStarted()) {
- kDebug() << "Waiting for name lookup thread to start";
- lookupThread.wait(1000);
+ // Look up the name in the KIO/KHTML DNS cache...
+ hostInfo = HostInfo::lookupCachedHostInfoFor(hostName);
+ if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
+ return hostInfo;
}
- // Now wait for it to complete...
- if (!lookupThread.wait(timeout)) {
- kDebug() << "Name look up for" << hostName << "failed";
- lookupThread.terminate();
- lookupThread.wait();
- return QHostInfo();
+ // Failing all of the above, do the lookup...
+ QSharedPointer<NameLookupThreadRequest> request = QSharedPointer<NameLookupThreadRequest>(new NameLookupThreadRequest(hostName));
+ nameLookUpThread->semaphore()->acquire();
+ nameLookUpThread->semaphore()->release();
+ QMetaObject::invokeMethod(nameLookUpThread->worker(), "lookupHost", Qt::QueuedConnection, Q_ARG(QSharedPointer<NameLookupThreadRequest>, request));
+ if (request->semaphore()->tryAcquire(1, timeout)) {
+ hostInfo = request->result();
+ if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
+ HostInfo::cacheLookup(hostInfo); // cache the look up...
+ }
+ } else {
+ QMetaObject::invokeMethod(nameLookUpThread->worker(), "abortLookup", Qt::QueuedConnection, Q_ARG(QSharedPointer<NameLookupThreadRequest>, request));
}
//kDebug(7022) << "Name look up succeeded for" << hostName;
- return lookupThread.result();
+ return hostInfo;
}
QHostInfo HostInfo::lookupCachedHostInfoFor(const QString& hostName)