kdecore Library API Documentation

kresolvermanager.cpp

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