tclThreadJoin.cGo to the documentation of this file.00001 /* 00002 * tclThreadJoin.c -- 00003 * 00004 * This file implements a platform independent emulation layer for the 00005 * handling of joinable threads. The Windows platform uses this code to 00006 * provide the functionality of joining threads. This code is currently 00007 * not necessary on Unix. 00008 * 00009 * Copyright (c) 2000 by Scriptics Corporation 00010 * 00011 * See the file "license.terms" for information on usage and redistribution of 00012 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00013 * 00014 * RCS: @(#) $Id: tclThreadJoin.c,v 1.7 2005/11/07 15:15:06 dkf Exp $ 00015 */ 00016 00017 #include "tclInt.h" 00018 00019 #ifdef WIN32 00020 00021 /* 00022 * The information about each joinable thread is remembered in a structure as 00023 * defined below. 00024 */ 00025 00026 typedef struct JoinableThread { 00027 Tcl_ThreadId id; /* The id of the joinable thread. */ 00028 int result; /* A place for the result after the demise of 00029 * the thread. */ 00030 int done; /* Boolean flag. Initialized to 0 and set to 1 00031 * after the exit of the thread. This allows a 00032 * thread requesting a join to detect when 00033 * waiting is not necessary. */ 00034 int waitedUpon; /* Boolean flag. Initialized to 0 and set to 1 00035 * by the thread waiting for this one via 00036 * Tcl_JoinThread. Used to lock any other 00037 * thread trying to wait on this one. */ 00038 Tcl_Mutex threadMutex; /* The mutex used to serialize access to this 00039 * structure. */ 00040 Tcl_Condition cond; /* This is the condition a thread has to wait 00041 * upon to get notified of the end of the 00042 * described thread. It is signaled indirectly 00043 * by Tcl_ExitThread. */ 00044 struct JoinableThread *nextThreadPtr; 00045 /* Reference to the next thread in the list of 00046 * joinable threads. */ 00047 } JoinableThread; 00048 00049 /* 00050 * The following variable is used to maintain the global list of all joinable 00051 * threads. Usage by a thread is allowed only if the thread acquired the 00052 * 'joinMutex'. 00053 */ 00054 00055 TCL_DECLARE_MUTEX(joinMutex) 00056 00057 static JoinableThread* firstThreadPtr; 00058 00059 /* 00060 *---------------------------------------------------------------------- 00061 * 00062 * TclJoinThread -- 00063 * 00064 * This procedure waits for the exit of the thread with the specified id 00065 * and returns its result. 00066 * 00067 * Results: 00068 * A standard tcl result signaling the overall success/failure of the 00069 * operation and an integer result delivered by the thread which was 00070 * waited upon. 00071 * 00072 * Side effects: 00073 * Deallocates the memory allocated by TclRememberJoinableThread. 00074 * Removes the data associated to the thread waited upon from the list of 00075 * joinable threads. 00076 * 00077 *---------------------------------------------------------------------- 00078 */ 00079 00080 int 00081 TclJoinThread( 00082 Tcl_ThreadId id, /* The id of the thread to wait upon. */ 00083 int *result) /* Reference to a location for the result of 00084 * the thread we are waiting upon. */ 00085 { 00086 JoinableThread *threadPtr; 00087 00088 /* 00089 * Steps done here: 00090 * i. Acquire the joinMutex and search for the thread. 00091 * ii. Error out if it could not be found. 00092 * iii. If found, switch from exclusive access to the list to exclusive 00093 * access to the thread structure. 00094 * iv. Error out if some other is already waiting. 00095 * v. Skip the waiting part of the thread is already done. 00096 * vi. Wait for the thread to exit, mark it as waited upon too. 00097 * vii. Get the result form the structure, 00098 * viii. switch to exclusive access of the list, 00099 * ix. remove the structure from the list, 00100 * x. then switch back to exclusive access to the structure 00101 * xi. and delete it. 00102 */ 00103 00104 Tcl_MutexLock(&joinMutex); 00105 00106 threadPtr = firstThreadPtr; 00107 while (threadPtr!=NULL && threadPtr->id!=id) { 00108 threadPtr = threadPtr->nextThreadPtr; 00109 } 00110 00111 if (threadPtr == NULL) { 00112 /* 00113 * Thread not found. Either not joinable, or already waited upon and 00114 * exited. Whatever, an error is in order. 00115 */ 00116 00117 Tcl_MutexUnlock(&joinMutex); 00118 return TCL_ERROR; 00119 } 00120 00121 /* 00122 * [1] If we don't lock the structure before giving up exclusive access to 00123 * the list some other thread just completing its wait on the same thread 00124 * can delete the structure from under us, leaving us with a dangling 00125 * pointer. 00126 */ 00127 00128 Tcl_MutexLock(&threadPtr->threadMutex); 00129 Tcl_MutexUnlock(&joinMutex); 00130 00131 /* 00132 * [2] Now that we have the structure mutex any other thread that just 00133 * tries to delete structure will wait at location [3] until we are done 00134 * with the structure. And in that case we are done with it rather quickly 00135 * as 'waitedUpon' will be set and we will have to error out. 00136 */ 00137 00138 if (threadPtr->waitedUpon) { 00139 Tcl_MutexUnlock(&threadPtr->threadMutex); 00140 return TCL_ERROR; 00141 } 00142 00143 /* 00144 * We are waiting now, let other threads recognize this. 00145 */ 00146 00147 threadPtr->waitedUpon = 1; 00148 00149 while (!threadPtr->done) { 00150 Tcl_ConditionWait(&threadPtr->cond, &threadPtr->threadMutex, NULL); 00151 } 00152 00153 /* 00154 * We have to release the structure before trying to access the list again 00155 * or we can run into deadlock with a thread at [1] (see above) because of 00156 * us holding the structure and the other holding the list. There is no 00157 * problem with dangling pointers here as 'waitedUpon == 1' is still valid 00158 * and any other thread will error out and not come to this place. IOW, 00159 * the fact that we are here also means that no other thread came here 00160 * before us and is able to delete the structure. 00161 */ 00162 00163 Tcl_MutexUnlock(&threadPtr->threadMutex); 00164 Tcl_MutexLock(&joinMutex); 00165 00166 /* 00167 * We have to search the list again as its structure may (may, almost 00168 * certainly) have changed while we were waiting. Especially now is the 00169 * time to compute the predecessor in the list. Any earlier result can be 00170 * dangling by now. 00171 */ 00172 00173 if (firstThreadPtr == threadPtr) { 00174 firstThreadPtr = threadPtr->nextThreadPtr; 00175 } else { 00176 JoinableThread *prevThreadPtr = firstThreadPtr; 00177 00178 while (prevThreadPtr->nextThreadPtr != threadPtr) { 00179 prevThreadPtr = prevThreadPtr->nextThreadPtr; 00180 } 00181 prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr; 00182 } 00183 00184 Tcl_MutexUnlock(&joinMutex); 00185 00186 /* 00187 * [3] Now that the structure is not part of the list anymore no other 00188 * thread can acquire its mutex from now on. But it is possible that 00189 * another thread is still holding the mutex though, see location [2]. So 00190 * we have to acquire the mutex one more time to wait for that thread to 00191 * finish. We can (and have to) release the mutex immediately. 00192 */ 00193 00194 Tcl_MutexLock(&threadPtr->threadMutex); 00195 Tcl_MutexUnlock(&threadPtr->threadMutex); 00196 00197 /* 00198 * Copy the result to us, finalize the synchronisation objects, then free 00199 * the structure and return. 00200 */ 00201 00202 *result = threadPtr->result; 00203 00204 Tcl_ConditionFinalize(&threadPtr->cond); 00205 Tcl_MutexFinalize(&threadPtr->threadMutex); 00206 ckfree((char *) threadPtr); 00207 00208 return TCL_OK; 00209 } 00210 00211 /* 00212 *---------------------------------------------------------------------- 00213 * 00214 * TclRememberJoinableThread -- 00215 * 00216 * This procedure remebers a thread as joinable. Only a call to 00217 * TclJoinThread will remove the structre created (and initialized) here. 00218 * IOW, not waiting upon a joinable thread will cause memory leaks. 00219 * 00220 * Results: 00221 * None. 00222 * 00223 * Side effects: 00224 * Allocates memory, adds it to the global list of all joinable threads. 00225 * 00226 *---------------------------------------------------------------------- 00227 */ 00228 00229 void 00230 TclRememberJoinableThread( 00231 Tcl_ThreadId id) /* The thread to remember as joinable */ 00232 { 00233 JoinableThread *threadPtr; 00234 00235 threadPtr = (JoinableThread *) ckalloc(sizeof(JoinableThread)); 00236 threadPtr->id = id; 00237 threadPtr->done = 0; 00238 threadPtr->waitedUpon = 0; 00239 threadPtr->threadMutex = (Tcl_Mutex) NULL; 00240 threadPtr->cond = (Tcl_Condition) NULL; 00241 00242 Tcl_MutexLock(&joinMutex); 00243 00244 threadPtr->nextThreadPtr = firstThreadPtr; 00245 firstThreadPtr = threadPtr; 00246 00247 Tcl_MutexUnlock(&joinMutex); 00248 } 00249 00250 /* 00251 *---------------------------------------------------------------------- 00252 * 00253 * TclSignalExitThread -- 00254 * 00255 * This procedure signals that the specified thread is done with its 00256 * work. If the thread is joinable this signal is propagated to the 00257 * thread waiting upon it. 00258 * 00259 * Results: 00260 * None. 00261 * 00262 * Side effects: 00263 * Modifies the associated structure to hold the result. 00264 * 00265 *---------------------------------------------------------------------- 00266 */ 00267 00268 void 00269 TclSignalExitThread( 00270 Tcl_ThreadId id, /* Id of the thread signaling its exit. */ 00271 int result) /* The result from the thread. */ 00272 { 00273 JoinableThread *threadPtr; 00274 00275 Tcl_MutexLock(&joinMutex); 00276 00277 threadPtr = firstThreadPtr; 00278 while ((threadPtr != NULL) && (threadPtr->id != id)) { 00279 threadPtr = threadPtr->nextThreadPtr; 00280 } 00281 00282 if (threadPtr == NULL) { 00283 /* 00284 * Thread not found. Not joinable. No problem, nothing to do. 00285 */ 00286 00287 Tcl_MutexUnlock(&joinMutex); 00288 return; 00289 } 00290 00291 /* 00292 * Switch over the exclusive access from the list to the structure, then 00293 * store the result, set the flag and notify the waiting thread, provided 00294 * that it exists. The order of lock/unlock ensures that a thread entering 00295 * 'TclJoinThread' will not interfere with us. 00296 */ 00297 00298 Tcl_MutexLock(&threadPtr->threadMutex); 00299 Tcl_MutexUnlock(&joinMutex); 00300 00301 threadPtr->done = 1; 00302 threadPtr->result = result; 00303 00304 if (threadPtr->waitedUpon) { 00305 Tcl_ConditionNotify(&threadPtr->cond); 00306 } 00307 00308 Tcl_MutexUnlock(&threadPtr->threadMutex); 00309 } 00310 #endif /* WIN32 */ 00311 00312 /* 00313 * Local Variables: 00314 * mode: c 00315 * c-basic-offset: 4 00316 * fill-column: 78 00317 * End: 00318 */
Generated on Wed Mar 12 12:18:22 2008 by 1.5.1 |