1 /**************************************************************************** |
1 /**************************************************************************** |
2 ** |
2 ** |
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
4 ** All rights reserved. |
4 ** All rights reserved. |
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
6 ** |
6 ** |
7 ** This file is part of the QtNetwork module of the Qt Toolkit. |
7 ** This file is part of the QtNetwork module of the Qt Toolkit. |
8 ** |
8 ** |
110 IDNA and Punycode standards. |
110 IDNA and Punycode standards. |
111 |
111 |
112 To retrieve the name of the local host, use the static |
112 To retrieve the name of the local host, use the static |
113 QHostInfo::localHostName() function. |
113 QHostInfo::localHostName() function. |
114 |
114 |
|
115 \note Since Qt 4.6.1 QHostInfo is using multiple threads for DNS lookup |
|
116 instead of one dedicated DNS thread. This improves performance, |
|
117 but also changes the order of signal emissions when using lookupHost() |
|
118 compared to previous versions of Qt. |
|
119 \note Since Qt 4.6.3 QHostInfo is using a small internal 60 second DNS cache |
|
120 for performance improvements. |
|
121 |
115 \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492} |
122 \sa QAbstractSocket, {http://www.rfc-editor.org/rfc/rfc3492.txt}{RFC 3492} |
116 */ |
123 */ |
117 |
124 |
118 static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1); |
125 static QBasicAtomicInt theIdCounter = Q_BASIC_ATOMIC_INITIALIZER(1); |
119 |
126 |
179 QScopedPointer<QHostInfoResult> result(new QHostInfoResult); |
186 QScopedPointer<QHostInfoResult> result(new QHostInfoResult); |
180 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), |
187 QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)), |
181 receiver, member, Qt::QueuedConnection); |
188 receiver, member, Qt::QueuedConnection); |
182 result.data()->emitResultsReady(hostInfo); |
189 result.data()->emitResultsReady(hostInfo); |
183 #else |
190 #else |
184 QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); |
191 QHostInfoLookupManager *manager = theHostInfoLookupManager(); |
185 QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); |
192 if (manager) { |
186 theHostInfoLookupManager()->scheduleLookup(runnable); |
193 // the application is still alive |
|
194 if (manager->cache.isEnabled()) { |
|
195 // check cache first |
|
196 bool valid = false; |
|
197 QHostInfo info = manager->cache.get(name, &valid); |
|
198 if (valid) { |
|
199 info.setLookupId(id); |
|
200 QHostInfoResult result; |
|
201 QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); |
|
202 result.emitResultsReady(info); |
|
203 return id; |
|
204 } |
|
205 } |
|
206 // cache is not enabled or it was not in the cache, do normal lookup |
|
207 QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id); |
|
208 QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection); |
|
209 manager->scheduleLookup(runnable); |
|
210 } |
187 #endif |
211 #endif |
188 |
212 |
189 return id; |
213 return id; |
190 } |
214 } |
191 |
215 |
416 if (manager->wasAborted(id)) { |
440 if (manager->wasAborted(id)) { |
417 manager->lookupFinished(this); |
441 manager->lookupFinished(this); |
418 return; |
442 return; |
419 } |
443 } |
420 |
444 |
421 // check cache |
445 QHostInfo hostInfo; |
422 // FIXME |
446 |
423 |
447 // QHostInfo::lookupHost already checks the cache. However we need to check |
424 // if not in cache: OS lookup |
448 // it here too because it might have been cache saved by another QHostInfoRunnable |
425 QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp); |
449 // in the meanwhile while this QHostInfoRunnable was scheduled but not running |
426 |
450 if (manager->cache.isEnabled()) { |
427 // save to cache |
451 // check the cache first |
428 // FIXME |
452 bool valid = false; |
|
453 hostInfo = manager->cache.get(toBeLookedUp, &valid); |
|
454 if (!valid) { |
|
455 // not in cache, we need to do the lookup and store the result in the cache |
|
456 hostInfo = QHostInfoAgent::fromName(toBeLookedUp); |
|
457 manager->cache.put(toBeLookedUp, hostInfo); |
|
458 } |
|
459 } else { |
|
460 // cache is not enabled, just do the lookup and continue |
|
461 hostInfo = QHostInfoAgent::fromName(toBeLookedUp); |
|
462 } |
429 |
463 |
430 // check aborted again |
464 // check aborted again |
431 if (manager->wasAborted(id)) { |
465 if (manager->wasAborted(id)) { |
432 manager->lookupFinished(this); |
466 manager->lookupFinished(this); |
433 return; |
467 return; |
443 } |
477 } |
444 |
478 |
445 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false) |
479 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false) |
446 { |
480 { |
447 moveToThread(QCoreApplicationPrivate::mainThread()); |
481 moveToThread(QCoreApplicationPrivate::mainThread()); |
|
482 connect(QCoreApplication::instance(), SIGNAL(destroyed()), SLOT(waitForThreadPoolDone()), Qt::DirectConnection); |
448 threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel |
483 threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel |
449 } |
484 } |
450 |
485 |
451 QHostInfoLookupManager::~QHostInfoLookupManager() |
486 QHostInfoLookupManager::~QHostInfoLookupManager() |
452 { |
487 { |
453 wasDeleted = true; |
488 wasDeleted = true; |
|
489 |
|
490 // don't qDeleteAll currentLookups, the QThreadPool has ownership |
|
491 qDeleteAll(postponedLookups); |
|
492 qDeleteAll(scheduledLookups); |
|
493 qDeleteAll(finishedLookups); |
454 } |
494 } |
455 |
495 |
456 void QHostInfoLookupManager::work() |
496 void QHostInfoLookupManager::work() |
457 { |
497 { |
458 if (wasDeleted) |
498 if (wasDeleted) |
568 currentLookups.removeOne(r); |
608 currentLookups.removeOne(r); |
569 finishedLookups.append(r); |
609 finishedLookups.append(r); |
570 work(); |
610 work(); |
571 } |
611 } |
572 |
612 |
|
613 // This function returns immediatly when we had a result in the cache, else it will later emit a signal |
|
614 QHostInfo qt_qhostinfo_lookup(const QString &name, QObject *receiver, const char *member, bool *valid, int *id) |
|
615 { |
|
616 *valid = false; |
|
617 *id = -1; |
|
618 |
|
619 // check cache |
|
620 QHostInfoLookupManager* manager = theHostInfoLookupManager(); |
|
621 if (manager && manager->cache.isEnabled()) { |
|
622 QHostInfo info = manager->cache.get(name, valid); |
|
623 if (*valid) { |
|
624 return info; |
|
625 } |
|
626 } |
|
627 |
|
628 // was not in cache, trigger lookup |
|
629 *id = QHostInfo::lookupHost(name, receiver, member); |
|
630 |
|
631 // return empty response, valid==false |
|
632 return QHostInfo(); |
|
633 } |
|
634 |
|
635 void qt_qhostinfo_clear_cache() |
|
636 { |
|
637 QHostInfoLookupManager* manager = theHostInfoLookupManager(); |
|
638 if (manager) { |
|
639 manager->cache.clear(); |
|
640 } |
|
641 } |
|
642 |
|
643 void Q_AUTOTEST_EXPORT qt_qhostinfo_enable_cache(bool e) |
|
644 { |
|
645 QHostInfoLookupManager* manager = theHostInfoLookupManager(); |
|
646 if (manager) { |
|
647 manager->cache.setEnabled(e); |
|
648 } |
|
649 } |
|
650 |
|
651 // cache for 60 seconds |
|
652 // cache 64 items |
|
653 QHostInfoCache::QHostInfoCache() : max_age(60), enabled(true), cache(64) |
|
654 { |
|
655 #ifdef QT_QHOSTINFO_CACHE_DISABLED_BY_DEFAULT |
|
656 enabled = false; |
|
657 #endif |
|
658 } |
|
659 |
|
660 bool QHostInfoCache::isEnabled() |
|
661 { |
|
662 return enabled; |
|
663 } |
|
664 |
|
665 // this function is currently only used for the auto tests |
|
666 // and not usable by public API |
|
667 void QHostInfoCache::setEnabled(bool e) |
|
668 { |
|
669 enabled = e; |
|
670 } |
|
671 |
|
672 |
|
673 QHostInfo QHostInfoCache::get(const QString &name, bool *valid) |
|
674 { |
|
675 QMutexLocker locker(&this->mutex); |
|
676 |
|
677 *valid = false; |
|
678 if (cache.contains(name)) { |
|
679 QHostInfoCacheElement *element = cache.object(name); |
|
680 if (element->age.elapsed() < max_age*1000) |
|
681 *valid = true; |
|
682 return element->info; |
|
683 |
|
684 // FIXME idea: |
|
685 // if too old but not expired, trigger a new lookup |
|
686 // to freshen our cache |
|
687 } |
|
688 |
|
689 return QHostInfo(); |
|
690 } |
|
691 |
|
692 void QHostInfoCache::put(const QString &name, const QHostInfo &info) |
|
693 { |
|
694 // if the lookup failed, don't cache |
|
695 if (info.error() != QHostInfo::NoError) |
|
696 return; |
|
697 |
|
698 QHostInfoCacheElement* element = new QHostInfoCacheElement(); |
|
699 element->info = info; |
|
700 element->age = QTime(); |
|
701 element->age.start(); |
|
702 |
|
703 QMutexLocker locker(&this->mutex); |
|
704 cache.insert(name, element); // cache will take ownership |
|
705 } |
|
706 |
|
707 void QHostInfoCache::clear() |
|
708 { |
|
709 QMutexLocker locker(&this->mutex); |
|
710 cache.clear(); |
|
711 } |
|
712 |
573 #endif // QT_NO_THREAD |
713 #endif // QT_NO_THREAD |
574 |
714 |
575 QT_END_NAMESPACE |
715 QT_END_NAMESPACE |