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
00059
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
00081
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
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
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
00221 addWorker(worker);
00222
00223 while (m_run) {
00224 while (m_pause && m_run) {
00225 worker.setPaused(true);
00226 wait(m_sleeptime);
00227 }
00228 worker.setPaused(false);
00229 if (!m_run) return;
00230
00231 if (!popAndWork(worker)) {
00232 waitForData();
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();
00319 worker.setBusy(true);
00320 boost::timer t;
00321 work(data);
00322 addTime(t.elapsed());
00323 worker.setBusy(false);
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 }
00446 #endif //#ifndef WORKERGROUP_H