os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclThreadJoin.c
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 */