src/network/kernel/qhostinfo.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
2:56cd8111b7f7 3:41300fa6a67c
    58 #  include <unistd.h>
    58 #  include <unistd.h>
    59 #endif
    59 #endif
    60 
    60 
    61 QT_BEGIN_NAMESPACE
    61 QT_BEGIN_NAMESPACE
    62 
    62 
    63 Q_GLOBAL_STATIC(QHostInfoAgent, theAgent)
    63 #ifndef QT_NO_THREAD
    64 void QHostInfoAgent::staticCleanup()
    64 Q_GLOBAL_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
    65 {
    65 #endif
    66     theAgent()->cleanup();
       
    67 }
       
    68 
    66 
    69 //#define QHOSTINFO_DEBUG
    67 //#define QHOSTINFO_DEBUG
    70 
    68 
    71 /*!
    69 /*!
    72     \class QHostInfo
    70     \class QHostInfo
   140     perform a \e reverse lookup). On success, the resulting QHostInfo will
   138     perform a \e reverse lookup). On success, the resulting QHostInfo will
   141     contain both the resolved domain name and IP addresses for the host
   139     contain both the resolved domain name and IP addresses for the host
   142     name. Example:
   140     name. Example:
   143 
   141 
   144     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
   142     \snippet doc/src/snippets/code/src_network_kernel_qhostinfo.cpp 4
       
   143 
       
   144     \note There is no guarantee on the order the signals will be emitted
       
   145     if you start multiple requests with lookupHost().
   145 
   146 
   146     \sa abortHostLookup(), addresses(), error(), fromName()
   147     \sa abortHostLookup(), addresses(), error(), fromName()
   147 */
   148 */
   148 int QHostInfo::lookupHost(const QString &name, QObject *receiver,
   149 int QHostInfo::lookupHost(const QString &name, QObject *receiver,
   149                           const char *member)
   150                           const char *member)
   157         return -1;
   158         return -1;
   158     }
   159     }
   159 
   160 
   160     qRegisterMetaType<QHostInfo>("QHostInfo");
   161     qRegisterMetaType<QHostInfo>("QHostInfo");
   161 
   162 
   162 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
   163     int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
   163     QWindowsSockInit bust; // makes sure WSAStartup was callled
   164 
       
   165     if (name.isEmpty()) {
       
   166         QHostInfo hostInfo(id);
       
   167         hostInfo.setError(QHostInfo::HostNotFound);
       
   168         hostInfo.setErrorString(QObject::tr("No host name given"));
       
   169         QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
       
   170         QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
       
   171                          receiver, member, Qt::QueuedConnection);
       
   172         result.data()->emitResultsReady(hostInfo);
       
   173         return id;
       
   174     }
       
   175 
       
   176 #ifdef QT_NO_THREAD
       
   177     QHostInfo hostInfo = QHostInfoAgent::fromName(name);
       
   178     hostInfo.setLookupId(id);
       
   179     QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
       
   180     QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
       
   181                      receiver, member, Qt::QueuedConnection);
       
   182     result.data()->emitResultsReady(hostInfo);
       
   183 #else
       
   184     QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id);
       
   185     QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)), receiver, member, Qt::QueuedConnection);
       
   186     theHostInfoLookupManager()->scheduleLookup(runnable);
   164 #endif
   187 #endif
   165 
   188 
   166     QScopedPointer<QHostInfoResult> result(new QHostInfoResult);
   189     return id;
   167     result.data()->autoDelete = false;
   190 }
   168     QObject::connect(result.data(), SIGNAL(resultsReady(QHostInfo)),
   191 
   169                      receiver, member);
   192 /*!
   170     int id = result.data()->lookupId = theIdCounter.fetchAndAddRelaxed(1);
   193     Aborts the host lookup with the ID \a id, as returned by lookupHost().
   171 
   194 
   172     if (name.isEmpty()) {
   195     \sa lookupHost(), lookupId()
   173         QHostInfo info(id);
   196 */
   174         info.setError(QHostInfo::HostNotFound);
   197 void QHostInfo::abortHostLookup(int id)
   175         info.setErrorString(QObject::tr("No host name given"));
   198 {
   176         QMetaObject::invokeMethod(result.data(), "emitResultsReady", Qt::QueuedConnection,
   199 #ifndef QT_NO_THREAD
   177                                   Q_ARG(QHostInfo, info));
   200     theHostInfoLookupManager()->abortLookup(id);
   178         result.take()->autoDelete = true;
       
   179         return id;
       
   180     }
       
   181 
       
   182     QHostInfoAgent *agent = theAgent();
       
   183     agent->addHostName(name, result.take());
       
   184 
       
   185 #if !defined QT_NO_THREAD
       
   186     if (!agent->isRunning())
       
   187         agent->start();
       
   188 #else
   201 #else
   189 //    if (!agent->isRunning())
   202     // we cannot abort if it was non threaded.. the result signal has already been posted
   190 	agent->run();
   203     Q_UNUSED(id);
   191 //    else
       
   192 //	agent->wakeOne();
       
   193 #endif
   204 #endif
   194     return id;
       
   195 }
       
   196 
       
   197 /*!
       
   198     Aborts the host lookup with the ID \a id, as returned by lookupHost().
       
   199 
       
   200     \sa lookupHost(), lookupId()
       
   201 */
       
   202 void QHostInfo::abortHostLookup(int id)
       
   203 {
       
   204     QHostInfoAgent *agent = theAgent();
       
   205     agent->abortLookup(id);
       
   206 }
   205 }
   207 
   206 
   208 /*!
   207 /*!
   209     Looks up the IP address(es) for the given host \a name. The
   208     Looks up the IP address(es) for the given host \a name. The
   210     function blocks during the lookup which means that execution of
   209     function blocks during the lookup which means that execution of
   226 
   225 
   227     return QHostInfoAgent::fromName(name);
   226     return QHostInfoAgent::fromName(name);
   228 }
   227 }
   229 
   228 
   230 /*!
   229 /*!
   231     \internal
       
   232     Pops a query off the queries list, performs a blocking call to
       
   233     QHostInfoAgent::lookupHost(), and emits the resultsReady()
       
   234     signal. This process repeats until the queries list is empty.
       
   235 */
       
   236 void QHostInfoAgent::run()
       
   237 {
       
   238 #ifndef QT_NO_THREAD
       
   239     // Dont' allow thread termination during event delivery, but allow it
       
   240     // during the actual blocking host lookup stage.
       
   241     setTerminationEnabled(false);
       
   242     forever
       
   243 #endif
       
   244     {
       
   245         QHostInfoQuery *query;
       
   246         {
       
   247 #ifndef QT_NO_THREAD
       
   248             // the queries list is shared between threads. lock all
       
   249             // access to it.
       
   250             QMutexLocker locker(&mutex);
       
   251             if (!quit && queries.isEmpty())
       
   252                 cond.wait(&mutex);
       
   253             if (quit) {
       
   254                 // Reset the quit variable in case QCoreApplication is
       
   255                 // destroyed and recreated.
       
   256                 quit = false;
       
   257                 break;
       
   258             }
       
   259 	    if (queries.isEmpty())
       
   260 		continue;
       
   261 #else
       
   262 	    if (queries.isEmpty())
       
   263 		return;
       
   264 #endif
       
   265             query = queries.takeFirst();
       
   266             pendingQueryId = query->object->lookupId;
       
   267         }
       
   268 
       
   269 #if defined(QHOSTINFO_DEBUG)
       
   270         qDebug("QHostInfoAgent::run(%p): looking up \"%s\"", this,
       
   271                query->hostName.toLatin1().constData());
       
   272 #endif
       
   273 
       
   274 #ifndef QT_NO_THREAD
       
   275         // Start query - allow termination at this point, but not outside. We
       
   276         // don't want to all termination during event delivery, but we don't
       
   277         // want the lookup to prevent the app from quitting (the agent
       
   278         // destructor terminates the thread).
       
   279         setTerminationEnabled(true);
       
   280 #endif
       
   281         QHostInfo info = fromName(query->hostName);
       
   282 #ifndef QT_NO_THREAD
       
   283         setTerminationEnabled(false);
       
   284 #endif
       
   285 
       
   286         int id = query->object->lookupId;
       
   287         info.setLookupId(id);
       
   288         if (pendingQueryId == id)
       
   289             query->object->emitResultsReady(info);
       
   290         delete query;
       
   291     }
       
   292 }
       
   293 
       
   294 /*!
       
   295     \enum QHostInfo::HostInfoError
   230     \enum QHostInfo::HostInfoError
   296 
   231 
   297     This enum describes the various errors that can occur when trying
   232     This enum describes the various errors that can occur when trying
   298     to resolve a host name.
   233     to resolve a host name.
   299 
   234 
   465     Windows networks.
   400     Windows networks.
   466 
   401 
   467     \sa hostName()
   402     \sa hostName()
   468 */
   403 */
   469 
   404 
       
   405 #ifndef QT_NO_THREAD
       
   406 QHostInfoRunnable::QHostInfoRunnable(QString hn, int i) : toBeLookedUp(hn), id(i)
       
   407 {
       
   408     setAutoDelete(true);
       
   409 }
       
   410 
       
   411 // the QHostInfoLookupManager will at some point call this via a QThreadPool
       
   412 void QHostInfoRunnable::run()
       
   413 {
       
   414     QHostInfoLookupManager *manager = theHostInfoLookupManager();
       
   415     // check aborted
       
   416     if (manager->wasAborted(id)) {
       
   417         manager->lookupFinished(this);
       
   418         return;
       
   419     }
       
   420 
       
   421     // check cache
       
   422     // FIXME
       
   423 
       
   424     // if not in cache: OS lookup
       
   425     QHostInfo hostInfo = QHostInfoAgent::fromName(toBeLookedUp);
       
   426 
       
   427     // save to cache
       
   428     // FIXME
       
   429 
       
   430     // check aborted again
       
   431     if (manager->wasAborted(id)) {
       
   432         manager->lookupFinished(this);
       
   433         return;
       
   434     }
       
   435 
       
   436     // signal emission
       
   437     hostInfo.setLookupId(id);
       
   438     resultEmitter.emitResultsReady(hostInfo);
       
   439 
       
   440     manager->lookupFinished(this);
       
   441 
       
   442     // thread goes back to QThreadPool
       
   443 }
       
   444 
       
   445 QHostInfoLookupManager::QHostInfoLookupManager() : mutex(QMutex::Recursive), wasDeleted(false)
       
   446 {
       
   447     moveToThread(QCoreApplicationPrivate::mainThread());
       
   448     threadPool.setMaxThreadCount(5); // do 5 DNS lookups in parallel
       
   449 }
       
   450 
       
   451 QHostInfoLookupManager::~QHostInfoLookupManager()
       
   452 {
       
   453     wasDeleted = true;
       
   454 }
       
   455 
       
   456 void QHostInfoLookupManager::work()
       
   457 {
       
   458     if (wasDeleted)
       
   459         return;
       
   460 
       
   461     // goals of this function:
       
   462     //  - launch new lookups via the thread pool
       
   463     //  - make sure only one lookup per host/IP is in progress
       
   464 
       
   465     QMutexLocker locker(&mutex);
       
   466 
       
   467     if (!finishedLookups.isEmpty()) {
       
   468         // remove ID from aborted if it is in there
       
   469         for (int i = 0; i < finishedLookups.length(); i++) {
       
   470            abortedLookups.removeAll(finishedLookups.at(i)->id);
       
   471         }
       
   472 
       
   473         finishedLookups.clear();
       
   474     }
       
   475 
       
   476     if (!postponedLookups.isEmpty()) {
       
   477         // try to start the postponed ones
       
   478 
       
   479         QMutableListIterator<QHostInfoRunnable*> iterator(postponedLookups);
       
   480         while (iterator.hasNext()) {
       
   481             QHostInfoRunnable* postponed = iterator.next();
       
   482 
       
   483             // check if none of the postponed hostnames is currently running
       
   484             bool alreadyRunning = false;
       
   485             for (int i = 0; i < currentLookups.length(); i++) {
       
   486                 if (currentLookups.at(i)->toBeLookedUp == postponed->toBeLookedUp) {
       
   487                     alreadyRunning = true;
       
   488                     break;
       
   489                 }
       
   490             }
       
   491             if (!alreadyRunning) {
       
   492                 iterator.remove();
       
   493                 scheduledLookups.prepend(postponed); // prepend! we want to finish it ASAP
       
   494             }
       
   495         }
       
   496     }
       
   497 
       
   498     if (!scheduledLookups.isEmpty()) {
       
   499         // try to start the new ones
       
   500         QMutableListIterator<QHostInfoRunnable*> iterator(scheduledLookups);
       
   501         while (iterator.hasNext()) {
       
   502             QHostInfoRunnable *scheduled = iterator.next();
       
   503 
       
   504             // check if a lookup for this host is already running, then postpone
       
   505             for (int i = 0; i < currentLookups.size(); i++) {
       
   506                 if (currentLookups.at(i)->toBeLookedUp == scheduled->toBeLookedUp) {
       
   507                     iterator.remove();
       
   508                     postponedLookups.append(scheduled);
       
   509                     scheduled = 0;
       
   510                     break;
       
   511                 }
       
   512             }
       
   513 
       
   514             if (scheduled && threadPool.tryStart(scheduled)) {
       
   515                 // runnable now running in new thread, track this in currentLookups
       
   516                 iterator.remove();
       
   517                 currentLookups.append(scheduled);
       
   518             } else if (scheduled) {
       
   519                 // wanted to start, but could not because thread pool is busy
       
   520                 break;
       
   521             } else {
       
   522                 // was postponed, continue iterating
       
   523                 continue;
       
   524             }
       
   525         };
       
   526     }
       
   527 }
       
   528 
       
   529 // called by QHostInfo
       
   530 void QHostInfoLookupManager::scheduleLookup(QHostInfoRunnable *r)
       
   531 {
       
   532     if (wasDeleted)
       
   533         return;
       
   534 
       
   535     QMutexLocker locker(&this->mutex);
       
   536     scheduledLookups.enqueue(r);
       
   537     work();
       
   538 }
       
   539 
       
   540 // called by QHostInfo
       
   541 void QHostInfoLookupManager::abortLookup(int id)
       
   542 {
       
   543     if (wasDeleted)
       
   544         return;
       
   545 
       
   546     QMutexLocker locker(&this->mutex);
       
   547     if (!abortedLookups.contains(id))
       
   548         abortedLookups.append(id);
       
   549 }
       
   550 
       
   551 // called from QHostInfoRunnable
       
   552 bool QHostInfoLookupManager::wasAborted(int id)
       
   553 {
       
   554     if (wasDeleted)
       
   555         return true;
       
   556 
       
   557     QMutexLocker locker(&this->mutex);
       
   558     return abortedLookups.contains(id);
       
   559 }
       
   560 
       
   561 // called from QHostInfoRunnable
       
   562 void QHostInfoLookupManager::lookupFinished(QHostInfoRunnable *r)
       
   563 {
       
   564     if (wasDeleted)
       
   565         return;
       
   566 
       
   567     QMutexLocker locker(&this->mutex);
       
   568     currentLookups.removeOne(r);
       
   569     finishedLookups.append(r);
       
   570     work();
       
   571 }
       
   572 
       
   573 #endif // QT_NO_THREAD
       
   574 
   470 QT_END_NAMESPACE
   575 QT_END_NAMESPACE