kresolvermanager.cpp

来自「konqueror3 embedded版本, KDE环境下的当家浏览器的嵌入式版」· C++ 代码 · 共 823 行 · 第 1/2 页

CPP
823
字号
/*  -*- C++ -*- *  Copyright (C) 2003-2005 Thiago Macieira <thiago.macieira@kdemail.net> * * *  Permission is hereby granted, free of charge, to any person obtaining *  a copy of this software and associated documentation files (the *  "Software"), to deal in the Software without restriction, including *  without limitation the rights to use, copy, modify, merge, publish, *  distribute, sublicense, and/or sell copies of the Software, and to *  permit persons to whom the Software is furnished to do so, subject to *  the following conditions: * *  The above copyright notice and this permission notice shall be included  *  in all copies or substantial portions of the Software. * *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */#include "config.h"#include <sys/types.h>#include <netinet/in.h>#include <limits.h>#include <unistd.h>		// only needed for pid_t#ifdef HAVE_RES_INIT# include <sys/stat.h>extern "C" {#   include <arpa/nameser.h>}# include <time.h># include <resolv.h>#endif#include <qapplication.h>#include <qstring.h>#include <qcstring.h>#include <qptrlist.h>#include <qtimer.h>#include <qmutex.h>#include <qthread.h>#include <qwaitcondition.h>#include <qsemaphore.h>#include <kde_file.h>#include <kdebug.h>#include "kresolver.h"#include "kresolver_p.h"#include "kresolverworkerbase.h"namespace KNetwork{  namespace Internal  {    void initSrvWorker();    void initStandardWorkers();  }}using namespace KNetwork;using namespace KNetwork::Internal;/* * Explanation on how the resolver system works   When KResolver::start is called, it calls KResolverManager::enqueue to add   an entry to the queue. KResolverManager::enqueue will verify the availability   of a worker thread: if one is available, it will dispatch the request to it.   If no threads are available, it will then decide whether to launch a thread   or to queue for the future.   (This process is achieved by always queueing the new request, starting a   new thread if necessary and then notifying of the availability of data   to all worker threads). * Worker thread   A new thread, when started, will enter its event loop   immediately. That is, it'll first try to acquire new data to   process, which means it will lock and unlock the manager mutex in   the process.   If it finds no new data, it'll wait on the feedWorkers condition   for a certain maximum time. If that time expires and there's still   no data, the thread will exit, in order to save system resources.   If it finds data, however, it'll set up and call the worker class   that has been selected by the manager. Once that worker is done,   the thread releases the data through KResolverManager::releaseData. * Data requesting/releasing   A worker thread always calls upon functions on the resolver manager   in order to acquire and release data.   When data is being requested, the KResolverManager::requestData   function will look the currentRequests list and return the first   Queued request it finds, while marking it to be InProgress.   When the worker class has returned, the worker thread will release   that data through the KResolverManager::releaseData function. If the   worker class has requested no further data (nRequests == 0), the   request's status is marked to be Done. It'll then look at the   requestor for that data: if it was requested by another worker,   it'll decrement the requests count for that one and add the results   to a list. And, finally, if the requests count for the requestor   becomes 0, it'll repeat this process for the requestor as well   (change status to Done, check for a requestor). */namespace{/* * This class is used to control the access to the * system's resolver API. * * It is necessary to periodically poll /etc/resolv.conf and reload * it if any changes are noticed. This class does exactly that. * * However, there's also the problem of reloading the structure while * some threads are in progress. Therefore, we keep a usage reference count. */class ResInitUsage{public:#ifdef HAVE_RES_INIT  time_t mTime;  int useCount;# ifndef RES_INIT_THREADSAFE  QWaitCondition cond;  QMutex mutex;# endif  bool shouldResInit()  {    // check if /etc/resolv.conf has changed     KDE_struct_stat st;    if (KDE_stat("/etc/resolv.conf", &st) != 0)      return false;        if (mTime != st.st_mtime)      {	kdDebug(179) << "shouldResInit: /etc/resolv.conf updated" << endl;	return true;      }    return false;  }  void callResInit()  {    if (mTime != 0)      {	// don't call it the first time	// let it be initialised naturally	kdDebug(179) << "callResInit: calling res_init()" << endl;	res_init();      }        KDE_struct_stat st;    if (KDE_stat("/etc/resolv.conf", &st) == 0)      mTime = st.st_mtime;  }  ResInitUsage()    : mTime(0), useCount(0)  { }  /*   * Marks the end of usage to the resolver tools   */  void release()  {# ifndef RES_INIT_THREADSAFE    QMutexLocker locker(&mutex);    if (--useCount == 0)      {	if (shouldResInit())	  callResInit();	// we've reached 0, wake up anyone that's waiting to call res_init	cond.wakeAll();      }# else    // do nothing# endif  }  /*   * Marks the beginning of usage of the resolver API   */  void acquire()  {# ifndef RES_INIT_THREADSAFE    mutex.lock();    if (shouldResInit())      {	if (useCount)	  {	    // other threads are already using the API, so wait till	    // it's all clear	    // the thread that emits this condition will also call res_init	    //qDebug("ResInitUsage: waiting for libresolv to be clear");	    cond.wait(&mutex);	  }	else	  // we're clear	  callResInit();      }    useCount++;    mutex.unlock();# else    if (shouldResInit())      callResInit();# endif  }#else  ResInitUsage()  { }  bool shouldResInit()  { return false; }  void acquire()  { }  void release()  { }#endif} resInit;} // anonymous namespace/* * parameters */// a thread will try maxThreadRetries to get data, waiting at most// maxThreadWaitTime milliseconds between each attempt. After that, it'll// exitstatic const int maxThreadWaitTime = 2000; // 2 secondsstatic const int maxThreads = 5;static pid_t pid;		// FIXME -- disable when everything is okKResolverThread::KResolverThread()  : data(0L){}// remember! This function runs in a separate thread!void KResolverThread::run(){  // initialisation  // enter the loop already  //qDebug("KResolverThread(thread %u/%p): started", pid, (void*)QThread::currentThread());  KResolverManager::manager()->registerThread(this);  while (true)    {      data = KResolverManager::manager()->requestData(this, ::maxThreadWaitTime);      //qDebug("KResolverThread(thread %u/%p) got data %p", KResolverManager::pid,       //       (void*)QThread::currentThread(), (void*)data);      if (data)	{	  // yes, we got data	  // process it!      	  // 1) set up	  ;      	  // 2) run it	  data->worker->run();	  	  // 3) release data	  KResolverManager::manager()->releaseData(this, data);	  	  // now go back to the loop	}      else	break;    }  KResolverManager::manager()->unregisterThread(this);  //qDebug("KResolverThread(thread %u/%p): exiting", pid, (void*)QThread::currentThread());}bool KResolverThread::checkResolver(){  return resInit.shouldResInit();}void KResolverThread::acquireResolver(){#if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)  getXXbyYYmutex.lock();#endif  resInit.acquire();}void KResolverThread::releaseResolver(){#if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)  getXXbyYYmutex.unlock();#endif  resInit.release();}static KResolverManager *globalManager;KResolverManager* KResolverManager::manager(){  if (globalManager == 0L)    new KResolverManager();  return globalManager;}KResolverManager::KResolverManager()  : runningThreads(0), availableThreads(0){  globalManager = this;  workers.setAutoDelete(true);  currentRequests.setAutoDelete(true);  initSrvWorker();  initStandardWorkers();  pid = getpid();}KResolverManager::~KResolverManager(){  // this should never be called  // kill off running threads  for (workers.first(); workers.current(); workers.next())    workers.current()->terminate();}void KResolverManager::registerThread(KResolverThread* ){}void KResolverManager::unregisterThread(KResolverThread*){  runningThreads--;}// this function is called by KResolverThread::runRequestData* KResolverManager::requestData(KResolverThread *th, int maxWaitTime){  /////  // This function is called in a worker thread!!  /////  // lock the mutex, so that the manager thread or other threads won't  // interfere.  QMutexLocker locker(&mutex);  RequestData *data = findData(th);  if (data)    // it found something, that's good    return data;  // nope, nothing found; sleep for a while  availableThreads++;  feedWorkers.wait(&mutex, maxWaitTime);  availableThreads--;  data = findData(th);  return data;}RequestData* KResolverManager::findData(KResolverThread* th){  /////  // This function is called by @ref requestData above and must  // always be called with a locked mutex  /////  // now find data to be processed  for (RequestData *curr = newRequests.first(); curr; curr = newRequests.next())    if (!curr->worker->m_finished)      {	// found one	if (curr->obj)	  curr->obj->status = KResolver::InProgress;	curr->worker->th = th;	// move it to the currentRequests list	currentRequests.append(newRequests.take());	return curr;      }  // found nothing!  return 0L;}// this function is called by KResolverThread::runvoid KResolverManager::releaseData(KResolverThread *, RequestData* data)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?