tclThreadJoin.c

Go 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  doxygen 1.5.1