threadpool.h

Go to the documentation of this file.
00001 /*
00002  * threadpool.h
00003  *
00004  * Generalised Thread Pooling functions
00005  *
00006  * Portable Tools Library
00007  *
00008  * Copyright (C) 2009 Post Increment
00009  *
00010  * The contents of this file are subject to the Mozilla Public License
00011  * Version 1.0 (the "License"); you may not use this file except in
00012  * compliance with the License. You may obtain a copy of the License at
00013  * http://www.mozilla.org/MPL/
00014  *
00015  * Software distributed under the License is distributed on an "AS IS"
00016  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
00017  * the License for the specific language governing rights and limitations
00018  * under the License.
00019  *
00020  * The Original Code is Portable Windows Library.
00021  *
00022  * The Initial Developer of the Original Code is Post Increment
00023  *
00024  * Portions of this code were written with the financial assistance of 
00025  * Metreos Corporation (http://www.metros.com).
00026  *
00027  * Contributor(s): ______________________________________.
00028  *
00029  * $Revision: 22552 $
00030  * $Author: csoutheren $
00031  * $Date: 2009-05-07 08:53:20 +0000 (Thu, 07 May 2009) $
00032  */
00033 
00034 
00035 #ifndef PTLIB_THREADPOOL_H
00036 #define PTLIB_THREADPOOL_H
00037 
00038 #ifdef P_USE_PRAGMA
00039 #pragma interface
00040 #endif
00041 
00042 #include <map>
00043 
00044 
00129 class PThreadPoolBase : public PObject
00130 {
00131   public:
00132     class WorkerThreadBase : public PThread
00133     {
00134       public:
00135         WorkerThreadBase()
00136           : PThread(100, NoAutoDeleteThread, NormalPriority, "Pool")
00137           , m_shutdown(false)
00138         { }
00139 
00140         virtual void Shutdown() = 0;
00141         virtual unsigned GetWorkSize() const = 0;
00142 
00143         bool m_shutdown;
00144         PMutex m_workerMutex;
00145     };
00146 
00147     class InternalWorkBase
00148     {
00149       public:
00150         InternalWorkBase(const char * group)
00151         { 
00152           if (group != NULL)
00153             m_group = group;
00154         }
00155         std::string m_group;
00156     };
00157 
00158     PThreadPoolBase(unsigned maxWorkerCount = 10, unsigned maxWorkUnitCount = 0);
00159     ~PThreadPoolBase();
00160 
00161     virtual WorkerThreadBase * CreateWorkerThread() = 0;
00162     virtual WorkerThreadBase * AllocateWorker();
00163 
00164   protected:
00165     virtual bool CheckWorker(WorkerThreadBase * worker);
00166     void StopWorker(WorkerThreadBase * worker);
00167     PMutex m_listMutex;
00168 
00169     typedef std::vector<WorkerThreadBase *> WorkerList_t;
00170     WorkerList_t m_workers;
00171 
00172     unsigned m_maxWorkerCount;
00173     unsigned m_maxWorkUnitCount;
00174 };
00175 
00176 
00177 template <class Work_T>
00178 class PThreadPool : public PThreadPoolBase
00179 {
00180   PCLASSINFO(PThreadPool, PThreadPoolBase);
00181   public:
00182     //
00183     // define the ancestor of the worker thread
00184     //
00185     class WorkerThread : public WorkerThreadBase
00186     {
00187       public:
00188         WorkerThread(PThreadPool & pool_)
00189           : m_pool(pool_)
00190         {
00191         }
00192 
00193         virtual void AddWork(Work_T * work) = 0;
00194         virtual void RemoveWork(Work_T * work) = 0;
00195         virtual void Main() = 0;
00196   
00197       protected:
00198         PThreadPool & m_pool;
00199     };
00200 
00201     //
00202     // define internal worker wrapper class
00203     //
00204     class InternalWork : public InternalWorkBase
00205     {
00206       public:
00207         InternalWork(WorkerThread * worker, Work_T * work, const char * group)
00208           : InternalWorkBase(group)
00209           , m_worker(worker)
00210           , m_work(work)
00211         { 
00212         }
00213 
00214         WorkerThread * m_worker;
00215         Work_T * m_work;
00216     };
00217 
00218     //
00219     // define map for external work units to internal work
00220     //
00221     typedef std::map<Work_T *, InternalWork> ExternalToInternalWorkMap_T;
00222     ExternalToInternalWorkMap_T m_externalToInternalWorkMap;
00223 
00224 
00225     //
00226     // define class for storing group informationm
00227     //
00228     struct GroupInfo {
00229       unsigned m_count;
00230       WorkerThread * m_worker;
00231     };
00232 
00233 
00234     //
00235     //  define map for group ID to group information
00236     //
00237     typedef std::map<std::string, GroupInfo> GroupInfoMap_t;
00238     GroupInfoMap_t m_groupInfoMap;
00239 
00240 
00241     //
00242     //  constructor
00243     //
00244     PThreadPool(unsigned maxWorkers = 10, unsigned maxWorkUnits = 0)
00245       : PThreadPoolBase(maxWorkers, maxWorkUnits) 
00246     { }
00247 
00248 
00249     //
00250     //  add a new unit of work to a worker thread
00251     //
00252     bool AddWork(Work_T * work, const char * group = NULL)
00253     {
00254       PWaitAndSignal m(m_listMutex);
00255 
00256       // allocate by group if specified
00257       // else allocate to least busy
00258       WorkerThread * worker;
00259       if ((group == NULL) || (strlen(group) == 0)) {
00260         worker = (WorkerThread *)AllocateWorker();
00261       }
00262       else {
00263 
00264         // find the worker thread with the matching group ID
00265         typename GroupInfoMap_t::iterator g = m_groupInfoMap.find(group);
00266         if (g == m_groupInfoMap.end()) 
00267           worker = (WorkerThread *)AllocateWorker();
00268         else {
00269           worker = g->second.m_worker;
00270           PTRACE(4, "ThreadPool\tAllocated worker thread by group Id " << group);
00271         }
00272       }
00273 
00274       // if cannot allocate worker, return
00275       if (worker == NULL) 
00276         return false;
00277 
00278       // create internal work structure
00279       InternalWork internalWork(worker, work, group);
00280 
00281       // add work to external to internal map
00282       m_externalToInternalWorkMap.insert(typename ExternalToInternalWorkMap_T::value_type(work, internalWork));
00283 
00284       // add group ID to map
00285       if (!internalWork.m_group.empty()) {
00286         typename GroupInfoMap_t::iterator r = m_groupInfoMap.find(internalWork.m_group);
00287         if (r != m_groupInfoMap.end())
00288           ++r->second.m_count;
00289         else {
00290           GroupInfo info;
00291           info.m_count  = 1;
00292           info.m_worker = worker;
00293           m_groupInfoMap.insert(typename GroupInfoMap_t::value_type(internalWork.m_group, info));
00294         }
00295       }
00296       
00297       // give the work to the worker
00298       worker->AddWork(work);
00299     
00300       return true;
00301     }
00302 
00303     //
00304     //  remove a unit of work from a worker thread
00305     //
00306     bool RemoveWork(Work_T * work, bool removeFromWorker = true)
00307     {
00308       PWaitAndSignal m(m_listMutex);
00309 
00310       // find worker with work unit to remove
00311       typename ExternalToInternalWorkMap_T::iterator r = m_externalToInternalWorkMap.find(work);
00312       if (r == m_externalToInternalWorkMap.end())
00313         return false;
00314 
00315       InternalWork & internalWork = r->second;
00316 
00317       // tell worker to stop processing work
00318       if (removeFromWorker)
00319         internalWork.m_worker->RemoveWork(work);
00320 
00321       // update group information
00322       if (!internalWork.m_group.empty()) {
00323         typename GroupInfoMap_t::iterator r = m_groupInfoMap.find(internalWork.m_group);
00324         PAssert(r != m_groupInfoMap.end(), "Attempt to find thread from unknown work group");
00325         if (r != m_groupInfoMap.end()) {
00326           if (--r->second.m_count == 0)
00327             m_groupInfoMap.erase(r);
00328         }
00329       }
00330 
00331       // see if workers need reorganising
00332       CheckWorker(internalWork.m_worker);
00333 
00334       // remove element from work unit map
00335       m_externalToInternalWorkMap.erase(r);
00336 
00337       return true;
00338     }
00339 };
00340 
00341 
00342 #endif // PTLIB_THREADPOOL_H
00343 
00344 
00345 // End Of File ///////////////////////////////////////////////////////////////

Generated on Tue May 19 09:51:19 2009 for PTLib by  doxygen 1.5.1