$search
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