os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclThreadJoin.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclThreadJoin.c --
     3  *
     4  *	This file implements a platform independent emulation layer for
     5  *	the handling of joinable threads. The Mac and Windows platforms
     6  *	use this code to provide the functionality of joining threads.
     7  *	This code is currently not necessary on Unix.
     8  *
     9  * Copyright (c) 2000 by Scriptics Corporation
    10  *
    11  * See the file "license.terms" for information on usage and redistribution
    12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    13  *
    14  * RCS: @(#) $Id: tclThreadJoin.c,v 1.4 2002/04/24 20:35:40 hobbs Exp $
    15  */
    16 
    17 #include "tclInt.h"
    18 
    19 #if defined(WIN32) || defined(MAC_TCL)
    20 
    21 /* The information about each joinable thread is remembered in a
    22  * structure as defined below.
    23  */
    24 
    25 typedef struct JoinableThread {
    26   Tcl_ThreadId  id;                     /* The id of the joinable thread */
    27   int           result;                 /* A place for the result after the
    28 					 * demise of the thread */
    29   int           done;                   /* Boolean flag. Initialized to 0
    30 					 * and set to 1 after the exit of
    31 					 * the thread. This allows a thread
    32 					 * requesting a join to detect when
    33 					 * waiting is not necessary. */
    34   int           waitedUpon;             /* Boolean flag. Initialized to 0
    35 					 * and set to 1 by the thread waiting
    36 					 * for this one via Tcl_JoinThread.
    37 					 * Used to lock any other thread
    38 					 * trying to wait on this one.
    39 					 */
    40   Tcl_Mutex     threadMutex;            /* The mutex used to serialize access
    41 					 * to this structure. */
    42   Tcl_Condition cond;                   /* This is the condition a thread has
    43 					 * to wait upon to get notified of the
    44 					 * end of the described thread. It is
    45 					 * signaled indirectly by
    46 					 * Tcl_ExitThread. */
    47   struct JoinableThread* nextThreadPtr; /* Reference to the next thread in the
    48 					 * list of joinable threads */
    49 } JoinableThread;
    50 
    51 /* The following variable is used to maintain the global list of all
    52  * joinable threads. Usage by a thread is allowed only if the
    53  * thread acquired the 'joinMutex'.
    54  */
    55 
    56 TCL_DECLARE_MUTEX(joinMutex)
    57 
    58 static JoinableThread* firstThreadPtr;
    59 
    60 
    61 
    62 /*
    63  *----------------------------------------------------------------------
    64  *
    65  * TclJoinThread --
    66  *
    67  *	This procedure waits for the exit of the thread with the specified
    68  *	id and returns its result.
    69  *
    70  * Results:
    71  *	A standard tcl result signaling the overall success/failure of the
    72  *	operation and an integer result delivered by the thread which was
    73  *	waited upon.
    74  *
    75  * Side effects:
    76  *	Deallocates the memory allocated by TclRememberJoinableThread.
    77  *	Removes the data associated to the thread waited upon from the
    78  *	list of joinable threads.
    79  *
    80  *----------------------------------------------------------------------
    81  */
    82 
    83 int
    84 TclJoinThread(id, result)
    85     Tcl_ThreadId id;     /* The id of the thread to wait upon. */
    86     int*         result; /* Reference to a location for the result
    87 			  * of the thread we are waiting upon. */
    88 {
    89     /* Steps done here:
    90      * i.    Acquire the joinMutex and search for the thread.
    91      * ii.   Error out if it could not be found.
    92      * iii.  If found, switch from exclusive access to the list to exclusive
    93      *       access to the thread structure.
    94      * iv.   Error out if some other is already waiting.
    95      * v.    Skip the waiting part of the thread is already done.
    96      * vi.   Wait for the thread to exit, mark it as waited upon too.
    97      * vii.  Get the result form the structure, 
    98      * viii. switch to exclusive access of the list,
    99      * ix.   remove the structure from the list,
   100      * x.    then switch back to exclusive access to the structure
   101      * xi.   and delete it.
   102      */
   103 
   104     JoinableThread* threadPtr;
   105 
   106     Tcl_MutexLock (&joinMutex);
   107 
   108     for (threadPtr = firstThreadPtr;
   109 	 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
   110 	 threadPtr = threadPtr->nextThreadPtr)
   111         /* empty body */
   112       ;
   113 
   114     if (threadPtr == (JoinableThread*) NULL) {
   115         /* Thread not found. Either not joinable, or already waited
   116 	 * upon and exited. Whatever, an error is in order.
   117 	 */
   118 
   119       Tcl_MutexUnlock (&joinMutex);
   120       return TCL_ERROR;
   121     }
   122 
   123     /* [1] If we don't lock the structure before giving up exclusive access
   124      * to the list some other thread just completing its wait on the same
   125      * thread can delete the structure from under us, leaving us with a
   126      * dangling pointer.
   127      */
   128 
   129     Tcl_MutexLock   (&threadPtr->threadMutex);
   130     Tcl_MutexUnlock (&joinMutex);
   131 
   132     /* [2] Now that we have the structure mutex any other thread that just
   133      * tries to delete structure will wait at location [3] until we are
   134      * done with the structure. And in that case we are done with it
   135      * rather quickly as 'waitedUpon' will be set and we will have to
   136      * error out.
   137      */
   138 
   139     if (threadPtr->waitedUpon) {
   140         Tcl_MutexUnlock (&threadPtr->threadMutex);
   141 	return TCL_ERROR;
   142     }
   143 
   144     /* We are waiting now, let other threads recognize this
   145      */
   146 
   147     threadPtr->waitedUpon = 1;
   148 
   149     while (!threadPtr->done) {
   150       Tcl_ConditionWait (&threadPtr->cond, &threadPtr->threadMutex, NULL);
   151     }
   152 
   153     /* We have to release the structure before trying to access the list
   154      * again or we can run into deadlock with a thread at [1] (see above)
   155      * because of us holding the structure and the other holding the list.
   156      * There is no problem with dangling pointers here as 'waitedUpon == 1'
   157      * is still valid and any other thread will error out and not come to
   158      * this place. IOW, the fact that we are here also means that no other
   159      * thread came here before us and is able to delete the structure.
   160      */
   161 
   162     Tcl_MutexUnlock (&threadPtr->threadMutex);
   163     Tcl_MutexLock   (&joinMutex);
   164 
   165     /* We have to search the list again as its structure may (may, almost
   166      * certainly) have changed while we were waiting. Especially now is the
   167      * time to compute the predecessor in the list. Any earlier result can
   168      * be dangling by now.
   169      */
   170 
   171     if (firstThreadPtr == threadPtr) {
   172         firstThreadPtr = threadPtr->nextThreadPtr;
   173     } else {
   174         JoinableThread* prevThreadPtr;
   175 
   176 	for (prevThreadPtr = firstThreadPtr;
   177 	     prevThreadPtr->nextThreadPtr != threadPtr;
   178 	     prevThreadPtr = prevThreadPtr->nextThreadPtr)
   179 	    /* empty body */
   180 	  ;
   181 
   182 	prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
   183     }
   184 
   185     Tcl_MutexUnlock (&joinMutex);
   186 
   187     /* [3] Now that the structure is not part of the list anymore no other
   188      * thread can acquire its mutex from now on. But it is possible that
   189      * another thread is still holding the mutex though, see location [2].
   190      * So we have to acquire the mutex one more time to wait for that thread
   191      * to finish. We can (and have to) release the mutex immediately.
   192      */
   193 
   194     Tcl_MutexLock   (&threadPtr->threadMutex);
   195     Tcl_MutexUnlock (&threadPtr->threadMutex);
   196 
   197     /* Copy the result to us, finalize the synchronisation objects, then
   198      * free the structure and return.
   199      */
   200 
   201     *result = threadPtr->result;
   202 
   203     Tcl_ConditionFinalize (&threadPtr->cond);
   204     Tcl_MutexFinalize (&threadPtr->threadMutex);
   205     ckfree ((VOID*) threadPtr);
   206 
   207     return TCL_OK;
   208 }
   209 
   210 /*
   211  *----------------------------------------------------------------------
   212  *
   213  * TclRememberJoinableThread --
   214  *
   215  *	This procedure remebers a thread as joinable. Only a call to
   216  *	TclJoinThread will remove the structre created (and initialized)
   217  *	here. IOW, not waiting upon a joinable thread will cause memory
   218  *	leaks.
   219  *
   220  * Results:
   221  *	None.
   222  *
   223  * Side effects:
   224  *	Allocates memory, adds it to the global list of all joinable
   225  *	threads.
   226  *
   227  *----------------------------------------------------------------------
   228  */
   229 
   230 VOID
   231 TclRememberJoinableThread(id)
   232     Tcl_ThreadId id; /* The thread to remember as joinable */
   233 {
   234     JoinableThread* threadPtr;
   235 
   236     threadPtr = (JoinableThread*) ckalloc (sizeof (JoinableThread));
   237     threadPtr->id          = id;
   238     threadPtr->done        = 0;
   239     threadPtr->waitedUpon  = 0;
   240     threadPtr->threadMutex = (Tcl_Mutex) NULL;
   241     threadPtr->cond        = (Tcl_Condition) NULL;
   242 
   243     Tcl_MutexLock (&joinMutex);
   244 
   245     threadPtr->nextThreadPtr = firstThreadPtr;
   246     firstThreadPtr           = threadPtr;
   247 
   248     Tcl_MutexUnlock (&joinMutex);
   249 }
   250 
   251 /*
   252  *----------------------------------------------------------------------
   253  *
   254  * TclSignalExitThread --
   255  *
   256  *	This procedure signals that the specified thread is done with
   257  *	its work. If the thread is joinable this signal is propagated
   258  *	to the thread waiting upon it.
   259  *
   260  * Results:
   261  *	None.
   262  *
   263  * Side effects:
   264  *	Modifies the associated structure to hold the result.
   265  *
   266  *----------------------------------------------------------------------
   267  */
   268 
   269 VOID
   270 TclSignalExitThread(id,result)
   271     Tcl_ThreadId id;     /* Id of the thread signaling its exit */
   272     int          result; /* The result from the thread */
   273 {
   274     JoinableThread* threadPtr;
   275 
   276     Tcl_MutexLock (&joinMutex);
   277 
   278     for (threadPtr = firstThreadPtr;
   279 	 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
   280 	 threadPtr = threadPtr->nextThreadPtr)
   281         /* empty body */
   282       ;
   283 
   284     if (threadPtr == (JoinableThread*) NULL) {
   285         /* Thread not found. Not joinable. No problem, nothing to do.
   286 	 */
   287 
   288         Tcl_MutexUnlock (&joinMutex);
   289 	return;
   290     }
   291 
   292     /* Switch over the exclusive access from the list to the structure,
   293      * then store the result, set the flag and notify the waiting thread,
   294      * provided that it exists. The order of lock/unlock ensures that a
   295      * thread entering 'TclJoinThread' will not interfere with us.
   296      */
   297 
   298     Tcl_MutexLock   (&threadPtr->threadMutex);
   299     Tcl_MutexUnlock (&joinMutex);
   300 
   301     threadPtr->done   = 1;
   302     threadPtr->result = result;
   303 
   304     if (threadPtr->waitedUpon) {
   305       Tcl_ConditionNotify (&threadPtr->cond);
   306     }
   307 
   308     Tcl_MutexUnlock (&threadPtr->threadMutex);
   309 }
   310 
   311 #endif /* WIN32 || MAC_TCL */