Thread.cpp
Go to the documentation of this file.
00001 //
00002 // Thread.cpp: implementation file
00003 //
00004 // Copyright (C) Walter E. Capers.  All rights reserved
00005 //
00006 // This source is free to use as you like.  If you make
00007 // any changes please keep me in the loop.  Email your changes
00008 // to walt.capers@comcast.net.
00009 //
00010 // PURPOSE:
00011 //
00012 //  To implement threading as a C++ object
00013 //
00014 // NOTES:
00015 //  This object supports two types of thread models, event driven and
00016 //  interval driven.  Under the event driven model, a thread waits
00017 //  in a paused state until the member function Event is called.  When
00018 //  the Event function is called the thread wakes up and calls OnTask.
00019 //  Under the interval driven model, the thread wakes up every
00020 //  m_dwIdle milli-seconds and calls OnTask.
00021 //
00022 //  You can switch between the two models from within the same object.
00023 //
00024 // COMPILER NOTES:
00025 // On Unix you must use -lpthread a -lrt
00026 // On Windows you must specify threaded under C++ code generation
00027 //
00028 // REVISIONS
00029 // =======================================================
00030 // Date: 10.24.07        
00031 // Name: Walter E. Capers
00032 // Description: File creation
00033 //
00034 // Date: 10.24.07 11:49 am
00035 // Name: Walter E. Capers
00036 // Description: Added SetIdle function to allow the idle time to be altered
00037 //              independent of the SetThreadType member function.
00038 //              Added sleep interval to Stop function.
00039 //
00040 // Date: 10.25.07
00041 // Name: Walter E. Capers
00042 // Description: Added support for other non-windows platforms.
00043 //
00044 //              Added static functions: ThreadIdsEqual and ThreadId.
00045 //
00046 //              Added que for handling multiple events.
00047 //
00048 //              Created the CEventClass and CMutexClass classes to facilitate
00049 //              platform independence.
00050 //
00051 // Date: 10.26.07
00052 // Name: Walter E. Capers
00053 // Description: Made object production ready...
00054 //              Added more comments
00055 //
00056 //              Addressed various issues with threads on UNIX systems.
00057 //                -- there was a defect in the Sleep function
00058 //                -- there was a defect in the main thread function THKERNEL
00059 //                   , when transitioning between thread models the CEvent::Reset
00060 //                   function was not being called when it was necessary resulting
00061 //                   in a lock up.
00062 //              
00063 //                               Transition between thread types also failed on WINDOWS since the Event
00064 //               member function was being called from within SetThreadType.  This
00065 //               resulted in an Event usage error.  To correct the problem m_event.Set
00066 //               is called instead.  Also, eliminated unecessary logic.
00067 //
00068 //               Got rid of OnStart, OnStop, OnDestroy... Could not override with a derived
00069 //                               class, not sure why I will come back to in a later release.
00070 //
00071 //                               Changed default behavior of thread.  If OnTask is not redefined in the derived
00072 //               class the default version now expects a CTask object.  The Class for CTask 
00073 //               is defined in thread.h.  A class must be derived from CTask to use it in
00074 //               the default version of OnTask(LPVOID).
00075 //
00076 //
00077 
00078 #include <blort/ThreadObject/Thread.h>
00079 
00080 
00081 #ifndef WINDOWS
00082 extern "C"
00083 {
00084  int    usleep(useconds_t useconds);
00085 #ifdef NANO_SECOND_SLEEP
00086  int    nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
00087 #endif
00088 }
00089 
00090 void Sleep( unsigned int milli )
00091 {
00092 #ifdef NANO_SECOND_SLEEP
00093         struct timespec interval, remainder;
00094         milli = milli * 1000000;
00095         interval.tv_sec= 0;
00096         interval.tv_nsec=milli;
00097         nanosleep(&interval,&remainder);
00098 #else
00099         usleep(milli*1000);
00100 #endif  
00101 }
00102 #endif
00103 
00104 #include <iostream>
00105 using namespace std;
00106 
00114 #ifdef WINDOWS
00115 DWORD WINAPI
00116 #else
00117 LPVOID
00118 #endif
00119 _THKERNEL( LPVOID lpvData /* CThread Object */ 
00120                   )
00121 {
00122         CThread *pThread = (CThread *)lpvData;
00123         ThreadType_t lastType;
00124         /*
00125          *
00126          * initialization
00127          *
00128          */
00129 
00130 
00131     pThread->m_mutex.Lock();
00132                 pThread->m_state = ThreadStateWaiting;
00133                 pThread->m_bRunning = TRUE;
00134 #ifndef WINDOWS
00135                 pThread->m_dwId = CThread::ThreadId();
00136 #endif
00137         pThread->m_mutex.Unlock();
00138         
00139         while( TRUE )
00140         {
00141                 lastType = pThread->m_type;
00142 
00143                 if( lastType == ThreadTypeEventDriven )
00144                 {
00145                         if( ! pThread->m_event.Wait()  ) 
00146                                         break;
00147                 }
00148         
00149                 if( ! pThread->KernelProcess() ) 
00150                                 break;
00151 
00152 
00153                 if( lastType == ThreadTypeEventDriven )
00154                         pThread->m_event.Reset();
00155 
00156                 if( pThread->m_type == ThreadTypeIntervalDriven )
00157                         Sleep(pThread->m_dwIdle);
00158 
00159         }
00160 
00161 
00162         pThread->m_mutex.Lock();
00163                 pThread->m_state = ThreadStateDown;
00164                 pThread->m_bRunning = FALSE;
00165         pThread->m_mutex.Unlock();
00166 
00167 
00168 #ifdef WINDOWS
00169         return 0;
00170 #else
00171         return (LPVOID)0;
00172 #endif
00173 }
00174 
00182 BOOL 
00183 CThread::OnTask( LPVOID lpvData /*data passed from thread*/ 
00184                                            )
00185 {
00186         CTask *pTask = (CTask *)lpvData;
00187 
00188         pTask->SetTaskStatus(TaskStatusBeingProcessed);
00189 
00190     BOOL bReturn = pTask->Task();
00191 
00192         pTask->SetTaskStatus(TaskStatusCompleted);
00193 
00194 
00195         return bReturn; 
00196 } 
00197 
00198 
00206 BOOL
00207 CThread::OnTask()
00208 {
00209         printf("\nthread is alive\n");
00210 
00211         return TRUE;
00212 }
00213 
00214 
00215 BOOL
00216 CThread::Event(CTask *pvTask /* data to be processed by thread */
00217                            )
00218 {
00219         ThreadId_t id;
00220         m_mutex.Lock();
00221         if( m_type != ThreadTypeEventDriven )
00222         {
00223                 m_mutex.Unlock();
00224                 m_dwObjectCondition |= ILLEGAL_USE_OF_EVENT;
00225                 m_state = ThreadStateFault;
00226                 return FALSE;
00227         }
00228 
00229         m_mutex.Unlock();
00230         GetId(&id);
00231         pvTask->SetId(&id);
00232         if( ! Push((LPVOID)pvTask) )
00233                 return FALSE;
00234 
00235         pvTask->SetTaskStatus(TaskStatusWaitingOnQueue);
00236         m_event.Set();
00237 
00238         return TRUE;
00239 }
00240 
00247 BOOL
00248 CThread::Event(LPVOID lpvData /* data to be processed by thread */
00249                            )
00250 {
00251         m_mutex.Lock();
00252         if( m_type != ThreadTypeEventDriven )
00253         {
00254                 m_mutex.Unlock();
00255                 m_dwObjectCondition |= ILLEGAL_USE_OF_EVENT;
00256                 m_state = ThreadStateFault;
00257                 return FALSE;
00258         }
00259 
00260         m_mutex.Unlock();
00261         if( ! Push(lpvData) )
00262                 return FALSE;
00263 
00264         m_event.Set();
00265 
00266         return TRUE;
00267 }
00268 
00269 
00278 void
00279 CThread::SetPriority(DWORD dwPriority)
00280 {
00281 
00282 #ifdef WINDOWS
00283         SetThreadPriority(m_thread,dwPriority);
00284 #endif
00285 }
00286 
00287           
00294 BOOL
00295 CThread::KernelProcess()
00296 {
00297 
00298         m_mutex.Lock();
00299         m_state = ThreadStateBusy;
00300         if( !m_bRunning )
00301         {
00302                 m_state = ThreadStateShuttingDown;
00303                 m_mutex.Unlock();
00304                 return FALSE;
00305         }
00306         m_mutex.Unlock();
00307 
00308         if( !Empty() )
00309         {
00310                 while( !Empty() )
00311                 {
00312                         Pop();
00313                         if( !OnTask(m_lpvProcessor) )
00314                         {
00315                                 m_mutex.Lock();
00316                                 m_lpvProcessor = NULL;
00317                                 m_state = ThreadStateShuttingDown;
00318                                 m_mutex.Unlock();
00319                                 return FALSE;
00320                         }
00321                 }
00322                 m_mutex.Lock();
00323                 m_lpvProcessor = NULL;
00324                 m_state = ThreadStateWaiting;
00325         }
00326         else {
00327                 if( !OnTask() )
00328                 {
00329                         m_mutex.Lock();
00330                         m_state = ThreadStateShuttingDown;
00331                         m_mutex.Unlock();
00332                         return FALSE;
00333                 }
00334                 m_mutex.Lock();
00335                 m_state = ThreadStateWaiting;
00336         }
00337 
00338         m_mutex.Unlock();
00339 
00340         return TRUE;
00341 }
00342 
00343 
00351 unsigned int
00352 CThread::GetEventsPending()
00353 {
00354         unsigned int chEventsWaiting;
00355 
00356         m_mutex.Lock();
00357           chEventsWaiting = m_quePos;
00358     m_mutex.Unlock();
00359 
00360         return chEventsWaiting;
00361 }
00362 
00363 
00371 CThread::CThread(void)
00372 :m_bRunning(FALSE)
00373 #ifdef WINDOWS
00374 ,m_thread(NULL)
00375 #endif
00376 ,m_dwId(0L)
00377 ,m_chQue(QUE_SIZE)
00378 ,m_quePos(0)
00379 ,m_lpvProcessor(NULL)
00380 ,m_dwIdle(100)
00381 ,m_type(ThreadTypeEventDriven)
00382 ,m_stackSize(DEFAULT_STACK_SIZE)
00383 {
00384 
00385         m_dwObjectCondition = NO_ERRORS;
00386 
00387         m_lppvQue = new LPVOID [QUE_SIZE];
00388 
00389         if( !m_lppvQue ) 
00390         {
00391                 m_dwObjectCondition |= MEMORY_FAULT;
00392                 m_state = ThreadStateFault;
00393                 return;
00394         }
00395 
00396         if( !m_mutex.m_bCreated )
00397         {
00398                 perror("mutex creation failed");
00399                 m_dwObjectCondition |= MUTEX_CREATION;
00400                 m_state = ThreadStateFault;
00401                 return;
00402         }
00403 
00404 
00405         if( !m_event.m_bCreated )
00406         {
00407                 perror("event creation failed");
00408                 m_dwObjectCondition |= EVENT_CREATION;
00409                 m_state = ThreadStateFault;
00410                 return;
00411         }
00412 
00413 
00414         Start();
00415 
00416 }
00417 
00425 BOOL
00426 CThread::Empty()
00427 {
00428         m_mutex.Lock();
00429         if( m_quePos <= 0 )
00430         {
00431                 m_mutex.Unlock();
00432                 return TRUE;
00433         }
00434         m_mutex.Unlock();
00435         return FALSE;
00436 }
00437 
00438 
00439 
00446 BOOL
00447 CThread::Push( LPVOID lpv )
00448 {
00449         if( !lpv ) return TRUE;
00450 
00451         m_mutex.Lock();
00452 
00453         if( m_quePos+1 >= m_chQue ) {
00454                 m_mutex.Unlock();
00455                 return FALSE;
00456         }
00457 
00458         m_lppvQue[m_quePos++] = lpv;
00459         m_mutex.Unlock();
00460         return TRUE;
00461 }
00462 
00463 
00470 BOOL
00471 CThread::Pop()
00472 {
00473 
00474         m_mutex.Lock();
00475         if( (int)m_quePos-1 < 0 )
00476         {
00477                 m_quePos = 0;
00478                 m_mutex.Unlock();
00479                 return FALSE;
00480         }
00481         m_quePos--;
00482         m_lpvProcessor = m_lppvQue[m_quePos];
00483         m_mutex.Unlock();
00484         return TRUE;
00485 }
00486 
00487 
00501 void
00502 CThread::SetThreadType(ThreadType_t typ,
00503               DWORD dwIdle)
00504 {
00505 
00506         m_mutex.Lock();
00507          m_dwIdle = dwIdle;
00508         
00509          if( m_type == typ ) {
00510                  m_mutex.Unlock();
00511                  return;
00512          }
00513 
00514                 m_type = typ;
00515 
00516 
00517    m_mutex.Unlock();
00518    m_event.Set();
00519 }
00520 
00521 
00528 void
00529 CThread::Stop()
00530 {
00531                 m_mutex.Lock();
00532                         m_bRunning = FALSE;
00533                 m_mutex.Unlock();
00534                 Event();
00535 
00536                 Sleep(m_dwIdle);
00537                 while(TRUE)
00538                 {
00539                         m_mutex.Lock();
00540                         if( m_state == ThreadStateDown )
00541                         {
00542                                 m_mutex.Unlock();
00543                                 return;
00544                         }
00545                         m_mutex.Unlock();
00546                         Sleep(m_dwIdle);
00547                 }
00548 }
00549 
00550 
00557 void
00558 CThread::SetIdle(DWORD dwIdle)
00559 {
00560         m_mutex.Lock();
00561                 m_dwIdle = dwIdle;
00562         m_mutex.Unlock();
00563 }
00564 
00571 BOOL
00572 CThread::Start()
00573 {
00574         m_mutex.Lock();
00575         if( m_bRunning ) 
00576         {
00577                 m_mutex.Unlock();
00578                 return TRUE;
00579         }
00580 
00581         m_mutex.Unlock();
00582 
00583 
00584         if( m_dwObjectCondition & THREAD_CREATION )
00585                 m_dwObjectCondition = m_dwObjectCondition ^ THREAD_CREATION;
00586 
00587 #ifdef WINDOWS
00588         if( m_thread ) CloseHandle(m_thread);
00589 
00590         m_thread = CreateThread(NULL,m_stackSize ,_THKERNEL,(LPVOID)this,0,&m_dwId);
00591         if( !m_thread )
00592         {
00593                 perror("thread creating failed");
00594                 m_dwObjectCondition |= THREAD_CREATION;
00595                 m_state = ThreadStateFault;
00596                 return FALSE;
00597         }
00598 #else
00599         pthread_attr_t attr;
00600         
00601         pthread_attr_init(&attr);
00602 
00603 #ifdef VMS
00604         if( m_stackSize == 0 )
00605                 pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN*10);
00606 #endif
00607         if( m_stackSize != 0 )
00608                 pthread_attr_setstacksize(&attr,m_stackSize);
00609 
00610         int error = pthread_create(&m_thread,&attr,_THKERNEL,(LPVOID)this);
00611 
00612         if( error != 0 )
00613         {
00614                 m_dwObjectCondition |= THREAD_CREATION;
00615                 m_state = ThreadStateFault;
00616 
00617 #if defined(HPUX) || defined(SUNOS) || defined(LINUX)
00618                   switch(error)/* show the thread error */
00619                   {
00620 
00621                   case EINVAL:
00622                           cerr << "error: attr in an invalid thread attributes object\n";
00623                           break;
00624                   case EAGAIN:
00625                           cerr << "error: the necessary resources to create a thread are not\n";
00626                           cerr << "available.\n";
00627                           break;
00628                   case EPERM:
00629                           cerr << "error: the caller does not have the privileges to create\n";
00630                           cerr << "the thread with the specified attr object.\n";
00631                           break;
00632 #if defined(HPUX)
00633                   case ENOSYS:
00634 
00635                           cerr << "error: pthread_create not implemented!\n";
00636                           if( __is_threadlib_linked()==0 )
00637                           {
00638                                   cerr << "error: threaded library not being used, improper linkage \"-lpthread -lc\"!\n";
00639                           }
00640                           break;
00641 #endif
00642                   default:
00643                           cerr << "error: an unknown error was encountered attempting to create\n";
00644                           cerr << "the requested thread.\n";
00645                           break;
00646                   }
00647 #else
00648                 cerr << "error: could not create thread, pthread_create failed (" << error << ")!\n";
00649 #endif
00650                  return FALSE;  
00651         }
00652 #endif
00653         return TRUE;
00654 }
00655 
00656 
00663 ThreadState_t 
00664 CThread::ThreadState()
00665 {
00666         ThreadState_t currentState;
00667         m_mutex.Lock();
00668                 currentState = m_state;
00669         m_mutex.Unlock();
00670         return currentState;
00671 }
00672 
00680 CThread::~CThread(void)
00681 {
00682 #ifdef WINDOWS
00683         if( m_bRunning )
00684         {
00685                 // TerminateThread(m_thread,1); brutal termination
00686           Stop(); // gracefull terminatation
00687           WaitForSingleObject(m_thread,INFINITE);
00688 
00689         }
00690         CloseHandle(m_thread);
00691 #else
00692         LPVOID lpv;
00693 
00694         if( m_bRunning )
00695         {
00696                 Stop();
00697                 pthread_join(m_thread,&lpv);
00698         }
00699 #endif
00700 
00701         delete [] m_lppvQue;
00702 }
00703 
00704 
00711 BOOL
00712 CThread::PingThread(DWORD dwTimeout /* timeout in milli-seconds */
00713                                  )
00714 {
00715     DWORD dwTotal = 0;
00716 
00717         while(TRUE)
00718         {
00719                 if( dwTotal > dwTimeout && dwTimeout > 0 )
00720                         return FALSE;
00721                 m_mutex.Lock();
00722                         if( m_bRunning )
00723                         {
00724                                 m_mutex.Unlock();
00725                                 return TRUE;
00726                         }
00727                 dwTotal += m_dwIdle;
00728                 m_mutex.Unlock();
00729                 Sleep(m_dwIdle);
00730         }
00731 
00732         return FALSE;
00733 }
00734 
00735 
00736 
00737 


blort
Author(s): Michael Zillich, Thomas Mörwald, Johann Prankl, Andreas Richtsfeld, Bence Magyar (ROS version)
autogenerated on Thu Jan 2 2014 11:38:26