00001 /* 00002 ** ThreadableObject is a base class for objects that are intended to 00003 ** internally spawn processing threads to execute 00004 ** specific member methods....a common example would be 00005 ** a multi-threaded server. 00006 ** This is needed because C++ member functions do not 00007 ** have the signature required by thread-spawning 00008 ** library functions. 00009 ** 00010 ** 00011 ** Derive the object class from ThreadableObject, and implement 00012 ** the class's StartThreadFunc method (this is the method through 00013 ** which a newly spawned thread reenters the class code). 00014 ** At the locations in the new class where a handler thread is to be 00015 ** spawned, set any appropriate variables (see discussion below), 00016 ** then call StartThreadWithArg() or StartThread(). 00017 ** 00018 ** Any method to be executed by a spawned thread should generally 00019 ** have the signature: THR_RTR func(void *). That is, it returns void 00020 ** and takes one parameter that is a void pointer. The parameter is 00021 ** not strictly required, but it can simplify coding under one set 00022 ** of circumstances. 00023 ** The spawning process allows a single void pointer parameter to be 00024 ** passed to a new thread. Even if a method to be executed by a 00025 ** spawned thread needs no parameters, the void pointer must still 00026 ** be passed, even as NULL. Keep in mind that 00027 ** If there will be more than one function that might be executed by 00028 ** a spawned thread, then there must be some way for a thread 00029 ** to determine which method to enter. One way to do this is 00030 ** by using a member variable (possibly with a static mutex). 00031 ** The method might also be indicated as part of a structure 00032 ** passed to the spawning code through the void pointer parameter. 00033 ** In some cases, the simplest routing code might result with an 00034 ** implementation that uses a pointer to class member function: 00035 ** <return_type>(<class_name>::*<pointer_type_name>)(<argument_list>) 00036 ** All of the methods pointed to by this pointer type must have the 00037 ** same signature. If they all really need different parameters, 00038 ** then designing them all to accept a single void pointer parameter 00039 ** will make them look the same. Using this method, just before calling 00040 ** StartThreadWithArg, set the static method pointer to 00041 ** point at the desired method: 00042 ** <my_member_pointer> = &<class_name>::<method_name> 00043 ** and in the StartThreadFunc, dereference the member pointer: 00044 ** (this->*<my_member_pointer>)(p_arg) 00045 ** note that in this call, p_arg is the void pointer passed through 00046 ** the spawn process -- just that some of the methods might use it and 00047 ** some might not. 00048 ** The reason why this is a simplification is that the code in 00049 ** the StartThreadFunc method does not have to decode the correct 00050 ** method to call based upon the void pointer contents, or any 00051 ** other variables -- it is implicit within the member method pointer. 00052 ** So, this implementation would be appropriate if the logic for 00053 ** determining which method a new thread should execute is complex. 00054 */ 00055 //--------------------------------------------------------------------------- 00056 #ifndef threadableobjectH 00057 #define threadableobjectH 00058 //--------------------------------------------------------------------------- 00059 #include <worm_statuscode.h> 00060 #include <mutex.h> 00061 00062 00063 //--------------------------------------------------------------------------- 00064 00065 #if defined(_WINNT) || defined(_Windows) // - - - - - - - - - - - - - - - - - - - - - - - 00066 00067 00068 #include <windows.h> 00069 #include <process.h> 00070 00071 #include <stddef.h> 00072 00073 typedef unsigned int TO_STACK_SIZE; 00074 typedef long TO_THREAD_ID; 00075 00076 00077 #define THREAD_RETURN void // Thread functions return this 00078 00079 00080 00081 #elif defined(_SOLARIS) // - - - - - - - - - - - - - - - - - - - - - - - 00082 00083 00084 #include <stdio.h> 00085 00086 #include <sys/types.h> 00087 #include <thread.h> 00088 00089 typedef size_t TO_STACK_SIZE; // or is it unsigned int? 00090 typedef thread_t TO_THREAD_ID; 00091 00092 00093 typedef void THREAD_RETURN; // Thread functions return this 00094 00095 00096 #else // - - - - - - - - - - - - - - - - - - - - - - - 00097 00098 #error threadableobject.h not complete for this O/S 00099 00100 #endif // - - - - - - - - - - - - - - - - - - - - - - - 00101 00102 //--------------------------------------------------------------------------- 00103 00104 00105 00106 class ThreadableObject 00107 { 00108 protected: 00109 00110 /* 00111 ** StartThreadWithArg() -- start a new thread with argument(s) 00112 ** 00113 ** Arguments: 00114 ** stack_size: Stack size of new thread in bytes 00115 ** If 0, stack size is set to 8192. 00116 ** In OS2, 4096 or 8192 is recommended. 00117 ** In SOLARIS, this argument is ignored 00118 ** In Windows NT, if stack_size=0, use the stack 00119 ** size of the calling thread. 00120 ** thread_id: Thread identification number returned to 00121 ** calling program. 00122 ** arg: an unsigned long (void*) passed to the thread. 00123 ** p_isdaemon start the thread/process as a daemon 00124 ** rather than internal thread (Solaris only) 00125 ** 00126 ** Returns: 00127 ** WORM_STAT_FAILURE if error 00128 ** WORM_STAT_SUCCESS if ok 00129 */ 00130 00131 WORM_STATUS_CODE StartThreadWithArg( TO_STACK_SIZE stack_size 00132 , TO_THREAD_ID * thread_id 00133 , void * arg 00134 #ifdef _SOLARIS 00135 , bool p_isdaemon = false 00136 #endif 00137 ); 00138 00139 /* 00140 ** StartThread() -- start a new thread with no arguments 00141 ** 00142 ** Arguments: 00143 ** stack_size: Stack size of new thread in bytes 00144 ** If 0, stack size is set to 8192. 00145 ** In OS2, 4096 or 8192 is recommended. 00146 ** In SOLARIS, this argument is ignored 00147 ** In Windows NT, if stack_size=0, use the stack 00148 ** size of the calling thread. 00149 ** thread_id: Thread identification number returned to 00150 ** calling program. 00151 ** 00152 ** Returns: 00153 ** WORM_STAT_FAILURE if error 00154 ** WORM_STAT_SUCCESS if ok 00155 */ 00156 00157 WORM_STATUS_CODE StartThread( TO_STACK_SIZE stack_size 00158 , TO_THREAD_ID * thread_id 00159 #ifdef _SOLARIS 00160 , bool p_isdaemon = false 00161 #endif 00162 ) 00163 { 00164 return StartThreadWithArg( stack_size 00165 , thread_id 00166 , NULL 00167 #ifdef _SOLARIS 00168 , p_isdaemon 00169 #endif 00170 ); 00171 } 00172 00173 /* 00174 ** KillSelfThread() -- Exit thread without effecting 00175 ** other threads 00176 */ 00177 00178 static void KillSelfThread(); 00179 00180 /* 00181 ** WaitForThread() -- wait for thread to terminate 00182 ** 00183 ** Returns: 00184 ** WORM_STAT_FAILURE if error 00185 ** WORM_STAT_SUCCESS if ok 00186 */ 00187 static int WaitForThread( TO_THREAD_ID * thread_id ); 00188 00189 /* 00190 ** KillThread() -- force immediate, unclean, thread exit 00191 ** 00192 ** Windows NT documentation gives a strong warning against 00193 ** using TerminateThread(), since no stack cleanup, etc, 00194 ** is done. 00195 ** 00196 ** Argument: 00197 ** tid = id of thread to kill 00198 ** 00199 ** Returns: 00200 ** WORM_STAT_SUCCESS if ok 00201 ** WORM_STAT_FAILURE if an error 00202 */ 00203 static int KillThread( TO_THREAD_ID tid ); 00204 00205 00206 // That -- Pointer to the ThreadableObject requesting a 00207 // new internal thread. 00208 // 00209 static ThreadableObject * That; 00210 00211 // ThatMutex -- access lock for That pointer memory 00212 // 00213 static TMutex * ThatMutex; 00214 00215 public: 00216 00217 ThreadableObject() 00218 { 00219 if ( ThatMutex == NULL ) 00220 { 00221 ThatMutex = new TMutex("ThreadMutex"); 00222 } 00223 } 00224 00225 // GetThat() -- Method used by the file-scope function 00226 // StartObjectThread() to access the "That" 00227 // pointer which is the object requesting 00228 // the thread start. 00229 // 00230 static ThreadableObject * GetThat() { return That; } 00231 00232 /* StartThreadFunc() -- Method through which a newly-spawned 00233 ** thread returns to the "That" object 00234 ** to start execution. 00235 ** 00236 ** PARAMETERS: 00237 ** p_argument = pointer to any object containing 00238 ** requisite unique start conditions to 00239 ** the thread, possibly including 00240 ** indication of which method to execute. 00241 */ 00242 virtual void StartThreadFunc( void * p_argument ) = 0; 00243 }; 00244 00245 #endif 00246