Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | File List | Namespace Members | Data Fields | Related Pages

WorkerGroup.h

00001 #ifndef WORKERGROUP_H
00002 #define WORKERGROUP_H
00003 #include <queue>
00004 #include <vector>
00005 #include <algorithm>
00006 
00007 #include <boost/thread.hpp>
00008 #include <boost/timer.hpp>
00009 
00010 #include "Sct/LogicErrors.h"
00011 #include "Sct/ISUtilities.h"
00012 #include "Sct/SctNames.h"
00013 #include "IS/ServiceStatus.h"
00014 
00015 namespace SctService{
00016 
00027 template <class T>
00028 class WorkerGroup : private boost::noncopyable {
00029 public:
00030     typedef T DataType;
00031     
00033     void go(unsigned nWorker=1);    
00034 
00039     unsigned queueSize() const;
00043     unsigned nWorkers() const;
00047     unsigned long nDone() const;
00052     unsigned busy() const;
00056     void stop();
00057     /*
00058      * Raise flag to pause all worker threads.
00059      * @param true=pause, false=unpause/play/restart 
00060      */
00061     void setPaused(bool paused=true);
00065     void join();
00069     unsigned paused() const;
00074     void setSleepTime(unsigned msec);
00078     unsigned getSleepTime() const {return m_sleeptime;}
00079     /*
00080      * Pop an object off the front of the queue. @return T to the object
00081      * if the queue isn't empty, otherwise, T()
00082      */
00083     DataType pop();
00087     void push(DataType ob);    
00088     
00090     virtual ~WorkerGroup() {;} 
00091     
00093     WorkerGroup() : m_run(true), m_pause(false), m_sleeptime(1), m_reportInterval(1000), m_nDone(0) {}
00094 
00096     void reportTo(std::string isname);
00098     void reportInterval(int msec);
00099 protected:
00101     class Worker {
00102     public:
00103     Worker(WorkerGroup<DataType>& group) : group(group), m_busy(false), m_paused(false) {
00104     }
00105     
00106     Worker(const Worker& w) : group(w.group), m_busy(false), m_paused(false) {}
00107 
00108     void operator() () {
00109         group.threadMain(*this);
00110     }
00111     WorkerGroup<DataType>& group;
00113     bool busy();
00115     bool paused();
00117     void setBusy(bool v);
00119     void setPaused(bool v);
00121     //Worker& operator=(const Worker& w)
00122     private:
00123     boost::mutex m_statusMutex;      
00124     volatile bool m_busy;            
00125     volatile bool m_paused;          
00126     };
00127 
00129     class Reporter {
00130     public:
00131       Reporter(WorkerGroup<DataType>& group) : group(group){ 
00132       }
00133       void operator() (){
00134     group.reportThreadMain(*this);
00135       }
00136       WorkerGroup<DataType>& group;
00137       ServiceStatus last;
00138     };
00139 
00141     virtual void reportThreadMain(Reporter& reporter) throw();
00142     
00148     virtual void threadMain(Worker& worker) throw();
00153     virtual bool popAndWork(Worker& worker);
00157     virtual void waitForData();
00161     virtual void work(DataType data) throw() = 0;
00165     void wait(int msec);
00169     void addWorker(Worker& worker);
00170     
00171     typedef typename std::vector<Worker*> WorkerStore;
00172     typedef typename WorkerStore::iterator Iterator;
00173     typedef typename WorkerStore::const_iterator ConstIterator;
00174     
00180      WorkerStore m_workers;
00181 
00186     std::queue<DataType> m_queue;
00187     
00188 
00190     boost::thread_group m_thread_group;
00191     
00192     volatile bool m_run;  
00193     volatile bool m_pause; 
00194     volatile unsigned m_sleeptime; 
00195     std::string m_reportTo; 
00196     volatile int m_reportInterval;   
00197     
00198     boost::mutex m_queueAccess;  
00199     boost::mutex m_vectorAccess; 
00200     boost::recursive_mutex m_reportAccess; 
00201     volatile unsigned long m_nDone;   
00202     volatile double m_time;   
00203     friend class Worker;
00204     friend class Reporter;
00205     void addTime(double t);      
00206 };
00207 
00208 
00209 template <class T>
00210 inline void WorkerGroup<T>::go(unsigned nWorker){    
00211     // create nWorker new threads
00212     for (unsigned i=0; i<nWorker; ++i) {
00213     m_thread_group.create_thread(Worker(*this));
00214     }
00215     m_thread_group.create_thread(Reporter(*this));
00216 }
00217 
00218 template <class T>
00219 inline void WorkerGroup<T>::threadMain(Worker& worker) throw() { 
00220     //As the worker is owned by some boost thing, we just need to add it.
00221     addWorker(worker);
00222     
00223     while (m_run) {                        // if !m_run, thread will finish.
00224     while (m_pause && m_run) {     // while paused, and not stopped, wait in sleep loop.
00225         worker.setPaused(true);
00226         wait(m_sleeptime);
00227     }
00228     worker.setPaused(false);
00229     if (!m_run) return;                     // if no longer running, stop.
00230     
00231     if (!popAndWork(worker)) {
00232         waitForData();    // if queue is empty, sleep.
00233     }
00234     }
00235     return;
00236 }
00237 
00238 template <class T>
00239 inline void WorkerGroup<T>::reportThreadMain(Reporter& reporter) throw() { 
00240   bool problemsToMrs=false;
00241   bool first=true;
00242   std::cout << "Status to: [" << m_reportTo << "]" << std::endl;
00243   while (m_run) {
00244     while (m_pause && m_run) {
00245       wait(m_reportInterval);
00246     }
00247     if (!m_run) return; 
00248     wait(m_reportInterval);
00249     {
00250       boost::recursive_mutex::scoped_lock lock(m_reportAccess);
00251       if (!lock.locked()) {
00252     continue;
00253       }
00254       bool modified = (reporter.last.queue != queueSize()||
00255                reporter.last.done != nDone() || first);
00256       if (first) first=false;
00257       if (modified){
00258     reporter.last.averageTimeEach=0.;
00259     reporter.last.timeLeft=0.;
00260     reporter.last.queue = queueSize();
00261     reporter.last.done  = nDone();
00262     if (reporter.last.done!=0) {
00263       reporter.last.averageTimeEach = static_cast<double>(m_time)/static_cast<double>(reporter.last.done);
00264     }
00265     unsigned nw=nWorkers();
00266     if (nw!=0) {
00267       reporter.last.timeLeft = reporter.last.queue*reporter.last.averageTimeEach / static_cast<double>(nw);
00268     }
00269     if (m_reportTo!="") {
00270       try {Sct::ISUtilities::addOrUpdateOrThrow(m_reportTo, reporter.last,
00271                             __FILE__, __LINE__, MRS_DIAGNOSTIC);
00272       }catch(Sct::Throwable& e){
00273         if (!problemsToMrs){
00274           problemsToMrs=true;
00275           e.sendToMrs(MRS_DIAGNOSTIC);
00276         }
00277       }
00278     }
00279       }
00280     }
00281   }
00282   return;
00283 }
00284 
00285 template<class T>
00286 inline void WorkerGroup<T>::addWorker(Worker& worker) {
00287     boost::mutex::scoped_lock lock(m_vectorAccess);
00288     m_workers.push_back(&worker);
00289 }
00290 
00291 template <class T>
00292 inline void WorkerGroup<T>::push(DataType ob){
00293     boost::mutex::scoped_lock lock(m_queueAccess);
00294     if (lock.locked()) m_queue.push(ob);
00295     else throw Sct::IllegalStateError("Couldn't get queue lock", __FILE__, __LINE__);
00296 }
00297 
00298 template <class T>
00299 inline T WorkerGroup<T>::pop(){
00300     boost::mutex::scoped_lock lock(m_queueAccess);
00301     if (lock.locked()) {
00302     if (m_queue.size()!=0) {
00303         DataType a=m_queue.front();
00304         m_queue.pop();
00305         return a;
00306     }
00307     return DataType();
00308     } else throw Sct::IllegalStateError("Couldn't get queue lock", __FILE__, __LINE__);
00309 }
00310 
00311 template <class W>
00312 inline bool WorkerGroup<W>::popAndWork(Worker& worker){
00313     boost::mutex::scoped_lock lock(m_queueAccess);
00314     if (lock.locked()) {    
00315     if (m_queue.size()!=0) {
00316         DataType data=m_queue.front();
00317         m_queue.pop();
00318         lock.unlock();           //Don't hold onto the lock for too long!
00319         worker.setBusy(true);        // set the busy flag
00320         boost::timer t;
00321         work(data);                  // call the function to do work.
00322         addTime(t.elapsed());
00323         worker.setBusy(false);       // unset the busy flag 
00324         return true;
00325     } else return false;
00326     } else throw Sct::IllegalStateError("Couldn't get queue lock", __FILE__, __LINE__);
00327 }
00328 
00329 template <class W>    
00330 inline unsigned WorkerGroup<W>::queueSize() const{
00331     return m_queue.size();
00332 }
00333 
00334 template <class W>    
00335 inline unsigned long WorkerGroup<W>::nDone() const{
00336     return m_nDone;
00337 }
00338 
00339 template <class W>
00340 inline unsigned WorkerGroup<W>::nWorkers() const{
00341     return m_workers.size();
00342 }
00343 
00344 template <class W>
00345 inline void WorkerGroup<W>::stop(){
00346     m_run = false;
00347     join();
00348 }
00349 
00350 template <class W>
00351 inline void WorkerGroup<W>::join(){
00352     m_thread_group.join_all();
00353 }
00354 
00355 template <class W>
00356 inline void WorkerGroup<W>::setPaused(bool p){
00357     m_pause = p;
00358 }
00359 
00360 template <class W>
00361 inline unsigned WorkerGroup<W>::paused() const{
00362     unsigned npaused=0;
00363     for (ConstIterator it=m_workers.begin(); it!=m_workers.end(); ++it) 
00364     if ( (*it)->paused() ) ++npaused;
00365 
00366     return npaused;
00367 }
00368 
00369 template <class W>
00370 inline unsigned WorkerGroup<W>::busy() const{
00371     unsigned nbusy=0;
00372     for (ConstIterator it=m_workers.begin(); it!=m_workers.end(); ++it) {
00373     if ( (*it)->busy() ) ++nbusy;
00374     }
00375     return nbusy;
00376 }
00377 
00378 template <class W>
00379 inline void WorkerGroup<W>::setSleepTime(unsigned t){
00380     m_sleeptime = t;
00381 }
00382 
00383 template <class W>
00384 inline void WorkerGroup<W>::wait(int wait_time) {
00385     boost::xtime xt;
00386     boost::xtime_get(&xt, boost::TIME_UTC);
00387     xt.nsec += wait_time*1000000;
00388     boost::thread::sleep(xt);
00389 }
00390 
00391 template <class W>
00392 inline void WorkerGroup<W>::waitForData(){
00393     wait(getSleepTime());
00394 }
00395 
00396 template <class T>
00397 void WorkerGroup<T>::reportTo(std::string isname){
00398   boost::recursive_mutex::scoped_lock lock(m_reportAccess);
00399   m_reportTo=isname;
00400 }
00401 
00402 template <class T>
00403 inline void WorkerGroup<T>::reportInterval(int msec){
00404   boost::recursive_mutex::scoped_lock lock(m_reportAccess);
00405   if (msec>100){
00406     m_reportInterval=msec;
00407   }
00408 }
00409 
00410 template <class W>
00411 inline void WorkerGroup<W>::addTime(double t){
00412   boost::recursive_mutex::scoped_lock lock(m_reportAccess);
00413   m_time+=t;
00414   ++m_nDone;
00415 }
00416 
00417 template <class W>
00418 inline bool WorkerGroup<W>::Worker::busy(){
00419   boost::mutex::scoped_lock lock(m_statusMutex);
00420   if (!lock.locked()) throw Sct::IllegalStateError("Could not get status lock",__FILE__, __LINE__);
00421   return m_busy;
00422 }
00423 
00424 template <class W>
00425 inline bool WorkerGroup<W>::Worker::paused(){
00426   boost::mutex::scoped_lock lock(m_statusMutex);
00427   if (!lock.locked()) throw Sct::IllegalStateError("Could not get status lock",__FILE__, __LINE__);
00428   return m_paused;
00429 }
00430 
00431 template <class W>
00432 void WorkerGroup<W>::Worker::setBusy(bool val){
00433   boost::mutex::scoped_lock lock(m_statusMutex);
00434   if (!lock.locked()) throw Sct::IllegalStateError("Could not get status lock",__FILE__, __LINE__);
00435   m_busy=val;
00436 }
00437 
00438 template <class W>
00439 inline void WorkerGroup<W>::Worker::setPaused(bool val){
00440   boost::mutex::scoped_lock lock(m_statusMutex);
00441   if (!lock.locked()) throw Sct::IllegalStateError("Could not get status lock",__FILE__, __LINE__);
00442   m_paused=val;
00443 }
00444 
00445 } // end of namepace SctService
00446 #endif //#ifndef WORKERGROUP_H

Generated on Thu Feb 10 02:40:24 2005 for SCT DAQ/DCS Software - C++ by doxygen 1.3.5