os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclThreadJoin.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclThreadJoin.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,311 @@
     1.4 +/* 
     1.5 + * tclThreadJoin.c --
     1.6 + *
     1.7 + *	This file implements a platform independent emulation layer for
     1.8 + *	the handling of joinable threads. The Mac and Windows platforms
     1.9 + *	use this code to provide the functionality of joining threads.
    1.10 + *	This code is currently not necessary on Unix.
    1.11 + *
    1.12 + * Copyright (c) 2000 by Scriptics Corporation
    1.13 + *
    1.14 + * See the file "license.terms" for information on usage and redistribution
    1.15 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.16 + *
    1.17 + * RCS: @(#) $Id: tclThreadJoin.c,v 1.4 2002/04/24 20:35:40 hobbs Exp $
    1.18 + */
    1.19 +
    1.20 +#include "tclInt.h"
    1.21 +
    1.22 +#if defined(WIN32) || defined(MAC_TCL)
    1.23 +
    1.24 +/* The information about each joinable thread is remembered in a
    1.25 + * structure as defined below.
    1.26 + */
    1.27 +
    1.28 +typedef struct JoinableThread {
    1.29 +  Tcl_ThreadId  id;                     /* The id of the joinable thread */
    1.30 +  int           result;                 /* A place for the result after the
    1.31 +					 * demise of the thread */
    1.32 +  int           done;                   /* Boolean flag. Initialized to 0
    1.33 +					 * and set to 1 after the exit of
    1.34 +					 * the thread. This allows a thread
    1.35 +					 * requesting a join to detect when
    1.36 +					 * waiting is not necessary. */
    1.37 +  int           waitedUpon;             /* Boolean flag. Initialized to 0
    1.38 +					 * and set to 1 by the thread waiting
    1.39 +					 * for this one via Tcl_JoinThread.
    1.40 +					 * Used to lock any other thread
    1.41 +					 * trying to wait on this one.
    1.42 +					 */
    1.43 +  Tcl_Mutex     threadMutex;            /* The mutex used to serialize access
    1.44 +					 * to this structure. */
    1.45 +  Tcl_Condition cond;                   /* This is the condition a thread has
    1.46 +					 * to wait upon to get notified of the
    1.47 +					 * end of the described thread. It is
    1.48 +					 * signaled indirectly by
    1.49 +					 * Tcl_ExitThread. */
    1.50 +  struct JoinableThread* nextThreadPtr; /* Reference to the next thread in the
    1.51 +					 * list of joinable threads */
    1.52 +} JoinableThread;
    1.53 +
    1.54 +/* The following variable is used to maintain the global list of all
    1.55 + * joinable threads. Usage by a thread is allowed only if the
    1.56 + * thread acquired the 'joinMutex'.
    1.57 + */
    1.58 +
    1.59 +TCL_DECLARE_MUTEX(joinMutex)
    1.60 +
    1.61 +static JoinableThread* firstThreadPtr;
    1.62 +
    1.63 +
    1.64 +
    1.65 +/*
    1.66 + *----------------------------------------------------------------------
    1.67 + *
    1.68 + * TclJoinThread --
    1.69 + *
    1.70 + *	This procedure waits for the exit of the thread with the specified
    1.71 + *	id and returns its result.
    1.72 + *
    1.73 + * Results:
    1.74 + *	A standard tcl result signaling the overall success/failure of the
    1.75 + *	operation and an integer result delivered by the thread which was
    1.76 + *	waited upon.
    1.77 + *
    1.78 + * Side effects:
    1.79 + *	Deallocates the memory allocated by TclRememberJoinableThread.
    1.80 + *	Removes the data associated to the thread waited upon from the
    1.81 + *	list of joinable threads.
    1.82 + *
    1.83 + *----------------------------------------------------------------------
    1.84 + */
    1.85 +
    1.86 +int
    1.87 +TclJoinThread(id, result)
    1.88 +    Tcl_ThreadId id;     /* The id of the thread to wait upon. */
    1.89 +    int*         result; /* Reference to a location for the result
    1.90 +			  * of the thread we are waiting upon. */
    1.91 +{
    1.92 +    /* Steps done here:
    1.93 +     * i.    Acquire the joinMutex and search for the thread.
    1.94 +     * ii.   Error out if it could not be found.
    1.95 +     * iii.  If found, switch from exclusive access to the list to exclusive
    1.96 +     *       access to the thread structure.
    1.97 +     * iv.   Error out if some other is already waiting.
    1.98 +     * v.    Skip the waiting part of the thread is already done.
    1.99 +     * vi.   Wait for the thread to exit, mark it as waited upon too.
   1.100 +     * vii.  Get the result form the structure, 
   1.101 +     * viii. switch to exclusive access of the list,
   1.102 +     * ix.   remove the structure from the list,
   1.103 +     * x.    then switch back to exclusive access to the structure
   1.104 +     * xi.   and delete it.
   1.105 +     */
   1.106 +
   1.107 +    JoinableThread* threadPtr;
   1.108 +
   1.109 +    Tcl_MutexLock (&joinMutex);
   1.110 +
   1.111 +    for (threadPtr = firstThreadPtr;
   1.112 +	 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
   1.113 +	 threadPtr = threadPtr->nextThreadPtr)
   1.114 +        /* empty body */
   1.115 +      ;
   1.116 +
   1.117 +    if (threadPtr == (JoinableThread*) NULL) {
   1.118 +        /* Thread not found. Either not joinable, or already waited
   1.119 +	 * upon and exited. Whatever, an error is in order.
   1.120 +	 */
   1.121 +
   1.122 +      Tcl_MutexUnlock (&joinMutex);
   1.123 +      return TCL_ERROR;
   1.124 +    }
   1.125 +
   1.126 +    /* [1] If we don't lock the structure before giving up exclusive access
   1.127 +     * to the list some other thread just completing its wait on the same
   1.128 +     * thread can delete the structure from under us, leaving us with a
   1.129 +     * dangling pointer.
   1.130 +     */
   1.131 +
   1.132 +    Tcl_MutexLock   (&threadPtr->threadMutex);
   1.133 +    Tcl_MutexUnlock (&joinMutex);
   1.134 +
   1.135 +    /* [2] Now that we have the structure mutex any other thread that just
   1.136 +     * tries to delete structure will wait at location [3] until we are
   1.137 +     * done with the structure. And in that case we are done with it
   1.138 +     * rather quickly as 'waitedUpon' will be set and we will have to
   1.139 +     * error out.
   1.140 +     */
   1.141 +
   1.142 +    if (threadPtr->waitedUpon) {
   1.143 +        Tcl_MutexUnlock (&threadPtr->threadMutex);
   1.144 +	return TCL_ERROR;
   1.145 +    }
   1.146 +
   1.147 +    /* We are waiting now, let other threads recognize this
   1.148 +     */
   1.149 +
   1.150 +    threadPtr->waitedUpon = 1;
   1.151 +
   1.152 +    while (!threadPtr->done) {
   1.153 +      Tcl_ConditionWait (&threadPtr->cond, &threadPtr->threadMutex, NULL);
   1.154 +    }
   1.155 +
   1.156 +    /* We have to release the structure before trying to access the list
   1.157 +     * again or we can run into deadlock with a thread at [1] (see above)
   1.158 +     * because of us holding the structure and the other holding the list.
   1.159 +     * There is no problem with dangling pointers here as 'waitedUpon == 1'
   1.160 +     * is still valid and any other thread will error out and not come to
   1.161 +     * this place. IOW, the fact that we are here also means that no other
   1.162 +     * thread came here before us and is able to delete the structure.
   1.163 +     */
   1.164 +
   1.165 +    Tcl_MutexUnlock (&threadPtr->threadMutex);
   1.166 +    Tcl_MutexLock   (&joinMutex);
   1.167 +
   1.168 +    /* We have to search the list again as its structure may (may, almost
   1.169 +     * certainly) have changed while we were waiting. Especially now is the
   1.170 +     * time to compute the predecessor in the list. Any earlier result can
   1.171 +     * be dangling by now.
   1.172 +     */
   1.173 +
   1.174 +    if (firstThreadPtr == threadPtr) {
   1.175 +        firstThreadPtr = threadPtr->nextThreadPtr;
   1.176 +    } else {
   1.177 +        JoinableThread* prevThreadPtr;
   1.178 +
   1.179 +	for (prevThreadPtr = firstThreadPtr;
   1.180 +	     prevThreadPtr->nextThreadPtr != threadPtr;
   1.181 +	     prevThreadPtr = prevThreadPtr->nextThreadPtr)
   1.182 +	    /* empty body */
   1.183 +	  ;
   1.184 +
   1.185 +	prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
   1.186 +    }
   1.187 +
   1.188 +    Tcl_MutexUnlock (&joinMutex);
   1.189 +
   1.190 +    /* [3] Now that the structure is not part of the list anymore no other
   1.191 +     * thread can acquire its mutex from now on. But it is possible that
   1.192 +     * another thread is still holding the mutex though, see location [2].
   1.193 +     * So we have to acquire the mutex one more time to wait for that thread
   1.194 +     * to finish. We can (and have to) release the mutex immediately.
   1.195 +     */
   1.196 +
   1.197 +    Tcl_MutexLock   (&threadPtr->threadMutex);
   1.198 +    Tcl_MutexUnlock (&threadPtr->threadMutex);
   1.199 +
   1.200 +    /* Copy the result to us, finalize the synchronisation objects, then
   1.201 +     * free the structure and return.
   1.202 +     */
   1.203 +
   1.204 +    *result = threadPtr->result;
   1.205 +
   1.206 +    Tcl_ConditionFinalize (&threadPtr->cond);
   1.207 +    Tcl_MutexFinalize (&threadPtr->threadMutex);
   1.208 +    ckfree ((VOID*) threadPtr);
   1.209 +
   1.210 +    return TCL_OK;
   1.211 +}
   1.212 +
   1.213 +/*
   1.214 + *----------------------------------------------------------------------
   1.215 + *
   1.216 + * TclRememberJoinableThread --
   1.217 + *
   1.218 + *	This procedure remebers a thread as joinable. Only a call to
   1.219 + *	TclJoinThread will remove the structre created (and initialized)
   1.220 + *	here. IOW, not waiting upon a joinable thread will cause memory
   1.221 + *	leaks.
   1.222 + *
   1.223 + * Results:
   1.224 + *	None.
   1.225 + *
   1.226 + * Side effects:
   1.227 + *	Allocates memory, adds it to the global list of all joinable
   1.228 + *	threads.
   1.229 + *
   1.230 + *----------------------------------------------------------------------
   1.231 + */
   1.232 +
   1.233 +VOID
   1.234 +TclRememberJoinableThread(id)
   1.235 +    Tcl_ThreadId id; /* The thread to remember as joinable */
   1.236 +{
   1.237 +    JoinableThread* threadPtr;
   1.238 +
   1.239 +    threadPtr = (JoinableThread*) ckalloc (sizeof (JoinableThread));
   1.240 +    threadPtr->id          = id;
   1.241 +    threadPtr->done        = 0;
   1.242 +    threadPtr->waitedUpon  = 0;
   1.243 +    threadPtr->threadMutex = (Tcl_Mutex) NULL;
   1.244 +    threadPtr->cond        = (Tcl_Condition) NULL;
   1.245 +
   1.246 +    Tcl_MutexLock (&joinMutex);
   1.247 +
   1.248 +    threadPtr->nextThreadPtr = firstThreadPtr;
   1.249 +    firstThreadPtr           = threadPtr;
   1.250 +
   1.251 +    Tcl_MutexUnlock (&joinMutex);
   1.252 +}
   1.253 +
   1.254 +/*
   1.255 + *----------------------------------------------------------------------
   1.256 + *
   1.257 + * TclSignalExitThread --
   1.258 + *
   1.259 + *	This procedure signals that the specified thread is done with
   1.260 + *	its work. If the thread is joinable this signal is propagated
   1.261 + *	to the thread waiting upon it.
   1.262 + *
   1.263 + * Results:
   1.264 + *	None.
   1.265 + *
   1.266 + * Side effects:
   1.267 + *	Modifies the associated structure to hold the result.
   1.268 + *
   1.269 + *----------------------------------------------------------------------
   1.270 + */
   1.271 +
   1.272 +VOID
   1.273 +TclSignalExitThread(id,result)
   1.274 +    Tcl_ThreadId id;     /* Id of the thread signaling its exit */
   1.275 +    int          result; /* The result from the thread */
   1.276 +{
   1.277 +    JoinableThread* threadPtr;
   1.278 +
   1.279 +    Tcl_MutexLock (&joinMutex);
   1.280 +
   1.281 +    for (threadPtr = firstThreadPtr;
   1.282 +	 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
   1.283 +	 threadPtr = threadPtr->nextThreadPtr)
   1.284 +        /* empty body */
   1.285 +      ;
   1.286 +
   1.287 +    if (threadPtr == (JoinableThread*) NULL) {
   1.288 +        /* Thread not found. Not joinable. No problem, nothing to do.
   1.289 +	 */
   1.290 +
   1.291 +        Tcl_MutexUnlock (&joinMutex);
   1.292 +	return;
   1.293 +    }
   1.294 +
   1.295 +    /* Switch over the exclusive access from the list to the structure,
   1.296 +     * then store the result, set the flag and notify the waiting thread,
   1.297 +     * provided that it exists. The order of lock/unlock ensures that a
   1.298 +     * thread entering 'TclJoinThread' will not interfere with us.
   1.299 +     */
   1.300 +
   1.301 +    Tcl_MutexLock   (&threadPtr->threadMutex);
   1.302 +    Tcl_MutexUnlock (&joinMutex);
   1.303 +
   1.304 +    threadPtr->done   = 1;
   1.305 +    threadPtr->result = result;
   1.306 +
   1.307 +    if (threadPtr->waitedUpon) {
   1.308 +      Tcl_ConditionNotify (&threadPtr->cond);
   1.309 +    }
   1.310 +
   1.311 +    Tcl_MutexUnlock (&threadPtr->threadMutex);
   1.312 +}
   1.313 +
   1.314 +#endif /* WIN32 || MAC_TCL */