kresolvermanager.cpp
00001 /* -*- C++ -*- 00002 * Copyright (C) 2003-2005 Thiago Macieira <thiago.macieira@kdemail.net> 00003 * 00004 * 00005 * Permission is hereby granted, free of charge, to any person obtaining 00006 * a copy of this software and associated documentation files (the 00007 * "Software"), to deal in the Software without restriction, including 00008 * without limitation the rights to use, copy, modify, merge, publish, 00009 * distribute, sublicense, and/or sell copies of the Software, and to 00010 * permit persons to whom the Software is furnished to do so, subject to 00011 * the following conditions: 00012 * 00013 * The above copyright notice and this permission notice shall be included 00014 * in all copies or substantial portions of the Software. 00015 * 00016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 00020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 00021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 00022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00023 */ 00024 00025 #include "config.h" 00026 00027 #include <sys/types.h> 00028 #include <netinet/in.h> 00029 #include <limits.h> 00030 #include <unistd.h> // only needed for pid_t 00031 00032 #ifdef HAVE_RES_INIT 00033 # include <sys/stat.h> 00034 extern "C" { 00035 # include <arpa/nameser.h> 00036 } 00037 # include <time.h> 00038 # include <resolv.h> 00039 #endif 00040 00041 #include <tqapplication.h> 00042 #include <tqstring.h> 00043 #include <tqcstring.h> 00044 #include <tqptrlist.h> 00045 #include <tqtimer.h> 00046 #include <tqmutex.h> 00047 #include <tqthread.h> 00048 #include <tqwaitcondition.h> 00049 #include <tqsemaphore.h> 00050 00051 #include <kde_file.h> 00052 #include <kdebug.h> 00053 #include "kresolver.h" 00054 #include "kresolver_p.h" 00055 #include "kresolverworkerbase.h" 00056 00057 namespace KNetwork 00058 { 00059 namespace Internal 00060 { 00061 void initSrvWorker(); 00062 void initStandardWorkers(); 00063 } 00064 } 00065 00066 using namespace KNetwork; 00067 using namespace KNetwork::Internal; 00068 00069 /* 00070 * Explanation on how the resolver system works 00071 00072 When KResolver::start is called, it calls KResolverManager::enqueue to add 00073 an entry to the queue. KResolverManager::enqueue will verify the availability 00074 of a worker thread: if one is available, it will dispatch the request to it. 00075 If no threads are available, it will then decide whether to launch a thread 00076 or to queue for the future. 00077 00078 (This process is achieved by always queueing the new request, starting a 00079 new thread if necessary and then notifying of the availability of data 00080 to all worker threads). 00081 00082 * Worker thread 00083 A new thread, when started, will enter its event loop 00084 immediately. That is, it'll first try to acquire new data to 00085 process, which means it will lock and unlock the manager mutex in 00086 the process. 00087 00088 If it finds no new data, it'll wait on the feedWorkers condition 00089 for a certain maximum time. If that time expires and there's still 00090 no data, the thread will exit, in order to save system resources. 00091 00092 If it finds data, however, it'll set up and call the worker class 00093 that has been selected by the manager. Once that worker is done, 00094 the thread releases the data through KResolverManager::releaseData. 00095 00096 * Data requesting/releasing 00097 A worker thread always calls upon functions on the resolver manager 00098 in order to acquire and release data. 00099 00100 When data is being requested, the KResolverManager::requestData 00101 function will look the currentRequests list and return the first 00102 Queued request it finds, while marking it to be InProgress. 00103 00104 When the worker class has returned, the worker thread will release 00105 that data through the KResolverManager::releaseData function. If the 00106 worker class has requested no further data (nRequests == 0), the 00107 request's status is marked to be Done. It'll then look at the 00108 requestor for that data: if it was requested by another worker, 00109 it'll decrement the requests count for that one and add the results 00110 to a list. And, finally, if the requests count for the requestor 00111 becomes 0, it'll repeat this process for the requestor as well 00112 (change status to Done, check for a requestor). 00113 */ 00114 00115 namespace 00116 { 00117 00118 /* 00119 * This class is used to control the access to the 00120 * system's resolver API. 00121 * 00122 * It is necessary to periodically poll /etc/resolv.conf and reload 00123 * it if any changes are noticed. This class does exactly that. 00124 * 00125 * However, there's also the problem of reloading the structure while 00126 * some threads are in progress. Therefore, we keep a usage reference count. 00127 */ 00128 class ResInitUsage 00129 { 00130 public: 00131 00132 #ifdef HAVE_RES_INIT 00133 time_t mTime; 00134 int useCount; 00135 00136 # ifndef RES_INIT_THREADSAFE 00137 TQWaitCondition cond; 00138 TQMutex mutex; 00139 # endif 00140 00141 bool shouldResInit() 00142 { 00143 // check if /etc/resolv.conf has changed 00144 KDE_struct_stat st; 00145 if (KDE_stat("/etc/resolv.conf", &st) != 0) 00146 return false; 00147 00148 if (mTime != st.st_mtime) 00149 { 00150 kdDebug(179) << "shouldResInit: /etc/resolv.conf updated" << endl; 00151 return true; 00152 } 00153 return false; 00154 } 00155 00156 void callResInit() 00157 { 00158 if (mTime != 0) 00159 { 00160 // don't call it the first time 00161 // let it be initialised naturally 00162 kdDebug(179) << "callResInit: calling res_init()" << endl; 00163 res_init(); 00164 } 00165 00166 KDE_struct_stat st; 00167 if (KDE_stat("/etc/resolv.conf", &st) == 0) 00168 mTime = st.st_mtime; 00169 } 00170 00171 ResInitUsage() 00172 : mTime(0), useCount(0) 00173 { } 00174 00175 /* 00176 * Marks the end of usage to the resolver tools 00177 */ 00178 void release() 00179 { 00180 # ifndef RES_INIT_THREADSAFE 00181 TQMutexLocker locker(&mutex); 00182 if (--useCount == 0) 00183 { 00184 if (shouldResInit()) 00185 callResInit(); 00186 00187 // we've reached 0, wake up anyone that's waiting to call res_init 00188 cond.wakeAll(); 00189 } 00190 # else 00191 // do nothing 00192 # endif 00193 } 00194 00195 /* 00196 * Marks the beginning of usage of the resolver API 00197 */ 00198 void acquire() 00199 { 00200 # ifndef RES_INIT_THREADSAFE 00201 mutex.lock(); 00202 00203 if (shouldResInit()) 00204 { 00205 if (useCount) 00206 { 00207 // other threads are already using the API, so wait till 00208 // it's all clear 00209 // the thread that emits this condition will also call res_init 00210 //tqDebug("ResInitUsage: waiting for libresolv to be clear"); 00211 cond.wait(&mutex); 00212 } 00213 else 00214 // we're clear 00215 callResInit(); 00216 } 00217 useCount++; 00218 mutex.unlock(); 00219 00220 # else 00221 if (shouldResInit()) 00222 callResInit(); 00223 00224 # endif 00225 } 00226 00227 #else 00228 ResInitUsage() 00229 { } 00230 00231 bool shouldResInit() 00232 { return false; } 00233 00234 void acquire() 00235 { } 00236 00237 void release() 00238 { } 00239 #endif 00240 00241 } resInit; 00242 00243 } // anonymous namespace 00244 00245 /* 00246 * parameters 00247 */ 00248 // a thread will try maxThreadRetries to get data, waiting at most 00249 // maxThreadWaitTime milliseconds between each attempt. After that, it'll 00250 // exit 00251 static const int maxThreadWaitTime = 2000; // 2 seconds 00252 static const int maxThreads = 5; 00253 00254 static pid_t pid; // FIXME -- disable when everything is ok 00255 00256 KResolverThread::KResolverThread() 00257 : data(0L) 00258 { 00259 } 00260 00261 // remember! This function runs in a separate thread! 00262 void KResolverThread::run() 00263 { 00264 // initialisation 00265 // enter the loop already 00266 00267 //tqDebug("KResolverThread(thread %u/%p): started", pid, (void*)TQThread::currentThread()); 00268 KResolverManager::manager()->registerThread(this); 00269 while (true) 00270 { 00271 data = KResolverManager::manager()->requestData(this, ::maxThreadWaitTime); 00272 //tqDebug("KResolverThread(thread %u/%p) got data %p", KResolverManager::pid, 00273 // (void*)TQThread::currentThread(), (void*)data); 00274 if (data) 00275 { 00276 // yes, we got data 00277 // process it! 00278 00279 // 1) set up 00280 ; 00281 00282 // 2) run it 00283 data->worker->run(); 00284 00285 // 3) release data 00286 KResolverManager::manager()->releaseData(this, data); 00287 00288 // now go back to the loop 00289 } 00290 else 00291 break; 00292 } 00293 00294 KResolverManager::manager()->unregisterThread(this); 00295 //tqDebug("KResolverThread(thread %u/%p): exiting", pid, (void*)TQThread::currentThread()); 00296 } 00297 00298 bool KResolverThread::checkResolver() 00299 { 00300 return resInit.shouldResInit(); 00301 } 00302 00303 void KResolverThread::acquireResolver() 00304 { 00305 #if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD) 00306 getXXbyYYmutex.lock(); 00307 #endif 00308 00309 resInit.acquire(); 00310 } 00311 00312 void KResolverThread::releaseResolver() 00313 { 00314 #if defined(NEED_MUTEX) && !defined(Q_OS_FREEBSD) 00315 getXXbyYYmutex.unlock(); 00316 #endif 00317 00318 resInit.release(); 00319 } 00320 00321 static KResolverManager *globalManager; 00322 00323 KResolverManager* KResolverManager::manager() 00324 { 00325 if (globalManager == 0L) 00326 new KResolverManager(); 00327 return globalManager; 00328 } 00329 00330 KResolverManager::KResolverManager() 00331 : runningThreads(0), availableThreads(0) 00332 { 00333 globalManager = this; 00334 workers.setAutoDelete(true); 00335 currentRequests.setAutoDelete(true); 00336 initSrvWorker(); 00337 initStandardWorkers(); 00338 00339 pid = getpid(); 00340 } 00341 00342 KResolverManager::~KResolverManager() 00343 { 00344 // this should never be called 00345 00346 // kill off running threads 00347 for (workers.first(); workers.current(); workers.next()) 00348 workers.current()->terminate(); 00349 } 00350 00351 void KResolverManager::registerThread(KResolverThread* ) 00352 { 00353 } 00354 00355 void KResolverManager::unregisterThread(KResolverThread*) 00356 { 00357 runningThreads--; 00358 } 00359 00360 // this function is called by KResolverThread::run 00361 RequestData* KResolverManager::requestData(KResolverThread *th, int maxWaitTime) 00362 { 00364 // This function is called in a worker thread!! 00366 00367 // lock the mutex, so that the manager thread or other threads won't 00368 // interfere. 00369 TQMutexLocker locker(&mutex); 00370 RequestData *data = findData(th); 00371 00372 if (data) 00373 // it found something, that's good 00374 return data; 00375 00376 // nope, nothing found; sleep for a while 00377 availableThreads++; 00378 feedWorkers.wait(&mutex, maxWaitTime); 00379 availableThreads--; 00380 00381 data = findData(th); 00382 return data; 00383 } 00384 00385 RequestData* KResolverManager::findData(KResolverThread* th) 00386 { 00388 // This function is called by @ref requestData above and must 00389 // always be called with a locked mutex 00391 00392 // now find data to be processed 00393 for (RequestData *curr = newRequests.first(); curr; curr = newRequests.next()) 00394 if (!curr->worker->m_finished) 00395 { 00396 // found one 00397 if (curr->obj) 00398 curr->obj->status = KResolver::InProgress; 00399 curr->worker->th = th; 00400 00401 // move it to the currentRequests list 00402 currentRequests.append(newRequests.take()); 00403 00404 return curr; 00405 } 00406 00407 // found nothing! 00408 return 0L; 00409 } 00410 00411 // this function is called by KResolverThread::run 00412 void KResolverManager::releaseData(KResolverThread *, RequestData* data) 00413 { 00415 // This function is called in a worker thread!! 00417 00418 //tqDebug("KResolverManager::releaseData(%u/%p): %p has been released", pid, 00419 // (void*)TQThread::currentThread(), (void*)data); 00420 00421 if (data->obj) 00422 { 00423 data->obj->status = KResolver::PostProcessing; 00424 } 00425 00426 data->worker->m_finished = true; 00427 data->worker->th = 0L; // this releases the object 00428 00429 // handle finished requests 00430 handleFinished(); 00431 } 00432 00433 // this function is called by KResolverManager::releaseData above 00434 void KResolverManager::handleFinished() 00435 { 00436 bool redo = false; 00437 TQPtrQueue<RequestData> doneRequests; 00438 00439 mutex.lock(); 00440 00441 // loop over all items on the currently running list 00442 // we loop from the last to the first so that we catch requests with "requestors" before 00443 // we catch the requestor itself. 00444 RequestData *curr = currentRequests.last(); 00445 while (curr) 00446 { 00447 if (curr->worker->th == 0L) 00448 { 00449 if (handleFinishedItem(curr)) 00450 { 00451 doneRequests.enqueue(currentRequests.take()); 00452 if (curr->requestor && 00453 curr->requestor->nRequests == 0 && 00454 curr->requestor->worker->m_finished) 00455 // there's a requestor that is now finished 00456 redo = true; 00457 } 00458 } 00459 00460 curr = currentRequests.prev(); 00461 } 00462 00463 //tqDebug("KResolverManager::handleFinished(%u): %d requests to notify", pid, doneRequests.count()); 00464 while (RequestData *d = doneRequests.dequeue()) 00465 doNotifying(d); 00466 00467 mutex.unlock(); 00468 00469 if (redo) 00470 { 00471 //tqDebug("KResolverManager::handleFinished(%u): restarting processing to catch requestor", 00472 // pid); 00473 handleFinished(); 00474 } 00475 } 00476 00477 // This function is called by KResolverManager::handleFinished above 00478 bool KResolverManager::handleFinishedItem(RequestData* curr) 00479 00480 { 00481 // for all items that aren't currently running, remove from the list 00482 // this includes all finished or cancelled requests 00483 00484 if (curr->worker->m_finished && curr->nRequests == 0) 00485 { 00486 // this one has finished 00487 if (curr->obj) 00488 curr->obj->status = KResolver::PostProcessing; // post-processing is run in doNotifying() 00489 00490 if (curr->requestor) 00491 --curr->requestor->nRequests; 00492 00493 //tqDebug("KResolverManager::handleFinishedItem(%u): removing %p since it's done", 00494 // pid, (void*)curr); 00495 return true; 00496 } 00497 return false; 00498 } 00499 00500 00501 00502 void KResolverManager::registerNewWorker(KResolverWorkerFactoryBase *factory) 00503 { 00504 workerFactories.append(factory); 00505 } 00506 00507 KResolverWorkerBase* KResolverManager::findWorker(KResolverPrivate* p) 00508 { 00510 // this function can be called on any user thread 00512 00513 // this function is called with an unlocked mutex and it's expected to be 00514 // thread-safe! 00515 // but the factory list is expected not to be changed asynchronously 00516 00517 // This function is responsible for finding a suitable worker for the given 00518 // input. That means we have to do a costly operation to create each worker 00519 // class and call their preprocessing functions. The first one that 00520 // says they can process (i.e., preprocess() returns true) will get the job. 00521 00522 KResolverWorkerBase *worker; 00523 for (KResolverWorkerFactoryBase *factory = workerFactories.first(); factory; 00524 factory = workerFactories.next()) 00525 { 00526 worker = factory->create(); 00527 00528 // set up the data the worker needs to preprocess 00529 worker->input = &p->input; 00530 00531 if (worker->preprocess()) 00532 { 00533 // good, this one says it can process 00534 if (worker->m_finished) 00535 p->status = KResolver::PostProcessing; 00536 else 00537 p->status = KResolver::Queued; 00538 return worker; 00539 } 00540 00541 // no, try again 00542 delete worker; 00543 } 00544 00545 // found no worker 00546 return 0L; 00547 } 00548 00549 void KResolverManager::doNotifying(RequestData *p) 00550 { 00552 // This function may be called on any thread 00553 // any thread at all: user threads, GUI thread, manager thread or worker thread 00555 00556 // Notification and finalisation 00557 // 00558 // Once a request has finished the normal processing, we call the 00559 // post processing function. 00560 // 00561 // After that is done, we will consolidate all results in the object's 00562 // KResolverResults and then post an event indicating that the signal 00563 // be emitted 00564 // 00565 // In case we detect that the object is waiting for completion, we do not 00566 // post the event, for KResolver::wait will take care of emitting the 00567 // signal. 00568 // 00569 // Once we release the mutex on the object, we may no longer reference it 00570 // for it might have been deleted. 00571 00572 // "User" objects are those that are not created by the manager. Note that 00573 // objects created by worker threads are considered "user" objects. Objects 00574 // created by the manager are those created for KResolver::resolveAsync. 00575 // We should delete them. 00576 00577 if (p->obj) 00578 { 00579 // lock the object 00580 p->obj->mutex.lock(); 00581 KResolver* parent = p->obj->parent; // is 0 for non-"user" objects 00582 KResolverResults& r = p->obj->results; 00583 00584 if (p->obj->status == KResolver::Canceled) 00585 { 00586 p->obj->status = KResolver::Canceled; 00587 p->obj->errorcode = KResolver::Canceled; 00588 p->obj->syserror = 0; 00589 r.setError(KResolver::Canceled, 0); 00590 } 00591 else if (p->worker) 00592 { 00593 // post processing 00594 p->worker->postprocess(); // ignore the result 00595 00596 // copy the results from the worker thread to the final 00597 // object 00598 r = p->worker->results; 00599 00600 // reset address 00601 r.setAddress(p->input->node, p->input->service); 00602 00603 //tqDebug("KResolverManager::doNotifying(%u/%p): for %p whose status is %d and has %d results", 00604 //pid, (void*)TQThread::currentThread(), (void*)p, p->obj->status, r.count()); 00605 00606 p->obj->errorcode = r.error(); 00607 p->obj->syserror = r.systemError(); 00608 p->obj->status = !r.isEmpty() ? 00609 KResolver::Success : KResolver::Failed; 00610 } 00611 else 00612 { 00613 r.empty(); 00614 r.setError(p->obj->errorcode, p->obj->syserror); 00615 } 00616 00617 // check whether there's someone waiting 00618 if (!p->obj->waiting && parent) 00619 // no, so we must post an event requesting that the signal be emitted 00620 // sorry for the C-style cast, but neither static nor reintepret cast work 00621 // here; I'd have to do two casts 00622 TQApplication::postEvent(parent, new TQEvent((TQEvent::Type)(ResolutionCompleted))); 00623 00624 // release the mutex 00625 p->obj->mutex.unlock(); 00626 } 00627 else 00628 { 00629 // there's no object! 00630 if (p->worker) 00631 p->worker->postprocess(); 00632 } 00633 00634 delete p->worker; 00635 00636 // ignore p->requestor and p->nRequests 00637 // they have been dealt with by the main loop 00638 00639 delete p; 00640 00641 // notify any objects waiting in KResolver::wait 00642 notifyWaiters.wakeAll(); 00643 } 00644 00645 // enqueue a new request 00646 // this function is called from KResolver::start and 00647 // from KResolverWorkerBase::enqueue 00648 void KResolverManager::enqueue(KResolver *obj, RequestData *requestor) 00649 { 00650 RequestData *newrequest = new RequestData; 00651 newrequest->nRequests = 0; 00652 newrequest->obj = obj->d; 00653 newrequest->input = &obj->d->input; 00654 newrequest->requestor = requestor; 00655 00656 // when processing a new request, find the most 00657 // suitable worker 00658 if ((newrequest->worker = findWorker(obj->d)) == 0L) 00659 { 00660 // oops, problem 00661 // cannot find a worker class for this guy 00662 obj->d->status = KResolver::Failed; 00663 obj->d->errorcode = KResolver::UnsupportedFamily; 00664 obj->d->syserror = 0; 00665 00666 doNotifying(newrequest); 00667 return; 00668 } 00669 00670 // no, queue it 00671 // p->status was set in findWorker! 00672 if (requestor) 00673 requestor->nRequests++; 00674 00675 if (!newrequest->worker->m_finished) 00676 dispatch(newrequest); 00677 else if (newrequest->nRequests > 0) 00678 { 00679 mutex.lock(); 00680 currentRequests.append(newrequest); 00681 mutex.unlock(); 00682 } 00683 else 00684 // already done 00685 doNotifying(newrequest); 00686 } 00687 00688 // a new request has been created 00689 // dispatch it 00690 void KResolverManager::dispatch(RequestData *data) 00691 { 00692 // As stated in the beginning of the file, this function 00693 // is supposed to verify the availability of threads, start 00694 // any if necessary 00695 00696 TQMutexLocker locker(&mutex); 00697 00698 // add to the queue 00699 newRequests.append(data); 00700 00701 // check if we need to start a new thread 00702 // 00703 // we depend on the variables availableThreads and runningThreads to 00704 // know if we are supposed to start any threads: 00705 // - if availableThreads > 0, then there is at least one thread waiting, 00706 // blocked in KResolverManager::requestData. It can't unblock 00707 // while we are holding the mutex locked, therefore we are sure that 00708 // our event will be handled 00709 // - if availableThreads == 0: 00710 // - if runningThreads < maxThreads 00711 // we will start a new thread, which will certainly block in 00712 // KResolverManager::requestData because we are holding the mutex locked 00713 // - if runningThreads == maxThreads 00714 // This situation generally means that we have already maxThreads running 00715 // and that all of them are processing. We will not start any new threads, 00716 // but will instead wait for one to finish processing and request new data 00717 // 00718 // There's a possible race condition here, which goes unhandled: if one of 00719 // threads has timed out waiting for new data and is in the process of 00720 // exiting. In that case, availableThreads == 0 and runningThreads will not 00721 // have decremented yet. This means that we will not start a new thread 00722 // that we could have. However, since there are other threads working, our 00723 // event should be handled soon. 00724 // It won't be handled if and only if ALL threads are in the process of 00725 // exiting. That situation is EXTREMELY unlikely and is not handled either. 00726 // 00727 if (availableThreads == 0 && runningThreads < maxThreads) 00728 { 00729 // yes, a new thread should be started 00730 00731 // find if there's a finished one 00732 KResolverThread *th = workers.first(); 00733 while (th && th->running()) 00734 th = workers.next(); 00735 00736 if (th == 0L) 00737 // no, create one 00738 th = new KResolverThread; 00739 else 00740 workers.take(); 00741 00742 th->start(); 00743 workers.append(th); 00744 runningThreads++; 00745 } 00746 00747 feedWorkers.wakeAll(); 00748 00749 // clean up idle threads 00750 workers.first(); 00751 while (workers.current()) 00752 { 00753 if (!workers.current()->running()) 00754 workers.remove(); 00755 else 00756 workers.next(); 00757 } 00758 } 00759 00760 // this function is called by KResolverManager::dequeue 00761 bool KResolverManager::dequeueNew(KResolver* obj) 00762 { 00763 // This function must be called with a locked mutex 00764 // Deadlock warning: 00765 // always lock the global mutex first if both mutexes must be locked 00766 00767 KResolverPrivate *d = obj->d; 00768 00769 // check if it's in the new request list 00770 RequestData *curr = newRequests.first(); 00771 while (curr) 00772 if (curr->obj == d) 00773 { 00774 // yes, this object is still in the list 00775 // but it has never been processed 00776 d->status = KResolver::Canceled; 00777 d->errorcode = KResolver::Canceled; 00778 d->syserror = 0; 00779 newRequests.take(); 00780 00781 delete curr->worker; 00782 delete curr; 00783 00784 return true; 00785 } 00786 else 00787 curr = newRequests.next(); 00788 00789 // check if it's running 00790 curr = currentRequests.first(); 00791 while (curr) 00792 if (curr->obj == d) 00793 { 00794 // it's running. We cannot simply take it out of the list. 00795 // it will be handled when the thread that is working on it finishes 00796 d->mutex.lock(); 00797 00798 d->status = KResolver::Canceled; 00799 d->errorcode = KResolver::Canceled; 00800 d->syserror = 0; 00801 00802 // disengage from the running threads 00803 curr->obj = 0L; 00804 curr->input = 0L; 00805 if (curr->worker) 00806 curr->worker->input = 0L; 00807 00808 d->mutex.unlock(); 00809 } 00810 else 00811 curr = currentRequests.next(); 00812 00813 return false; 00814 } 00815 00816 // this function is called by KResolver::cancel 00817 // it's expected to be thread-safe 00818 void KResolverManager::dequeue(KResolver *obj) 00819 { 00820 TQMutexLocker locker(&mutex); 00821 dequeueNew(obj); 00822 }