diff options
-rw-r--r-- | src/corelib/thread/qmutex.h | 15 | ||||
-rw-r--r-- | src/gui/kernel/qwidget_mac.mm | 12 | ||||
-rw-r--r-- | src/gui/util/qsystemtrayicon_mac.mm | 4 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkconnectionchannel.cpp | 6 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo.cpp | 129 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_p.h | 32 | ||||
-rw-r--r-- | src/network/socket/qabstractsocket.cpp | 18 | ||||
-rw-r--r-- | tests/auto/qhostinfo/tst_qhostinfo.cpp | 55 | ||||
-rw-r--r-- | tests/auto/qsslsocket/qsslsocket.pro | 2 | ||||
-rw-r--r-- | tests/auto/qsslsocket/tst_qsslsocket.cpp | 6 | ||||
-rw-r--r-- | tests/auto/qtcpsocket/qtcpsocket.pro | 3 | ||||
-rw-r--r-- | tests/auto/qtcpsocket/tst_qtcpsocket.cpp | 13 |
12 files changed, 265 insertions, 30 deletions
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 80b50fc..677412e 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -95,7 +95,7 @@ class Q_CORE_EXPORT QMutexLocker { public: inline explicit QMutexLocker(QMutex *m) - : mtx(m) + : val(reinterpret_cast<quintptr>(m)) { Q_ASSERT_X((val & quintptr(1u)) == quintptr(0), "QMutexLocker", "QMutex pointer is misaligned"); @@ -105,19 +105,19 @@ public: inline void unlock() { - if (mtx) { + if (val) { if ((val & quintptr(1u)) == quintptr(1u)) { val &= ~quintptr(1u); - mtx->unlock(); + mutex()->unlock(); } } } inline void relock() { - if (mtx) { + if (val) { if ((val & quintptr(1u)) == quintptr(0u)) { - mtx->lock(); + mutex()->lock(); val |= quintptr(1u); } } @@ -140,10 +140,7 @@ public: private: Q_DISABLE_COPY(QMutexLocker) - union { - QMutex *mtx; - quintptr val; - }; + quintptr val; }; #else // QT_NO_THREAD diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index f78596e..c7acf69 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -1268,6 +1268,11 @@ OSStatus QWidgetPrivate::qt_widget_event(EventHandlerCallRef er, EventRef event, if (widget->isVisible() && widget->updatesEnabled()) { //process the actual paint event. if(widget->testAttribute(Qt::WA_WState_InPaintEvent)) qWarning("QWidget::repaint: Recursive repaint detected"); + if (widget->isWindow() && !widget->d_func()->isOpaque + && !widget->testAttribute(Qt::WA_MacBrushedMetal)) { + QRect qrgnRect = qrgn.boundingRect(); + CGContextClearRect(cg, CGRectMake(qrgnRect.x(), qrgnRect.y(), qrgnRect.width(), qrgnRect.height())); + } QPoint redirectionOffset(0, 0); QWidget *tl = widget->window(); @@ -1318,13 +1323,6 @@ OSStatus QWidgetPrivate::qt_widget_event(EventHandlerCallRef er, EventRef event, widget->d_func()->restoreRedirected(); } - if (widget->isWindow() && !widget->d_func()->isOpaque - && !widget->testAttribute(Qt::WA_MacBrushedMetal)) { - QRect qrgnRect = qrgn.boundingRect(); - CGContextClearRect(cg, CGRectMake(qrgnRect.x(), qrgnRect.y(), qrgnRect.width(), qrgnRect.height())); - } - - if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) CallNextEventHandler(er, event); diff --git a/src/gui/util/qsystemtrayicon_mac.mm b/src/gui/util/qsystemtrayicon_mac.mm index 0265a83..d829947 100644 --- a/src/gui/util/qsystemtrayicon_mac.mm +++ b/src/gui/util/qsystemtrayicon_mac.mm @@ -530,7 +530,11 @@ private: [item setToolTip:(NSString*)QCFString::toCFStringRef(action->toolTip())]; const QIcon icon = action->icon(); if(!icon.isNull()) { +#ifndef QT_MAC_USE_COCOA + const short scale = GetMBarHeight(); +#else const short scale = [[NSApp mainMenu] menuBarHeight]; +#endif NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(icon.pixmap(QSize(scale, scale)))); [item setImage: nsimage]; [nsimage release]; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index e92b2f3..806452c 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -681,7 +681,11 @@ void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests() connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i)); alreadyPipelinedRequests.clear(); - QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + // only run when the QHttpNetworkConnection is not currently being destructed, e.g. + // this function is called from _q_disconnected which is called because + // of ~QHttpNetworkConnectionPrivate + if (qobject_cast<QHttpNetworkConnection*>(connection)) + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); } void QHttpNetworkConnectionChannel::eatWhitespace() diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index b23f6db..a65ca50 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -181,9 +181,26 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, receiver, member, Qt::QueuedConnection); result.data()->emitResultsReady(hostInfo); #else - QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); - QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); - theHostInfoLookupManager()->scheduleLookup(runnable); + QHostInfoLookupManager *manager = theHostInfoLookupManager(); + if (manager) { + // the application is still alive + if (manager->cache.isEnabled()) { + // check cache first + bool valid = false; + QHostInfo info = manager->cache.get(name, &valid); + if (valid) { + info.setLookupId(id); + QHostInfoResult result; + QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); + result.emitResultsReady(info); + return id; + } + } + // cache is not enabled or it was not in the cache, do normal lookup + QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); + QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); + manager->scheduleLookup(runnable); + } #endif return id; @@ -418,14 +435,12 @@ void QHostInfoRunnable::run() return; } - // check cache - // FIXME - // if not in cache: OS lookup QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp); // save to cache - // FIXME + if (manager->cache.isEnabled()) + manager->cache.put(toBeLookedUp, hostInfo); // check aborted again if (manager->wasAborted(id)) { @@ -575,6 +590,106 @@ void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r) work(); } +// This function returns immediatly when we had a result in the cache, else it will later emit a signal +QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id) +{ + *valid = false; + *id = -1; + + // check cache + QHostInfoLookupManager* manager = theHostInfoLookupManager(); + if (manager && manager->cache.isEnabled()) { + QHostInfo info = manager->cache.get(name, valid); + if (*valid) { + return info; + } + } + + // was not in cache, trigger lookup + *id = QHostInfo::lookupHost(name, receiver, member); + + // return empty response, valid==false + return QHostInfo(); +} + +void qt_qhostinfo_clear_cache() +{ + QHostInfoLookupManager* manager = theHostInfoLookupManager(); + if (manager) { + manager->cache.clear(); + } +} + +void Q_NETWORK_EXPORT qt_qhostinfo_enable_cache(bool e) +{ + QHostInfoLookupManager* manager = theHostInfoLookupManager(); + if (manager) { + manager->cache.setEnabled(e); + } +} + +// cache for 60 seconds +// cache 64 items +QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(64) +{ +#ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT + enabled = false; +#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); + + *valid = false; + if (cache.contains(name)) { + QHostInfoCacheElement *element = cache.object(name); + if (element->age.elapsed() < max_age*1000) + *valid = true; + return element->info; + + // FIXME idea: + // if too old but not expired, trigger a new lookup + // to freshen our cache + } + + return QHostInfo(); +} + +void QHostInfoCache::put(const QString &name, const QHostInfo &info) +{ + // if the lookup failed, don't cache + if (info.error() != QHostInfo::NoError) + return; + + QHostInfoCacheElement* element = new QHostInfoCacheElement(); + element->info = info; + element->age = QTime(); + element->age.start(); + + QMutexLocker locker(&this->mutex); + cache.insert(name, element); // cache will take ownership +} + +void QHostInfoCache::clear() +{ + QMutexLocker locker(&this->mutex); + cache.clear(); +} + #endif // QT_NO_THREAD QT_END_NAMESPACE diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h index d6aa287..2b26b07 100644 --- a/src/network/kernel/qhostinfo_p.h +++ b/src/network/kernel/qhostinfo_p.h @@ -68,6 +68,8 @@ #include "QtCore/qrunnable.h" #include "QtCore/qlist.h" #include "QtCore/qqueue.h" +#include <QTime> +#include <QCache> #endif QT_BEGIN_NAMESPACE @@ -111,6 +113,34 @@ public: }; #ifndef QT_NO_THREAD +// These functions are outside of the QHostInfo class and strictly internal. +// Do NOT use them outside of QAbstractSocket. +QHostInfo Q_NETWORK_EXPORT qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id); +void Q_NETWORK_EXPORT qt_qhostinfo_clear_cache(); +void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e); + +class QHostInfoCache +{ +public: + QHostInfoCache(); + const int max_age; // seconds + + QHostInfo get(const QString &name, bool *valid); + void put(const QString &name, const QHostInfo &info); + void clear(); + + bool isEnabled(); + void setEnabled(bool e); +private: + bool enabled; + struct QHostInfoCacheElement { + QHostInfo info; + QTime age; + }; + QCache<QString,QHostInfoCacheElement> cache; + QMutex mutex; +}; + // the following classes are used for the (normal) case: We use multiple threads to lookup DNS class QHostInfoRunnable : public QRunnable @@ -141,6 +171,7 @@ public: void lookupFinished(QHostInfoRunnable *r); bool wasAborted(int id); + QHostInfoCache cache; protected: QList<QHostInfoRunnable*> currentLookups; // in progress QList<QHostInfoRunnable*> postponedLookups; // postponed because in progress for same host @@ -154,6 +185,7 @@ protected: bool wasDeleted; }; + #endif QT_END_NAMESPACE diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 275c436..d990448 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -354,6 +354,8 @@ #include "qabstractsocket.h" #include "qabstractsocket_p.h" +#include "private/qhostinfo_p.h" + #include <qabstracteventdispatcher.h> #include <qdatetime.h> #include <qhostaddress.h> @@ -1369,8 +1371,20 @@ void QAbstractSocket::connectToHostImplementation(const QString &hostName, quint return; #endif } else { - if (d->threadData->eventDispatcher) - d->hostLookupId = QHostInfo::lookupHost(hostName, this, SLOT(_q_startConnecting(QHostInfo))); + if (d->threadData->eventDispatcher) { + // this internal API for QHostInfo either immediatly gives us the desired + // QHostInfo from cache or later calls the _q_startConnecting slot. + bool immediateResultValid = false; + QHostInfo hostInfo = qt_qhostinfo_lookup(hostName, + this, + SLOT(_q_startConnecting(QHostInfo)), + &immediateResultValid, + &d->hostLookupId); + if (immediateResultValid) { + d->hostLookupId = -1; + d->_q_startConnecting(hostInfo); + } + } } #if defined(QABSTRACTSOCKET_DEBUG) diff --git a/tests/auto/qhostinfo/tst_qhostinfo.cpp b/tests/auto/qhostinfo/tst_qhostinfo.cpp index d5411d0..cbadcf5 100644 --- a/tests/auto/qhostinfo/tst_qhostinfo.cpp +++ b/tests/auto/qhostinfo/tst_qhostinfo.cpp @@ -72,6 +72,7 @@ #endif #include <qhostinfo.h> +#include "private/qhostinfo_p.h" #if !defined(QT_NO_GETADDRINFO) # if !defined(Q_OS_WINCE) @@ -108,10 +109,11 @@ public: public slots: void init(); void cleanup(); + void initTestCase(); + private slots: void getSetCheck(); void staticInformation(); - void initTestCase(); void lookupIPv4_data(); void lookupIPv4(); void lookupIPv6_data(); @@ -128,6 +130,8 @@ private slots: void multipleSameLookups(); void multipleDifferentLookups(); + void cache(); + protected slots: void resultsReady(const QHostInfo &); @@ -205,10 +209,21 @@ void tst_QHostInfo::initTestCase() // We have IPv6 support ipv6Available = true; } + + + // run each testcase with and without test enabled + QTest::addColumn<bool>("cache"); + QTest::newRow("WithCache") << false; + QTest::newRow("WithoutCache") << true; } void tst_QHostInfo::init() { + // delete the cache so inidividual testcase results are independant from each other + qt_qhostinfo_clear_cache(); + + QFETCH_GLOBAL(bool, cache); + qt_qhostinfo_enable_cache(cache); } void tst_QHostInfo::cleanup() @@ -458,6 +473,44 @@ void tst_QHostInfo::multipleDifferentLookups() QTRY_VERIFY(lookupsDoneCounter == COUNT); } +void tst_QHostInfo::cache() +{ + QFETCH_GLOBAL(bool, cache); + if (!cache) + return; // test makes only sense when cache enabled + + // reset slot counter + lookupsDoneCounter = 0; + + // lookup once, wait in event loop, result should not come directly. + bool valid = true; + QHostInfo result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(valid == false); + QVERIFY(result.addresses().isEmpty()); + + // loopkup second time, result should come directly + valid = false; + result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid); + QVERIFY(valid == true); + QVERIFY(!result.addresses().isEmpty()); + + // clear the cache + qt_qhostinfo_clear_cache(); + + // lookup third time, result should not come directly. + valid = true; + result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(valid == false); + QVERIFY(result.addresses().isEmpty()); + + // the slot should have been called 2 times. + QVERIFY(lookupsDoneCounter == 2); +} + void tst_QHostInfo::resultsReady(const QHostInfo &hi) { lookupDone = true; diff --git a/tests/auto/qsslsocket/qsslsocket.pro b/tests/auto/qsslsocket/qsslsocket.pro index 147175e..541b2d9 100644 --- a/tests/auto/qsslsocket/qsslsocket.pro +++ b/tests/auto/qsslsocket/qsslsocket.pro @@ -32,3 +32,5 @@ wince* { } else { DEFINES += SRCDIR=\\\"$$PWD/\\\" } + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index abd3237..ad2b50d 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -54,6 +54,8 @@ #include <QNetworkProxy> #include <QAuthenticator> +#include "private/qhostinfo_p.h" + #include "../network-settings.h" Q_DECLARE_METATYPE(QAbstractSocket::SocketState) @@ -288,6 +290,8 @@ void tst_QSslSocket::init() } QNetworkProxy::setApplicationProxy(proxy); } + + qt_qhostinfo_clear_cache(); } void tst_QSslSocket::cleanup() @@ -477,7 +481,7 @@ void tst_QSslSocket::simpleConnectWithIgnore() // Start connecting socket.connectToHost(QtNetworkSettings::serverName(), 993); - QCOMPARE(socket.state(), QAbstractSocket::HostLookupState); + QVERIFY(socket.state() != QAbstractSocket::UnconnectedState); // something must be in progress enterLoop(10); // Start handshake diff --git a/tests/auto/qtcpsocket/qtcpsocket.pro b/tests/auto/qtcpsocket/qtcpsocket.pro index 4bbec23..3d4eba3 100644 --- a/tests/auto/qtcpsocket/qtcpsocket.pro +++ b/tests/auto/qtcpsocket/qtcpsocket.pro @@ -3,3 +3,6 @@ TEMPLATE = subdirs !wince*: SUBDIRS = test stressTest wince*|symbian*|vxworks* : SUBDIRS = test + + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp index e638e287..ef7ff83 100644 --- a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp @@ -92,6 +92,8 @@ #include <unistd.h> #endif +#include "private/qhostinfo_p.h" + #include "../network-settings.h" Q_DECLARE_METATYPE(QAbstractSocket::SocketError) @@ -315,6 +317,8 @@ void tst_QTcpSocket::init() } QNetworkProxy::setApplicationProxy(proxy); } + + qt_qhostinfo_clear_cache(); } QTcpSocket *tst_QTcpSocket::newSocket() const @@ -1059,7 +1063,9 @@ void tst_QTcpSocket::disconnectWhileLookingUp() // just connect and disconnect, then make sure nothing weird happened QTcpSocket *socket = newSocket(); socket->connectToHost(QtNetworkSettings::serverName(), 21); - QVERIFY(socket->state() == QAbstractSocket::HostLookupState); + + // check that connect is in progress + QVERIFY(socket->state() != QAbstractSocket::UnconnectedState); QFETCH(bool, doClose); if (doClose) { @@ -1665,7 +1671,10 @@ void tst_QTcpSocket::waitForConnectedInHostLookupSlot() connect(tmpSocket, SIGNAL(hostFound()), this, SLOT(hostLookupSlot())); tmpSocket->connectToHost(QtNetworkSettings::serverName(), 143); - loop.exec(); + // only execute the loop if not already connected + if (tmpSocket->state() != QAbstractSocket::ConnectedState) + loop.exec(); + QCOMPARE(timerSpy.count(), 0); delete tmpSocket; |