summaryrefslogtreecommitdiffstats
path: root/kdecore/network/kresolvermanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/network/kresolvermanager.cpp')
-rw-r--r--kdecore/network/kresolvermanager.cpp822
1 files changed, 0 insertions, 822 deletions
diff --git a/kdecore/network/kresolvermanager.cpp b/kdecore/network/kresolvermanager.cpp
deleted file mode 100644
index b3c7172ae..000000000
--- a/kdecore/network/kresolvermanager.cpp
+++ /dev/null
@@ -1,822 +0,0 @@
-/* -*- 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 <tqapplication.h>
-#include <tqstring.h>
-#include <tqcstring.h>
-#include <tqptrlist.h>
-#include <tqtimer.h>
-#include <tqmutex.h>
-#include <tqthread.h>
-#include <tqwaitcondition.h>
-#include <tqsemaphore.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
- TQWaitCondition cond;
- TQMutex 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
- TQMutexLocker 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
-// exit
-static const int maxThreadWaitTime = 2000; // 2 seconds
-static const int maxThreads = 5;
-
-static pid_t pid; // FIXME -- disable when everything is ok
-
-KResolverThread::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*)TQThread::currentThread());
- KResolverManager::manager()->registerThread(this);
- while (true)
- {
- data = KResolverManager::manager()->requestData(this, ::maxThreadWaitTime);
- //qDebug("KResolverThread(thread %u/%p) got data %p", KResolverManager::pid,
- // (void*)TQThread::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*)TQThread::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::run
-RequestData* 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.
- TQMutexLocker 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::run
-void KResolverManager::releaseData(KResolverThread *, RequestData* data)
-{
- /////
- // This function is called in a worker thread!!
- /////
-
- //qDebug("KResolverManager::releaseData(%u/%p): %p has been released", pid,
-// (void*)TQThread::currentThread(), (void*)data);
-
- if (data->obj)
- {
- data->obj->status = KResolver::PostProcessing;
- }
-
- data->worker->m_finished = true;
- data->worker->th = 0L; // this releases the object
-
- // handle finished requests
- handleFinished();
-}
-
-// this function is called by KResolverManager::releaseData above
-void KResolverManager::handleFinished()
-{
- bool redo = false;
- TQPtrQueue<RequestData> doneRequests;
-
- mutex.lock();
-
- // loop over all items on the currently running list
- // we loop from the last to the first so that we catch requests with "requestors" before
- // we catch the requestor itself.
- RequestData *curr = currentRequests.last();
- while (curr)
- {
- if (curr->worker->th == 0L)
- {
- if (handleFinishedItem(curr))
- {
- doneRequests.enqueue(currentRequests.take());
- if (curr->requestor &&
- curr->requestor->nRequests == 0 &&
- curr->requestor->worker->m_finished)
- // there's a requestor that is now finished
- redo = true;
- }
- }
-
- curr = currentRequests.prev();
- }
-
- //qDebug("KResolverManager::handleFinished(%u): %d requests to notify", pid, doneRequests.count());
- while (RequestData *d = doneRequests.dequeue())
- doNotifying(d);
-
- mutex.unlock();
-
- if (redo)
- {
- //qDebug("KResolverManager::handleFinished(%u): restarting processing to catch requestor",
- // pid);
- handleFinished();
- }
-}
-
-// This function is called by KResolverManager::handleFinished above
-bool KResolverManager::handleFinishedItem(RequestData* curr)
-
-{
- // for all items that aren't currently running, remove from the list
- // this includes all finished or cancelled requests
-
- if (curr->worker->m_finished && curr->nRequests == 0)
- {
- // this one has finished
- if (curr->obj)
- curr->obj->status = KResolver::PostProcessing; // post-processing is run in doNotifying()
-
- if (curr->requestor)
- --curr->requestor->nRequests;
-
- //qDebug("KResolverManager::handleFinishedItem(%u): removing %p since it's done",
- // pid, (void*)curr);
- return true;
- }
- return false;
-}
-
-
-
-void KResolverManager::registerNewWorker(KResolverWorkerFactoryBase *factory)
-{
- workerFactories.append(factory);
-}
-
-KResolverWorkerBase* KResolverManager::findWorker(KResolverPrivate* p)
-{
- /////
- // this function can be called on any user thread
- /////
-
- // this function is called with an unlocked mutex and it's expected to be
- // thread-safe!
- // but the factory list is expected not to be changed asynchronously
-
- // This function is responsible for finding a suitable worker for the given
- // input. That means we have to do a costly operation to create each worker
- // class and call their preprocessing functions. The first one that
- // says they can process (i.e., preprocess() returns true) will get the job.
-
- KResolverWorkerBase *worker;
- for (KResolverWorkerFactoryBase *factory = workerFactories.first(); factory;
- factory = workerFactories.next())
- {
- worker = factory->create();
-
- // set up the data the worker needs to preprocess
- worker->input = &p->input;
-
- if (worker->preprocess())
- {
- // good, this one says it can process
- if (worker->m_finished)
- p->status = KResolver::PostProcessing;
- else
- p->status = KResolver::Queued;
- return worker;
- }
-
- // no, try again
- delete worker;
- }
-
- // found no worker
- return 0L;
-}
-
-void KResolverManager::doNotifying(RequestData *p)
-{
- /////
- // This function may be called on any thread
- // any thread at all: user threads, GUI thread, manager thread or worker thread
- /////
-
- // Notification and finalisation
- //
- // Once a request has finished the normal processing, we call the
- // post processing function.
- //
- // After that is done, we will consolidate all results in the object's
- // KResolverResults and then post an event indicating that the signal
- // be emitted
- //
- // In case we detect that the object is waiting for completion, we do not
- // post the event, for KResolver::wait will take care of emitting the
- // signal.
- //
- // Once we release the mutex on the object, we may no longer reference it
- // for it might have been deleted.
-
- // "User" objects are those that are not created by the manager. Note that
- // objects created by worker threads are considered "user" objects. Objects
- // created by the manager are those created for KResolver::resolveAsync.
- // We should delete them.
-
- if (p->obj)
- {
- // lock the object
- p->obj->mutex.lock();
- KResolver* parent = p->obj->parent; // is 0 for non-"user" objects
- KResolverResults& r = p->obj->results;
-
- if (p->obj->status == KResolver::Canceled)
- {
- p->obj->status = KResolver::Canceled;
- p->obj->errorcode = KResolver::Canceled;
- p->obj->syserror = 0;
- r.setError(KResolver::Canceled, 0);
- }
- else if (p->worker)
- {
- // post processing
- p->worker->postprocess(); // ignore the result
-
- // copy the results from the worker thread to the final
- // object
- r = p->worker->results;
-
- // reset address
- r.setAddress(p->input->node, p->input->service);
-
- //qDebug("KResolverManager::doNotifying(%u/%p): for %p whose status is %d and has %d results",
- //pid, (void*)TQThread::currentThread(), (void*)p, p->obj->status, r.count());
-
- p->obj->errorcode = r.error();
- p->obj->syserror = r.systemError();
- p->obj->status = !r.isEmpty() ?
- KResolver::Success : KResolver::Failed;
- }
- else
- {
- r.empty();
- r.setError(p->obj->errorcode, p->obj->syserror);
- }
-
- // check whether there's someone waiting
- if (!p->obj->waiting && parent)
- // no, so we must post an event requesting that the signal be emitted
- // sorry for the C-style cast, but neither static nor reintepret cast work
- // here; I'd have to do two casts
- TQApplication::postEvent(parent, new TQEvent((TQEvent::Type)(ResolutionCompleted)));
-
- // release the mutex
- p->obj->mutex.unlock();
- }
- else
- {
- // there's no object!
- if (p->worker)
- p->worker->postprocess();
- }
-
- delete p->worker;
-
- // ignore p->requestor and p->nRequests
- // they have been dealt with by the main loop
-
- delete p;
-
- // notify any objects waiting in KResolver::wait
- notifyWaiters.wakeAll();
-}
-
-// enqueue a new request
-// this function is called from KResolver::start and
-// from KResolverWorkerBase::enqueue
-void KResolverManager::enqueue(KResolver *obj, RequestData *requestor)
-{
- RequestData *newrequest = new RequestData;
- newrequest->nRequests = 0;
- newrequest->obj = obj->d;
- newrequest->input = &obj->d->input;
- newrequest->requestor = requestor;
-
- // when processing a new request, find the most
- // suitable worker
- if ((newrequest->worker = findWorker(obj->d)) == 0L)
- {
- // oops, problem
- // cannot find a worker class for this guy
- obj->d->status = KResolver::Failed;
- obj->d->errorcode = KResolver::UnsupportedFamily;
- obj->d->syserror = 0;
-
- doNotifying(newrequest);
- return;
- }
-
- // no, queue it
- // p->status was set in findWorker!
- if (requestor)
- requestor->nRequests++;
-
- if (!newrequest->worker->m_finished)
- dispatch(newrequest);
- else if (newrequest->nRequests > 0)
- {
- mutex.lock();
- currentRequests.append(newrequest);
- mutex.unlock();
- }
- else
- // already done
- doNotifying(newrequest);
-}
-
-// a new request has been created
-// dispatch it
-void KResolverManager::dispatch(RequestData *data)
-{
- // As stated in the beginning of the file, this function
- // is supposed to verify the availability of threads, start
- // any if necessary
-
- TQMutexLocker locker(&mutex);
-
- // add to the queue
- newRequests.append(data);
-
- // check if we need to start a new thread
- //
- // we depend on the variables availableThreads and runningThreads to
- // know if we are supposed to start any threads:
- // - if availableThreads > 0, then there is at least one thread waiting,
- // blocked in KResolverManager::requestData. It can't unblock
- // while we are holding the mutex locked, therefore we are sure that
- // our event will be handled
- // - if availableThreads == 0:
- // - if runningThreads < maxThreads
- // we will start a new thread, which will certainly block in
- // KResolverManager::requestData because we are holding the mutex locked
- // - if runningThreads == maxThreads
- // This situation generally means that we have already maxThreads running
- // and that all of them are processing. We will not start any new threads,
- // but will instead wait for one to finish processing and request new data
- //
- // There's a possible race condition here, which goes unhandled: if one of
- // threads has timed out waiting for new data and is in the process of
- // exiting. In that case, availableThreads == 0 and runningThreads will not
- // have decremented yet. This means that we will not start a new thread
- // that we could have. However, since there are other threads working, our
- // event should be handled soon.
- // It won't be handled if and only if ALL threads are in the process of
- // exiting. That situation is EXTREMELY unlikely and is not handled either.
- //
- if (availableThreads == 0 && runningThreads < maxThreads)
- {
- // yes, a new thread should be started
-
- // find if there's a finished one
- KResolverThread *th = workers.first();
- while (th && th->running())
- th = workers.next();
-
- if (th == 0L)
- // no, create one
- th = new KResolverThread;
- else
- workers.take();
-
- th->start();
- workers.append(th);
- runningThreads++;
- }
-
- feedWorkers.wakeAll();
-
- // clean up idle threads
- workers.first();
- while (workers.current())
- {
- if (!workers.current()->running())
- workers.remove();
- else
- workers.next();
- }
-}
-
-// this function is called by KResolverManager::dequeue
-bool KResolverManager::dequeueNew(KResolver* obj)
-{
- // This function must be called with a locked mutex
- // Deadlock warning:
- // always lock the global mutex first if both mutexes must be locked
-
- KResolverPrivate *d = obj->d;
-
- // check if it's in the new request list
- RequestData *curr = newRequests.first();
- while (curr)
- if (curr->obj == d)
- {
- // yes, this object is still in the list
- // but it has never been processed
- d->status = KResolver::Canceled;
- d->errorcode = KResolver::Canceled;
- d->syserror = 0;
- newRequests.take();
-
- delete curr->worker;
- delete curr;
-
- return true;
- }
- else
- curr = newRequests.next();
-
- // check if it's running
- curr = currentRequests.first();
- while (curr)
- if (curr->obj == d)
- {
- // it's running. We cannot simply take it out of the list.
- // it will be handled when the thread that is working on it finishes
- d->mutex.lock();
-
- d->status = KResolver::Canceled;
- d->errorcode = KResolver::Canceled;
- d->syserror = 0;
-
- // disengage from the running threads
- curr->obj = 0L;
- curr->input = 0L;
- if (curr->worker)
- curr->worker->input = 0L;
-
- d->mutex.unlock();
- }
- else
- curr = currentRequests.next();
-
- return false;
-}
-
-// this function is called by KResolver::cancel
-// it's expected to be thread-safe
-void KResolverManager::dequeue(KResolver *obj)
-{
- TQMutexLocker locker(&mutex);
- dequeueNew(obj);
-}