blob: 1bec1f01ae1e12794a9374ff77b4c26af1adecb5 [file] [log] [blame]
Alexandre Lision7fd5d3d2013-12-04 13:06:40 -05001//
2// Thread.cpp: implementation file
3//
4// Copyright (C) Walter E. Capers. All rights reserved
5//
6// This source is free to use as you like. If you make
7// any changes please keep me in the loop. Email your changes
8// to walt.capers@comcast.net.
9//
10// PURPOSE:
11//
12// To implement threading as a C++ object
13//
14// NOTES:
15// This object supports two types of thread models, event driven and
16// interval driven. Under the event driven model, a thread waits
17// in a paused state until the member function Event is called. When
18// the Event function is called the thread wakes up and calls OnTask.
19// Under the interval driven model, the thread wakes up every
20// m_dwIdle milli-seconds and calls OnTask.
21//
22// You can switch between the two models from within the same object.
23//
24// COMPILER NOTES:
25// On Unix you must use -lpthread a -lrt
26// On Windows you must specify threaded under C++ code generation
27//
28// REVISIONS
29// =======================================================
30// Date: 10.24.07
31// Name: Walter E. Capers
32// Description: File creation
33//
34// Date: 10.24.07 11:49 am
35// Name: Walter E. Capers
36// Description: Added SetIdle function to allow the idle time to be altered
37// independent of the SetThreadType member function.
38// Added sleep interval to Stop function.
39//
40// Date: 10.25.07
41// Name: Walter E. Capers
42// Description: Added support for other non-windows platforms.
43//
44// Added static functions: ThreadIdsEqual and ThreadId.
45//
46// Added que for handling multiple events.
47//
48// Created the CEventClass and CMutexClass classes to facilitate
49// platform independence.
50//
51// Date: 10.26.07
52// Name: Walter E. Capers
53// Description: Made object production ready...
54// Added more comments
55//
56// Addressed various issues with threads on UNIX systems.
57// -- there was a defect in the Sleep function
58// -- there was a defect in the main thread function THKERNEL
59// , when transitioning between thread models the CEvent::Reset
60// function was not being called when it was necessary resulting
61// in a lock up.
62//
63// Transition between thread types also failed on WINDOWS since the Event
64// member function was being called from within SetThreadType. This
65// resulted in an Event usage error. To correct the problem m_event.Set
66// is called instead. Also, eliminated unecessary logic.
67//
68// Got rid of OnStart, OnStop, OnDestroy... Could not override with a derived
69// class, not sure why I will come back to in a later release.
70//
71// Changed default behavior of thread. If OnTask is not redefined in the derived
72// class the default version now expects a CTask object. The Class for CTask
73// is defined in thread.h. A class must be derived from CTask to use it in
74// the default version of OnTask(LPVOID).
75//
76// Date: 11.01.07
77// Name: Walter E. Capers
78// Description: I introduced more logic and ASSERTIONS to insure the integrity of CThread objects.
79// Both the Homogeneous and Specialized thread types can be physically set using the
80// SetThreadType member function. If the thread type is not set, the thread will
81// determine its type based on calls to member functions; however, this does not
82// apply to interval-based threads. Interval-based threads must be implicitly
83// identified using the SetThreadType member function. The new integrity tests
84// are implemented to insure usage consistency with a CThread object.
85//
86// New member functions AtCapacity and PercentCapacity were added to determine
87// if a thread is truly busy. AtCapacity will return TRUE under one of two
88// conditions: the thread is processing an event and its stack is full, the thread
89// is not running. These new functions allow thread objects to be placed in arrays
90// and tasked based on their workloads.
91//
92// The Event member function has been modified to verify that a thread is running
93// before posting an event. This resolved a problem on SunOS were threads did not
94// start right away; there was a small delay of a few milliseconds.
95//
96// Error flags are automatically reset when certain member functions are called this
97// isolates error occurrences to specific call sequences.
98//
99//
100// Date: 11.01.07
101// Name: Walter E. Capers
102// Description: In THKernel, changed how events are released. Events are now released right after
103// They are recieved.
104
105#include "Thread.h"
106#ifdef USE_BEGIN_THREAD
107#include <process.h>
108#endif
109
110
111
112#ifndef WINDOWS
113
114#include <unistd.h>
115#include <pthread.h>
116
117extern "C"
118{
119 //int usleep(useconds_t useconds);
120#ifdef NANO_SECOND_SLEEP
121 int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
122#endif
123}
124
125void Sleep( unsigned int milli )
126{
127#ifdef NANO_SECOND_SLEEP
128 struct timespec interval, remainder;
129 milli = milli * 1000000;
130 interval.tv_sec= 0;
131 interval.tv_nsec=milli;
132 nanosleep(&interval,&remainder);
133#else
134 usleep(milli*1000);
135#endif
136}
137#endif
138
139#include <iostream>
140using namespace std;
141
142/**
143 *
144 * _THKERNEL
145 * thread callback function used by CreateThread
146 *
147 *
148 **/
149#ifdef WINDOWS
150#ifdef USE_BEGIN_THREAD
151unsigned __stdcall
152#else
153DWORD WINAPI
154#endif
155#else
156LPVOID
157#endif
158_THKERNEL( LPVOID lpvData /* CThread Object */
159 )
160{
161 CThread *pThread = (CThread *)lpvData;
162 ThreadType_t lastType;
163 /*
164 *
165 * initialization
166 *
167 */
168
169
170 pThread->m_mutex.Lock();
171 pThread->m_state = ThreadStateWaiting;
172 pThread->m_bRunning = TRUE;
173#ifndef WINDOWS
174 pThread->m_dwId = CThread::ThreadId();
175#endif
176 pThread->m_mutex.Unlock();
177
178 while( TRUE )
179 {
180 lastType = pThread->m_type;
181
182 if( lastType == ThreadTypeHomogeneous ||
183 lastType == ThreadTypeSpecialized ||
184 lastType == ThreadTypeNotDefined )
185 {
186 if( ! pThread->m_event.Wait() ) // wait for a message
187 break;
188 pThread->m_event.Reset(); // message recieved
189 }
190
191 if( ! pThread->KernelProcess() )
192 break;
193
194
195 /*if( lastType == ThreadTypeHomogeneous ||
196 lastType == ThreadTypeSpecialized ||
197 lastType == ThreadTypeNotDefined )
198 {
199 pThread->m_event.Reset();
200 } */
201
202 if( pThread->m_type == ThreadTypeIntervalDriven )
203 Sleep(pThread->m_dwIdle);
204
205 }
206
207
208 pThread->m_mutex.Lock();
209 pThread->m_state = ThreadStateDown;
210 pThread->m_bRunning = FALSE;
211 pThread->m_mutex.Unlock();
212
213
214#ifdef WINDOWS
215 return 0;
216#else
217 return (LPVOID)0;
218#endif
219}
220
221/**
222 *
223 * FromSameThread
224 * determines if the calling thread is the same
225 * as the thread assoicated with the object
226 *
227 **/
228BOOL
229CThread::FromSameThread()
230{
231 ThreadId_t id = ThreadId();
232 if( ThreadIdsEqual(&id,&m_dwId) ) return TRUE;
233 return FALSE;
234}
235
236/**
237 *
238 * OnTask
239 * called when a thread is tasked using the Event
240 * member function
241 *
242 **/
243BOOL
244CThread::OnTask( LPVOID lpvData /*data passed from thread*/
245 )
246{
247 ASSERT(lpvData && m_type == ThreadTypeHomogeneous);
248
249 if( m_type != ThreadTypeHomogeneous )
250 {
251 cerr << "Warning CThread::OnTask:\n\tOnTask(LPVOID) called for a non-homogeneous thread!\n";
252 return FALSE;
253 }
254
255 ((CTask *)lpvData)->SetTaskStatus(TaskStatusBeingProcessed);
256 BOOL bReturn = ((CTask *)lpvData)->Task();
257 ((CTask *)lpvData)->SetTaskStatus(TaskStatusCompleted);
258
259 return bReturn;
260}
261
262
263
264/**
265 *
266 * OnTask
267 * overloaded implementation of OnTask that
268 * takes no arguments
269 *
270 **/
271BOOL
272CThread::OnTask()
273{
274 ASSERT(m_type == ThreadTypeIntervalDriven);
275 if( m_type != ThreadTypeIntervalDriven )
276 {
277 cerr << "Warning CThread::OnTask:\n\tOnTask() called for a non-event driven thread!\n";
278 return FALSE;
279 }
280
281 printf("\nthread is alive\n");
282
283 return TRUE;
284}
285
286/**
287 *
288 * CEvent
289 * used to place tasks on the threads event queue
290 * wakes up thread.
291 *
292 **/
293BOOL
294CThread::Event(CTask *pvTask /* data to be processed by thread */
295 )
296{
297 m_mutex.Lock();
298
299 ASSERT(m_type == ThreadTypeHomogeneous ||
300 m_type == ThreadTypeNotDefined );
301
302 try
303 {
304 if( FromSameThread() )
305 {
306 throw "\n\tit is illegal for a thread to place an event on its own event stack!\n";
307 }
308
309
310 // make sure that the thread is running
311 if( !m_bRunning && m_dwObjectCondition == NO_ERRORS )
312 {
313 m_mutex.Unlock();
314 PingThread(m_dwIdle*2); // wait two idle cycles for it to start
315 m_mutex.Lock();
316 }
317 if( !m_bRunning ) // if it is not running return FALSE;
318 {
319 m_mutex.Unlock();
320 return FALSE;
321 }
322
323
324 if( m_dwObjectCondition & ILLEGAL_USE_OF_EVENT )
325 m_dwObjectCondition = m_dwObjectCondition ^ ILLEGAL_USE_OF_EVENT;
326 if( m_dwObjectCondition & EVENT_AND_TYPE_DONT_MATCH)
327 m_dwObjectCondition = m_dwObjectCondition ^ EVENT_AND_TYPE_DONT_MATCH;
328
329 if( m_type != ThreadTypeHomogeneous &&
330 m_type != ThreadTypeNotDefined )
331 {
332 m_mutex.Unlock();
333 m_dwObjectCondition |= ILLEGAL_USE_OF_EVENT;
334 m_dwObjectCondition |= EVENT_AND_TYPE_DONT_MATCH;
335 m_state = ThreadStateFault;
336 cerr << "Warning: invalid call to CEvent::Event(CTask *), thread type is not specialized\n";
337
338 return FALSE;
339 }
340
341 m_type = ThreadTypeHomogeneous;
342 m_mutex.Unlock();
343
344 pvTask->SetId(&m_dwId);
345 if( ! Push((LPVOID)pvTask) )
346 return FALSE;
347
348 pvTask->SetTaskStatus(TaskStatusWaitingOnQueue);
349 m_event.Set();
350
351 }
352 catch (char *psz)
353 {
354#ifdef WINDOWS
355 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::CEvent",MB_ICONHAND);
356 exit(-1);
357#else
358 cerr << "Fatal exception CThread::CEvent(CTask *pvTask):" << psz;
359#endif
360
361 }
362 return TRUE;
363}
364
365/**
366 *
367 * Event
368 * used to place tasks on the threads event queue
369 * wakes up thread.
370 *
371 **/
372BOOL
373CThread::Event(LPVOID lpvData /* data to be processed by thread */
374 )
375{
376
377 m_mutex.Lock();
378 ASSERT( m_type == ThreadTypeSpecialized ||
379 m_type == ThreadTypeNotDefined );
380 try
381 {
382 if( FromSameThread() )
383 {
384 throw "\n\tit is illegal for a thread to place an event on its own event stack!\n";
385 }
386 }
387 catch (char *psz)
388 {
389#ifdef WINDOWS
390 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::CEvent",MB_ICONHAND);
391 exit(-1);
392#else
393 cerr << "Fatal exception CThread::CEvent(LPVOID lpvData):" << psz;
394#endif
395
396 }
397
398 // make sure that the thread is running
399 if( !m_bRunning && m_dwObjectCondition == NO_ERRORS )
400 {
401 m_mutex.Unlock();
402 PingThread(m_dwIdle*2); // wait two idle cycles for it to start
403 m_mutex.Lock();
404 }
405 if( !m_bRunning ) // if it is not running return FALSE;
406 {
407 m_mutex.Unlock();
408 return FALSE;
409 }
410
411 if( m_dwObjectCondition & ILLEGAL_USE_OF_EVENT )
412 m_dwObjectCondition = m_dwObjectCondition ^ ILLEGAL_USE_OF_EVENT;
413 if( m_dwObjectCondition & EVENT_AND_TYPE_DONT_MATCH)
414 m_dwObjectCondition = m_dwObjectCondition ^ EVENT_AND_TYPE_DONT_MATCH;
415
416 if( m_type != ThreadTypeSpecialized &&
417 m_type != ThreadTypeNotDefined )
418 {
419 m_dwObjectCondition |= ILLEGAL_USE_OF_EVENT;
420 m_dwObjectCondition |= EVENT_AND_TYPE_DONT_MATCH;
421 cerr << "Warning: invalid call to CEvent::Event(LPVOID), thread type is not specialized\n";
422 m_mutex.Unlock();
423 return FALSE;
424 }
425 m_type = ThreadTypeSpecialized;
426
427 m_mutex.Unlock();
428 if( ! Push(lpvData) )
429 {
430 return FALSE;
431 }
432
433 m_event.Set();
434
435 return TRUE;
436}
437
438
439/**
440 *
441 * SetPriority
442 * sets a threads run priority, see SetThreadPriority
443 * Note: only works for Windows family of operating systems
444 *
445 *
446 **/
447void
448CThread::SetPriority(DWORD dwPriority)
449{
450
451#ifdef WINDOWS
452 SetThreadPriority(m_thread,dwPriority);
453#endif
454}
455
456
457/**
458 *
459 * KernelProcess
460 * routes thread activity
461 *
462 **/
463BOOL
464CThread::KernelProcess()
465{
466
467 m_mutex.Lock();
468 m_state = ThreadStateBusy;
469 if( !m_bRunning )
470 {
471 m_state = ThreadStateShuttingDown;
472 m_mutex.Unlock();
473 return FALSE;
474 }
475 m_mutex.Unlock();
476
477 if( !Empty() )
478 {
479 while( !Empty() )
480 {
481 Pop();
482 if( !OnTask(m_lpvProcessor) )
483 {
484 m_mutex.Lock();
485 m_lpvProcessor = NULL;
486 m_state = ThreadStateShuttingDown;
487 m_mutex.Unlock();
488 return FALSE;
489 }
490 }
491 m_mutex.Lock();
492 m_lpvProcessor = NULL;
493 m_state = ThreadStateWaiting;
494 }
495 else {
496 if( !OnTask() )
497 {
498 m_mutex.Lock();
499 m_state = ThreadStateShuttingDown;
500 m_mutex.Unlock();
501 return FALSE;
502 }
503 m_mutex.Lock();
504 m_state = ThreadStateWaiting;
505 }
506
507 m_mutex.Unlock();
508
509 return TRUE;
510}
511
512
513/**
514 *
515 * GetEventsPending
516 * returns the total number of vents waiting
517 * in the event que
518 *
519 **/
520unsigned int
521CThread::GetEventsPending()
522{
523 unsigned int chEventsWaiting;
524
525 m_mutex.Lock();
526 chEventsWaiting = m_queuePos;
527 m_mutex.Unlock();
528
529 return chEventsWaiting;
530}
531
532
533/**
534 *
535 * CThread
536 * instanciates thread object and
537 * starts thread.
538 *
539 **/
540CThread::CThread(void)
541:m_bRunning(FALSE)
542#ifdef WINDOWS
543,m_thread(NULL)
544#endif
545,m_dwId(0L)
546,m_state(ThreadStateDown)
547,m_dwIdle(100)
548,m_lppvQueue(NULL)
549,m_lpvProcessor(NULL)
550,m_chQueue(QUEUE_SIZE)
551,m_type(ThreadTypeNotDefined)
552,m_stackSize(DEFAULT_STACK_SIZE)
553,m_queuePos(0)
554,m_StopTimeout(30)
555{
556
557 m_dwObjectCondition = NO_ERRORS;
558
559 m_lppvQueue = new LPVOID [QUEUE_SIZE];
560
561 if( !m_lppvQueue )
562 {
563 m_dwObjectCondition |= MEMORY_FAULT;
564 m_state = ThreadStateFault;
565 return;
566 }
567
568 if( !m_mutex.m_bCreated )
569 {
570 perror("mutex creation failed");
571 m_dwObjectCondition |= MUTEX_CREATION;
572 m_state = ThreadStateFault;
573 return;
574 }
575
576
577 if( !m_event.m_bCreated )
578 {
579 perror("event creation failed");
580 m_dwObjectCondition |= EVENT_CREATION;
581 m_state = ThreadStateFault;
582 return;
583 }
584
585
586 Start();
587
588}
589
590
591/**
592 *
593 * PercentCapacity
594 * returns a floating point value identifying
595 * the current workload of the thread
596 *
597 **/
598float
599CThread::PercentCapacity()
600{
601 float fValue = 0;
602 m_mutex.Lock();
603 fValue = (float)m_queuePos/m_chQueue;
604 m_mutex.Unlock();
605 return fValue;
606}
607
608/**
609 *
610 * SetQueueSize
611 * changes the threads queue size
612 *
613 **/
614BOOL
615CThread::SetQueueSize( unsigned int ch )
616{
617 LPVOID * newQueue = NULL;
618
619 m_mutex.Lock();
620 ASSERT(ch > m_queuePos);
621
622 if( ch <= m_queuePos )
623 {
624 cerr << "Warning CThread::SetQueueSize:\n\tthe new queue size is less than the number of tasks on a non-empty queue! Request ignored.\n";
625 m_mutex.Unlock();
626 return FALSE;
627 }
628
629 newQueue = new LPVOID [ch];
630 if( !newQueue )
631 {
632 cerr << "Warning CThread::SetQueueSize:\n\ta low memory, could not reallocate queue!\n";
633 m_mutex.Unlock();
634 return FALSE;
635 }
636
637 for( unsigned int i=0;i<m_queuePos; i++ )
638 {
639 newQueue[i] = m_lppvQueue[i];
640 }
641
642 delete [] m_lppvQueue;
643
644 m_chQueue = ch;
645 m_lppvQueue = newQueue;
646
647 m_mutex.Unlock();
648
649 return TRUE;
650}
651
652
653
654/**
655 *
656 * Empty
657 * returns a value of TRUE if there are no items on the threads que
658 * otherwise a value of FALSE is returned.
659 *
660 **/
661BOOL
662CThread::Empty()
663{
664 m_mutex.Lock();
665 if( m_queuePos <= 0 )
666 {
667 m_mutex.Unlock();
668 return TRUE;
669 }
670 m_mutex.Unlock();
671 return FALSE;
672}
673
674
675
676/**
677 *
678 * Push
679 * place a data object in the threads que
680 *
681 **/
682BOOL
683CThread::Push( LPVOID lpv )
684{
685 if( !lpv ) return TRUE;
686
687 m_mutex.Lock();
688
689 if( m_queuePos+1 >= m_chQueue ) {
690 m_dwObjectCondition |= STACK_OVERFLOW;
691 m_mutex.Unlock();
692 return FALSE;
693 }
694 if( m_dwObjectCondition & STACK_EMPTY )
695 m_dwObjectCondition = m_dwObjectCondition ^ STACK_EMPTY;
696
697 if( m_dwObjectCondition & STACK_OVERFLOW )
698 m_dwObjectCondition = m_dwObjectCondition ^ STACK_OVERFLOW;
699
700 m_lppvQueue[m_queuePos++] = lpv;
701 if( m_queuePos+1 >= m_chQueue )
702 m_dwObjectCondition |= STACK_FULL;
703
704 m_mutex.Unlock();
705 return TRUE;
706}
707
708
709/**
710 *
711 * Pop
712 * move an object from the input que to the processor
713 *
714 **/
715BOOL
716CThread::Pop()
717{
718
719 m_mutex.Lock();
720 if( m_queuePos-1 < 0 )
721 {
722 m_queuePos = 0;
723 m_dwObjectCondition |= STACK_EMPTY;
724 m_mutex.Unlock();
725 return FALSE;
726 }
727 if( m_dwObjectCondition & STACK_EMPTY )
728 m_dwObjectCondition = m_dwObjectCondition ^ STACK_EMPTY;
729 if( m_dwObjectCondition & STACK_OVERFLOW )
730 m_dwObjectCondition = m_dwObjectCondition ^ STACK_OVERFLOW;
731 if( m_dwObjectCondition & STACK_FULL )
732 m_dwObjectCondition = m_dwObjectCondition ^ STACK_FULL;
733
734 m_queuePos--;
735 m_lpvProcessor = m_lppvQueue[m_queuePos];
736 m_mutex.Unlock();
737 return TRUE;
738}
739
740
741/**
742 *
743 * SetThreadType
744 * specifies the type of threading that is to be performed.
745 *
746 * ThreadTypeEventDriven (default): an event must be physically sent
747 * to the thread using the Event member
748 * function.
749 *
750 * ThreadTypeIntervalDriven : an event occurs automatically every
751 * dwIdle milli seconds.
752 *
753 **/
754void
755CThread::SetThreadType(ThreadType_t typ,
756 DWORD dwIdle)
757{
758
759 try
760 {
761 if( FromSameThread() )
762 {
763 throw "\n\tit is illegal for a thread to change its own type!\n";
764 }
765
766
767 m_mutex.Lock();
768 m_dwIdle = dwIdle;
769
770
771 if( m_type == typ ) {
772 m_mutex.Unlock();
773 return;
774 }
775 if( m_dwObjectCondition & ILLEGAL_USE_OF_EVENT )
776 m_dwObjectCondition = m_dwObjectCondition ^ ILLEGAL_USE_OF_EVENT;
777 if( m_dwObjectCondition & EVENT_AND_TYPE_DONT_MATCH )
778 m_dwObjectCondition = m_dwObjectCondition ^ EVENT_AND_TYPE_DONT_MATCH;
779
780 m_type = typ;
781
782
783 m_mutex.Unlock();
784 m_event.Set();
785 }
786 catch (char *psz)
787 {
788#ifdef WINDOWS
789 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::SetThreadType",MB_ICONHAND);
790 exit(-1);
791#else
792 cerr << "Fatal exception CThread::SetThreadType(ThreadType_t typ):" << psz;
793#endif
794
795 }
796}
797
798
799/**
800 *
801 * Stop
802 * stop thread
803 *
804 **/
805BOOL
806CThread::Stop()
807{
808 try
809 {
810 if( FromSameThread() )
811 {
812 throw "\n\tit is illegal for a thread to attempt to signal itself to stop!\n";
813 }
814
815 m_mutex.Lock();
816 m_bRunning = FALSE;
817 m_mutex.Unlock();
818 m_event.Set();
819
820 int ticks = (m_StopTimeout*1000)/100;
821
822 for( int i=0; i<ticks; i++ )
823 {
824 Sleep(100);
825
826 m_mutex.Lock();
827 if( m_state == ThreadStateDown )
828 {
829 m_mutex.Unlock();
830 return TRUE;
831 }
832 m_mutex.Unlock();
833
834 }
835 }
836 catch (char *psz)
837 {
838#ifdef WINDOWS
839 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::Stop",MB_ICONHAND);
840 exit(-1);
841#else
842 cerr << "Fatal exception CThread::Stop():" << psz;
843#endif
844
845 }
846 return FALSE;
847}
848
849
850/**
851 *
852 * SetIdle
853 * changes the threads idle interval
854 *
855 **/
856void
857CThread::SetIdle(DWORD dwIdle)
858{
859 m_mutex.Lock();
860 m_dwIdle = dwIdle;
861 m_mutex.Unlock();
862}
863
864/**
865 *
866 * Start
867 * start thread
868 *
869 **/
870BOOL
871CThread::Start()
872{
873 try
874 {
875 if( FromSameThread() )
876 {
877 throw "\n\tit is illegal for a thread to attempt to start itself!\n";
878 }
879
880
881 m_mutex.Lock();
882 if( m_bRunning )
883 {
884 m_mutex.Unlock();
885 return TRUE;
886 }
887
888 m_mutex.Unlock();
889
890
891 if( m_dwObjectCondition & THREAD_CREATION )
892 m_dwObjectCondition = m_dwObjectCondition ^ THREAD_CREATION;
893
894#ifdef WINDOWS
895 if( m_thread ) CloseHandle(m_thread);
896#ifdef USE_BEGIN_THREAD
897 m_thread = (HANDLE )_beginthreadex(NULL,(unsigned int)m_stackSize,_THKERNEL,(LPVOID)this,0,&m_dwId);
898#else
899 m_thread = CreateThread(NULL,m_stackSize ,_THKERNEL,(LPVOID)this,0,&m_dwId);
900#endif
901 if( !m_thread )
902 {
903 perror("thread creation failed");
904 m_dwObjectCondition |= THREAD_CREATION;
905 m_state = ThreadStateFault;
906 return FALSE;
907 }
908#else
909 pthread_attr_t attr;
910
911 pthread_attr_init(&attr);
912
913#ifdef VMS
914 if( m_stackSize == 0 )
915 pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN*10);
916#endif
917 if( m_stackSize != 0 )
918 pthread_attr_setstacksize(&attr,m_stackSize);
919
920 int error = pthread_create(&m_thread,&attr,_THKERNEL,(LPVOID)this);
921
922 if( error != 0 )
923 {
924 m_dwObjectCondition |= THREAD_CREATION;
925 m_state = ThreadStateFault;
926
927#if defined(HPUX) || defined(SUNOS) || defined(LINUX)
928 switch(error)/* show the thread error */
929 {
930
931 case EINVAL:
932 cerr << "error: attr in an invalid thread attributes object\n";
933 break;
934 case EAGAIN:
935 cerr << "error: the necessary resources to create a thread are not\n";
936 cerr << "available.\n";
937 break;
938 case EPERM:
939 cerr << "error: the caller does not have the privileges to create\n";
940 cerr << "the thread with the specified attr object.\n";
941 break;
942#if defined(HPUX)
943 case ENOSYS:
944
945 cerr << "error: pthread_create not implemented!\n";
946 if( __is_threadlib_linked()==0 )
947 {
948 cerr << "error: threaded library not being used, improper linkage \"-lpthread -lc\"!\n";
949 }
950 break;
951#endif
952 default:
953 cerr << "error: an unknown error was encountered attempting to create\n";
954 cerr << "the requested thread.\n";
955 break;
956 }
957#else
958 cerr << "error: could not create thread, pthread_create failed (" << error << ")!\n";
959#endif
960 return FALSE;
961 }
962#endif
963 }
964 catch (char *psz)
965 {
966#ifdef WINDOWS
967 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::Start",MB_ICONHAND);
968#else
969 cerr << "Fatal exception CThread::Start():" << psz;
970#endif
971 exit(-1);
972 }
973 return TRUE;
974}
975
976/**
977 *
978 * AtCapacity
979 * returns TRUE if the threads queue is full, and the thread
980 * is busy processing an event or the thread is not running
981 *
982 **/
983BOOL
984CThread::AtCapacity()
985{
986 m_mutex.Lock();
987 if( ((m_dwObjectCondition & STACK_OVERFLOW ||
988 m_dwObjectCondition & STACK_FULL ) &&
989 m_state == ThreadStateBusy) || !m_bRunning)
990 {
991 m_mutex.Unlock();
992 return TRUE;
993 }
994 m_mutex.Unlock();
995 return FALSE;
996}
997
998/**
999 *
1000 * ThreadState
1001 * return the current state of the thread
1002 *
1003 **/
1004ThreadState_t
1005CThread::ThreadState()
1006{
1007 ThreadState_t currentState;
1008 m_mutex.Lock();
1009 currentState = m_state;
1010 m_mutex.Unlock();
1011 return currentState;
1012}
1013
1014/**
1015 *
1016 * ~CThread
1017 * destructor. Stop should be called prior to destruction to
1018 * allow for gracefull thread termination.
1019 *
1020 **/
1021CThread::~CThread(void)
1022{
1023 if( m_bRunning ) // gracefull termination
1024 {
1025 try
1026 {
1027 if( !Stop() )
1028 {
1029 throw "\n\tthread failed to stop in a timely manner!\n";
1030 }
1031 }
1032 catch( char *psz )
1033 {
1034#ifdef WINDOWS
1035 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::Stop",MB_ICONHAND);
1036 exit(-1);
1037#else
1038 cerr << "Fatal exception CThread::Stop: " << psz;
1039#endif
1040 }
1041 }
1042#ifdef WINDOWS
1043 CloseHandle(m_thread);
1044#endif
1045
1046 delete [] m_lppvQueue;
1047}
1048
1049
1050/**
1051 *
1052 * PingThread
1053 * used to determine if a thread is running
1054 *
1055 **/
1056BOOL
1057CThread::PingThread(DWORD dwTimeout /* timeout in milli-seconds */
1058 )
1059{
1060 DWORD dwTotal = 0;
1061
1062 while(TRUE)
1063 {
1064 if( dwTotal > dwTimeout && dwTimeout > 0 )
1065 return FALSE;
1066 m_mutex.Lock();
1067 if( m_bRunning )
1068 {
1069 m_mutex.Unlock();
1070 return TRUE;
1071 }
1072 dwTotal += m_dwIdle;
1073 m_mutex.Unlock();
1074 Sleep(m_dwIdle);
1075 }
1076
1077 return FALSE;
1078}
1079
1080/**
1081 *
1082 * WaitTillExit
1083 * blocks caller until thread exits
1084 *
1085 **/
1086void
1087CThread::WaitTillExit()
1088{
1089
1090 /*
1091 *
1092 * prevent users from calling this function from within the same thread
1093 * of execution
1094 *
1095 */
1096 try
1097 {
1098 if( FromSameThread() )
1099 throw "\n\tthis function can not be called from within the same thread!\n";
1100
1101
1102
1103
1104 if( !m_bRunning ) return;
1105
1106
1107#ifdef WINDOWS
1108 WaitForSingleObject(m_thread,INFINITE);
1109#else
1110 LPVOID lpv;
1111
1112 pthread_join(m_thread,&lpv);
1113#endif
1114 }
1115 catch( char *psz )
1116 {
1117#ifdef WINDOWS
1118 MessageBoxA(NULL,&psz[2],"Fatal exception CThread::WaitTillExit",MB_ICONHAND);
1119 exit(-1);
1120#else
1121 cerr << "Fatal exception CThread::WaitTillExit: " << psz;
1122#endif
1123
1124 }
1125}
1126
1127
1128
1129