File indexing completed on 2024-04-14 14:19:49

0001 /*  -*- C++ -*-
0002  *  Copyright (C) 2003-2005 Thiago Macieira <thiago@kde.org>
0003  *
0004  *
0005  *  Permission is hereby granted, free of charge, to any person obtaining
0006  *  a copy of this software and associated documentation files (the
0007  *  "Software"), to deal in the Software without restriction, including
0008  *  without limitation the rights to use, copy, modify, merge, publish,
0009  *  distribute, sublicense, and/or sell copies of the Software, and to
0010  *  permit persons to whom the Software is furnished to do so, subject to
0011  *  the following conditions:
0012  *
0013  *  The above copyright notice and this permission notice shall be included
0014  *  in all copies or substantial portions of the Software.
0015  *
0016  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0017  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0018  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0019  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0020  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0021  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0022  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0023  */
0024 
0025 #include <config-network.h>
0026 
0027 #include <sys/types.h>
0028 #include <netinet/in.h>
0029 #include <limits.h>
0030 #include <unistd.h>     // only needed for pid_t
0031 
0032 #if HAVE_RES_INIT
0033 # include <sys/stat.h>
0034 extern "C" {
0035 #   include <arpa/nameser.h>
0036 }
0037 # include <time.h>
0038 # include <resolv.h>
0039 #endif
0040 
0041 #include <QByteArray>
0042 #include <QCoreApplication>
0043 #include <QList>
0044 #include <QMutableListIterator>
0045 #include <QMutex>
0046 #include <QQueue>
0047 #include <QSemaphore>
0048 
0049 #include <QThread>
0050 #include <QTimer>
0051 #include <QWaitCondition>
0052 
0053 #include <kde_file.h>
0054 #include <kdebug.h>
0055 #include "k3resolver.h"
0056 #include "k3resolver_p.h"
0057 #include "k3resolverworkerbase.h"
0058 #include "k3resolverstandardworkers_p.h"
0059 
0060 using namespace KNetwork;
0061 using namespace KNetwork::Internal;
0062 
0063 /*
0064  * Explanation on how the resolver system works
0065 
0066    When KResolver::start is called, it calls KResolverManager::enqueue to add
0067    an entry to the queue. KResolverManager::enqueue will verify the availability
0068    of a worker thread: if one is available, it will dispatch the request to it.
0069    If no threads are available, it will then decide whether to launch a thread
0070    or to queue for the future.
0071 
0072    (This process is achieved by always queuing the new request, starting a
0073    new thread if necessary and then notifying of the availability of data
0074    to all worker threads).
0075 
0076  * Worker thread
0077    A new thread, when started, will enter its event loop
0078    immediately. That is, it'll first try to acquire new data to
0079    process, which means it will lock and unlock the manager mutex in
0080    the process.
0081 
0082    If it finds no new data, it'll wait on the feedWorkers condition
0083    for a certain maximum time. If that time expires and there's still
0084    no data, the thread will exit, in order to save system resources.
0085 
0086    If it finds data, however, it'll set up and call the worker class
0087    that has been selected by the manager. Once that worker is done,
0088    the thread releases the data through KResolverManager::releaseData.
0089 
0090  * Data requesting/releasing
0091    A worker thread always calls upon functions on the resolver manager
0092    in order to acquire and release data.
0093 
0094    When data is being requested, the KResolverManager::requestData
0095    function will look the currentRequests list and return the first
0096    Queued request it finds, while marking it to be InProgress.
0097 
0098    When the worker class has returned, the worker thread will release
0099    that data through the KResolverManager::releaseData function. If the
0100    worker class has requested no further data (nRequests == 0), the
0101    request's status is marked to be Done. It'll then look at the
0102    requestor for that data: if it was requested by another worker,
0103    it'll decrement the requests count for that one and add the results
0104    to a list. And, finally, if the requests count for the requestor
0105    becomes 0, it'll repeat this process for the requestor as well
0106    (change status to Done, check for a requestor).
0107  */
0108 
0109 namespace
0110 {
0111 
0112 /*
0113  * This class is used to control the access to the
0114  * system's resolver API.
0115  *
0116  * It is necessary to periodically poll /etc/resolv.conf and reload
0117  * it if any changes are noticed. This class does exactly that.
0118  *
0119  * However, there's also the problem of reloading the structure while
0120  * some threads are in progress. Therefore, we keep a usage reference count.
0121  */
0122 class ResInitUsage
0123 {
0124 public:
0125 
0126 #if HAVE_RES_INIT
0127     time_t mTime;
0128     int useCount;
0129 
0130 # ifndef RES_INIT_THREADSAFE
0131     QWaitCondition cond;
0132     QMutex mutex;
0133 # endif
0134 
0135     bool shouldResInit()
0136     {
0137         // check if /etc/resolv.conf has changed
0138         KDE_struct_stat st;
0139         if (KDE_stat("/etc/resolv.conf", &st) != 0) {
0140             return false;
0141         }
0142 
0143         if (mTime != st.st_mtime) {
0144             kDebug(179) << "shouldResInit: /etc/resolv.conf updated";
0145             return true;
0146         }
0147         return false;
0148     }
0149 
0150     void callResInit()
0151     {
0152         if (mTime != 0) {
0153             // don't call it the first time
0154             // let it be initialized naturally
0155             kDebug(179) << "callResInit: calling res_init()";
0156             res_init();
0157         }
0158 
0159         KDE_struct_stat st;
0160         if (KDE_stat("/etc/resolv.conf", &st) == 0) {
0161             mTime = st.st_mtime;
0162         }
0163     }
0164 
0165     ResInitUsage()
0166         : mTime(0), useCount(0)
0167     { }
0168 
0169     /*
0170      * Marks the end of usage to the resolver tools
0171      */
0172     void release()
0173     {
0174 # ifndef RES_INIT_THREADSAFE
0175         QMutexLocker locker(&mutex);
0176         if (--useCount == 0) {
0177             if (shouldResInit()) {
0178                 callResInit();
0179             }
0180 
0181             // we've reached 0, wake up anyone that's waiting to call res_init
0182             cond.wakeAll();
0183         }
0184 # else
0185         // do nothing
0186 # endif
0187     }
0188 
0189     /*
0190      * Marks the beginning of usage of the resolver API
0191      */
0192     void acquire()
0193     {
0194 # ifndef RES_INIT_THREADSAFE
0195         mutex.lock();
0196 
0197         if (shouldResInit()) {
0198             if (useCount) {
0199                 // other threads are already using the API, so wait till
0200                 // it's all clear
0201                 // the thread that emits this condition will also call res_init
0202                 //qDebug("ResInitUsage: waiting for libresolv to be clear");
0203                 cond.wait(&mutex);
0204             } else
0205                 // we're clear
0206             {
0207                 callResInit();
0208             }
0209         }
0210         useCount++;
0211         mutex.unlock();
0212 
0213 # else
0214         if (shouldResInit()) {
0215             callResInit();
0216         }
0217 
0218 # endif
0219     }
0220 
0221 #else
0222     ResInitUsage()
0223     { }
0224 
0225     bool shouldResInit()
0226     {
0227         return false;
0228     }
0229 
0230     void acquire()
0231     { }
0232 
0233     void release()
0234     { }
0235 #endif
0236 
0237 } resInit;
0238 
0239 } // anonymous namespace
0240 
0241 /*
0242  * parameters
0243  */
0244 // a thread will try maxThreadRetries to get data, waiting at most
0245 // maxThreadWaitTime milliseconds between each attempt. After that, it'll
0246 // exit
0247 static const int maxThreadWaitTime = 2000; // 2 seconds
0248 static const int maxThreads = 5;
0249 
0250 static pid_t pid;       // FIXME -- disable when everything is ok
0251 
0252 KResolverThread::KResolverThread()
0253     : data(nullptr)
0254 {
0255 }
0256 
0257 // remember! This function runs in a separate thread!
0258 void KResolverThread::run()
0259 {
0260     // initialization
0261     // enter the loop already
0262 
0263     //qDebug("KResolverThread(thread %u/%p): started", pid, (void*)QThread::currentThread());
0264     KResolverManager::manager()->registerThread(this);
0265     while (true) {
0266         data = KResolverManager::manager()->requestData(this, ::maxThreadWaitTime);
0267         //qDebug("KResolverThread(thread %u/%p) got data %p", KResolverManager::pid,
0268         //       (void*)QThread::currentThread(), (void*)data);
0269         if (data) {
0270             // yes, we got data
0271             // process it!
0272 
0273             // 1) set up
0274             ;
0275 
0276             // 2) run it
0277             data->worker->run();
0278 
0279             // 3) release data
0280             KResolverManager::manager()->releaseData(this, data);
0281 
0282             // now go back to the loop
0283         } else {
0284             break;
0285         }
0286     }
0287 
0288     KResolverManager::manager()->unregisterThread(this);
0289     //qDebug("KResolverThread(thread %u/%p): exiting", pid, (void*)QThread::currentThread());
0290 }
0291 
0292 bool KResolverThread::checkResolver()
0293 {
0294     return resInit.shouldResInit();
0295 }
0296 
0297 void KResolverThread::acquireResolver()
0298 {
0299 #if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)
0300     getXXbyYYmutex.lock();
0301 #endif
0302 
0303     resInit.acquire();
0304 }
0305 
0306 void KResolverThread::releaseResolver()
0307 {
0308 #if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD)
0309     getXXbyYYmutex.unlock();
0310 #endif
0311 
0312     resInit.release();
0313 }
0314 
0315 static KResolverManager *globalManager;
0316 
0317 KResolverManager *KResolverManager::manager()
0318 {
0319     if (globalManager == nullptr) {
0320         new KResolverManager();
0321     }
0322     return globalManager;
0323 }
0324 
0325 KResolverManager::KResolverManager()
0326     : runningThreads(0), availableThreads(0)
0327 {
0328     globalManager = this;
0329     initStandardWorkers();
0330 
0331     pid = getpid();
0332 }
0333 
0334 KResolverManager::~KResolverManager()
0335 {
0336     // this should never be called
0337 
0338     // kill off running threads
0339     foreach (KResolverThread *worker, workers) {
0340         worker->terminate();
0341     }
0342 }
0343 
0344 void KResolverManager::registerThread(KResolverThread *)
0345 {
0346 }
0347 
0348 void KResolverManager::unregisterThread(KResolverThread *)
0349 {
0350     runningThreads--;
0351 }
0352 
0353 // this function is called by KResolverThread::run
0354 RequestData *KResolverManager::requestData(KResolverThread *th, int maxWaitTime)
0355 {
0356     /////
0357     // This function is called in a worker thread!!
0358     /////
0359 
0360     // lock the mutex, so that the manager thread or other threads won't
0361     // interfere.
0362     QMutexLocker locker(&mutex);
0363     RequestData *data = findData(th);
0364 
0365     if (data)
0366         // it found something, that's good
0367     {
0368         return data;
0369     }
0370 
0371     // nope, nothing found; sleep for a while
0372     availableThreads++;
0373     feedWorkers.wait(&mutex, maxWaitTime);
0374     availableThreads--;
0375 
0376     data = findData(th);
0377     return data;
0378 }
0379 
0380 RequestData *KResolverManager::findData(KResolverThread *th)
0381 {
0382     /////
0383     // This function is called by requestData() above and must
0384     // always be called with a locked mutex
0385     /////
0386 
0387     // now find data to be processed
0388     QMutableListIterator<RequestData *> it(newRequests);
0389     while (it.hasNext()) {
0390         RequestData *curr = it.next();
0391         if (!curr->worker->m_finished) {
0392             // found one
0393             if (curr->obj) {
0394                 curr->obj->status = KResolver::InProgress;
0395             }
0396             curr->worker->th = th;
0397 
0398             // move it to the currentRequests list
0399             it.remove();
0400             currentRequests.append(curr);
0401 
0402             return curr;
0403         }
0404     }
0405 
0406     // found nothing!
0407     return nullptr;
0408 }
0409 
0410 // this function is called by KResolverThread::run
0411 void KResolverManager::releaseData(KResolverThread *, RequestData *data)
0412 {
0413     /////
0414     // This function is called in a worker thread!!
0415     /////
0416 
0417     //qDebug("KResolverManager::releaseData(%u/%p): %p has been released", pid,
0418 //   (void*)QThread::currentThread(), (void*)data);
0419 
0420     if (data->obj) {
0421         data->obj->status = KResolver::PostProcessing;
0422     }
0423 
0424     data->worker->m_finished = true;
0425     data->worker->th = nullptr;    // this releases the object
0426 
0427     // handle finished requests
0428     handleFinished();
0429 }
0430 
0431 // this function is called by KResolverManager::releaseData above
0432 void KResolverManager::handleFinished()
0433 {
0434     bool redo = false;
0435     QQueue<RequestData *> doneRequests;
0436 
0437     mutex.lock();
0438     if (currentRequests.isEmpty()) {
0439         mutex.unlock();
0440         return;
0441     }
0442 
0443     // loop over all items on the currently running list
0444     // we loop from the last to the first so that we catch requests
0445     // with "requestors" before we catch the requestor itself.
0446     QMutableListIterator<RequestData *> it(currentRequests);
0447     it.toBack();
0448     while (it.hasPrevious()) {
0449         RequestData *curr = it.previous();
0450         if (curr->worker->th == nullptr) {
0451             if (handleFinishedItem(curr)) {
0452                 it.remove();
0453                 doneRequests.enqueue(curr);
0454 
0455                 if (curr->requestor &&
0456                         curr->requestor->nRequests == 0 &&
0457                         curr->requestor->worker->m_finished)
0458                     // there's a requestor that is now finished
0459                 {
0460                     redo = true;
0461                 }
0462             }
0463         }
0464     }
0465 
0466     //qDebug("KResolverManager::handleFinished(%u): %d requests to notify", pid, doneRequests.count());
0467     while (!doneRequests.isEmpty()) {
0468         doNotifying(doneRequests.dequeue());
0469     }
0470 
0471     mutex.unlock();
0472 
0473     if (redo) {
0474         //qDebug("KResolverManager::handleFinished(%u): restarting processing to catch requestor",
0475         //     pid);
0476         handleFinished();
0477     }
0478 }
0479 
0480 // This function is called by KResolverManager::handleFinished above
0481 bool KResolverManager::handleFinishedItem(RequestData *curr)
0482 
0483 {
0484     // for all items that aren't currently running, remove from the list
0485     // this includes all finished or canceled requests
0486 
0487     if (curr->worker->m_finished && curr->nRequests == 0) {
0488         // this one has finished
0489         if (curr->obj) {
0490             curr->obj->status = KResolver::PostProcessing;    // post-processing is run in doNotifying()
0491         }
0492 
0493         if (curr->requestor) {
0494             --curr->requestor->nRequests;
0495         }
0496 
0497         //qDebug("KResolverManager::handleFinishedItem(%u): removing %p since it's done",
0498         //     pid, (void*)curr);
0499         return true;
0500     }
0501     return false;
0502 }
0503 
0504 void KResolverManager::registerNewWorker(KResolverWorkerFactoryBase *factory)
0505 {
0506     workerFactories.append(factory);
0507 }
0508 
0509 KResolverWorkerBase *KResolverManager::findWorker(KResolverPrivate *p)
0510 {
0511     /////
0512     // this function can be called on any user thread
0513     /////
0514 
0515     // this function is called with an unlocked mutex and it's expected to be
0516     // thread-safe!
0517     // but the factory list is expected not to be changed asynchronously
0518 
0519     // This function is responsible for finding a suitable worker for the given
0520     // input. That means we have to do a costly operation to create each worker
0521     // class and call their preprocessing functions. The first one that
0522     // says they can process (i.e., preprocess() returns true) will get the job.
0523 
0524     foreach (KResolverWorkerFactoryBase *factory, workerFactories) {
0525         KResolverWorkerBase *worker = factory->create();
0526 
0527         // set up the data the worker needs to preprocess
0528         worker->input = &p->input;
0529 
0530         if (worker->preprocess()) {
0531             // good, this one says it can process
0532             if (worker->m_finished) {
0533                 p->status = KResolver::PostProcessing;
0534             } else {
0535                 p->status = KResolver::Queued;
0536             }
0537             return worker;
0538         }
0539 
0540         // no, try again
0541         delete worker;
0542     }
0543 
0544     // found no worker
0545     return nullptr;
0546 }
0547 
0548 void KResolverManager::doNotifying(RequestData *p)
0549 {
0550     /////
0551     // This function may be called on any thread
0552     // any thread at all: user threads, GUI thread, manager thread or worker thread
0553     /////
0554 
0555     // Notification and finalisation
0556     //
0557     // Once a request has finished the normal processing, we call the
0558     // post processing function.
0559     //
0560     // After that is done, we will consolidate all results in the object's
0561     // KResolverResults and then post an event indicating that the signal
0562     // be emitted
0563     //
0564     // In case we detect that the object is waiting for completion, we do not
0565     // post the event, for KResolver::wait will take care of emitting the
0566     // signal.
0567     //
0568     // Once we release the mutex on the object, we may no longer reference it
0569     // for it might have been deleted.
0570 
0571     // "User" objects are those that are not created by the manager. Note that
0572     // objects created by worker threads are considered "user" objects. Objects
0573     // created by the manager are those created for KResolver::resolveAsync.
0574     // We should delete them.
0575 
0576     if (p->obj) {
0577         // lock the object
0578         p->obj->mutex.lock();
0579         KResolver *parent = p->obj->parent; // is 0 for non-"user" objects
0580         KResolverResults &r = p->obj->results;
0581 
0582         if (p->obj->status == KResolver::Canceled) {
0583             p->obj->status = KResolver::Canceled;
0584             p->obj->errorcode = KResolver::Canceled;
0585             p->obj->syserror = 0;
0586             r.setError(KResolver::Canceled, 0);
0587         } else if (p->worker) {
0588             // post processing
0589             p->worker->postprocess(); // ignore the result
0590 
0591             // copy the results from the worker thread to the final
0592             // object
0593             r = p->worker->results;
0594 
0595             // reset address
0596             r.setAddress(p->input->node, p->input->service);
0597 
0598             //qDebug("KResolverManager::doNotifying(%u/%p): for %p whose status is %d and has %d results",
0599             //pid, (void*)QThread::currentThread(), (void*)p, p->obj->status, r.count());
0600 
0601             p->obj->errorcode = r.error();
0602             p->obj->syserror = r.systemError();
0603             p->obj->status = !r.isEmpty() ?
0604                              KResolver::Success : KResolver::Failed;
0605         } else {
0606             r.clear();
0607             r.setError(p->obj->errorcode, p->obj->syserror);
0608         }
0609 
0610         // check whether there's someone waiting
0611         if (!p->obj->waiting && parent)
0612             // no, so we must post an event requesting that the signal be emitted
0613             // sorry for the C-style cast, but neither static nor reintepret cast work
0614             // here; I'd have to do two casts
0615         {
0616             QCoreApplication::postEvent(parent, new QEvent((QEvent::Type)(ResolutionCompleted)));
0617         }
0618 
0619         // release the mutex
0620         p->obj->mutex.unlock();
0621     } else {
0622         // there's no object!
0623         if (p->worker) {
0624             p->worker->postprocess();
0625         }
0626     }
0627 
0628     delete p->worker;
0629 
0630     // ignore p->requestor and p->nRequests
0631     // they have been dealt with by the main loop
0632 
0633     delete p;
0634 
0635     // notify any objects waiting in KResolver::wait
0636     notifyWaiters.wakeAll();
0637 }
0638 
0639 // enqueue a new request
0640 // this function is called from KResolver::start and
0641 // from KResolverWorkerBase::enqueue
0642 void KResolverManager::enqueue(KResolver *obj, RequestData *requestor)
0643 {
0644     RequestData *newrequest = new RequestData;
0645     newrequest->nRequests = 0;
0646     newrequest->obj = obj->d;
0647     newrequest->input = &obj->d->input;
0648     newrequest->requestor = requestor;
0649 
0650     // when processing a new request, find the most
0651     // suitable worker
0652     if ((newrequest->worker = findWorker(obj->d)) == nullptr) {
0653         // oops, problem
0654         // cannot find a worker class for this guy
0655         obj->d->status = KResolver::Failed;
0656         obj->d->errorcode = KResolver::UnsupportedFamily;
0657         obj->d->syserror = 0;
0658 
0659         doNotifying(newrequest);
0660         return;
0661     }
0662 
0663     // no, queue it
0664     // p->status was set in findWorker!
0665     if (requestor) {
0666         requestor->nRequests++;
0667     }
0668 
0669     if (!newrequest->worker->m_finished) {
0670         dispatch(newrequest);
0671     } else if (newrequest->nRequests > 0) {
0672         mutex.lock();
0673         currentRequests.append(newrequest);
0674         mutex.unlock();
0675     } else
0676         // already done
0677     {
0678         doNotifying(newrequest);
0679     }
0680 }
0681 
0682 // a new request has been created
0683 // dispatch it
0684 void KResolverManager::dispatch(RequestData *data)
0685 {
0686     // As stated in the beginning of the file, this function
0687     // is supposed to verify the availability of threads, start
0688     // any if necessary
0689 
0690     QMutexLocker locker(&mutex);
0691 
0692     // add to the queue
0693     newRequests.append(data);
0694 
0695     // check if we need to start a new thread
0696     //
0697     // we depend on the variables availableThreads and runningThreads to
0698     // know if we are supposed to start any threads:
0699     // - if availableThreads > 0, then there is at least one thread waiting,
0700     //    blocked in KResolverManager::requestData. It can't unblock
0701     //    while we are holding the mutex locked, therefore we are sure that
0702     //    our event will be handled
0703     // - if availableThreads == 0:
0704     //   - if runningThreads < maxThreads
0705     //     we will start a new thread, which will certainly block in
0706     //     KResolverManager::requestData because we are holding the mutex locked
0707     //   - if runningThreads == maxThreads
0708     //     This situation generally means that we have already maxThreads running
0709     //     and that all of them are processing. We will not start any new threads,
0710     //     but will instead wait for one to finish processing and request new data
0711     //
0712     //     There's a possible race condition here, which goes unhandled: if one of
0713     //     threads has timed out waiting for new data and is in the process of
0714     //     exiting. In that case, availableThreads == 0 and runningThreads will not
0715     //     have decremented yet. This means that we will not start a new thread
0716     //     that we could have. However, since there are other threads working, our
0717     //     event should be handled soon.
0718     //     It won't be handled if and only if ALL threads are in the process of
0719     //     exiting. That situation is EXTREMELY unlikely and is not handled either.
0720     //
0721     if (availableThreads == 0 && runningThreads < maxThreads) {
0722         // yes, a new thread should be started
0723 
0724         // find if there's a finished one
0725         KResolverThread *th = nullptr;
0726         for (int i = 0; i < workers.size(); ++i)
0727             if (!workers[i]->isRunning()) {
0728                 th = workers[i];
0729                 break;
0730             }
0731 
0732         if (th == nullptr) {
0733             // no, create one
0734             th = new KResolverThread;
0735             workers.append(th);
0736         }
0737 
0738         th->start();
0739         runningThreads++;
0740     }
0741 
0742     feedWorkers.wakeAll();
0743 
0744     // clean up idle threads
0745     QMutableListIterator<KResolverThread *> it(workers);
0746     while (it.hasNext()) {
0747         KResolverThread *worker = it.next();
0748         if (!worker->isRunning()) {
0749             it.remove();
0750             delete worker;
0751         }
0752     }
0753 }
0754 
0755 // this function is called by KResolverManager::dequeue
0756 bool KResolverManager::dequeueNew(KResolver *obj)
0757 {
0758     // This function must be called with a locked mutex
0759     // Deadlock warning:
0760     // always lock the global mutex first if both mutexes must be locked
0761 
0762     KResolverPrivate *d = obj->d;
0763 
0764     // check if it's in the new request list
0765     for (QMutableListIterator<RequestData *> it(newRequests);
0766             it.hasNext();) {
0767         RequestData *curr = it.next();
0768         if (curr->obj == d) {
0769             // yes, this object is still in the list
0770             // but it has never been processed
0771             d->status = KResolver::Canceled;
0772             d->errorcode = KResolver::Canceled;
0773             d->syserror = 0;
0774             it.remove();
0775 
0776             delete curr->worker;
0777             delete curr;
0778 
0779             return true;
0780         }
0781     }
0782 
0783     // check if it's running
0784     for (int i = 0; i < currentRequests.size(); ++i) {
0785         RequestData *curr = currentRequests[i];
0786         if (curr->obj == d) {
0787             // it's running. We cannot simply take it out of the list.
0788             // it will be handled when the thread that is working on it finishes
0789             d->mutex.lock();
0790 
0791             d->status = KResolver::Canceled;
0792             d->errorcode = KResolver::Canceled;
0793             d->syserror = 0;
0794 
0795             // disengage from the running threads
0796             curr->obj = nullptr;
0797             curr->input = nullptr;
0798             if (curr->worker) {
0799                 curr->worker->input = nullptr;
0800             }
0801 
0802             d->mutex.unlock();
0803         }
0804     }
0805 
0806     return false;
0807 }
0808 
0809 // this function is called by KResolver::cancel
0810 // it's expected to be thread-safe
0811 void KResolverManager::dequeue(KResolver *obj)
0812 {
0813     QMutexLocker locker(&mutex);
0814     dequeueNew(obj);
0815 }