os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclThreadJoin.c
Update contrib.
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.
9 * Copyright (c) 2000 by Scriptics Corporation
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 * RCS: @(#) $Id: tclThreadJoin.c,v 1.4 2002/04/24 20:35:40 hobbs Exp $
19 #if defined(WIN32) || defined(MAC_TCL)
21 /* The information about each joinable thread is remembered in a
22 * structure as defined below.
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.
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
47 struct JoinableThread* nextThreadPtr; /* Reference to the next thread in the
48 * list of joinable threads */
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'.
56 TCL_DECLARE_MUTEX(joinMutex)
58 static JoinableThread* firstThreadPtr;
63 *----------------------------------------------------------------------
67 * This procedure waits for the exit of the thread with the specified
68 * id and returns its result.
71 * A standard tcl result signaling the overall success/failure of the
72 * operation and an integer result delivered by the thread which was
76 * Deallocates the memory allocated by TclRememberJoinableThread.
77 * Removes the data associated to the thread waited upon from the
78 * list of joinable threads.
80 *----------------------------------------------------------------------
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. */
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
104 JoinableThread* threadPtr;
106 Tcl_MutexLock (&joinMutex);
108 for (threadPtr = firstThreadPtr;
109 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
110 threadPtr = threadPtr->nextThreadPtr)
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.
119 Tcl_MutexUnlock (&joinMutex);
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
129 Tcl_MutexLock (&threadPtr->threadMutex);
130 Tcl_MutexUnlock (&joinMutex);
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
139 if (threadPtr->waitedUpon) {
140 Tcl_MutexUnlock (&threadPtr->threadMutex);
144 /* We are waiting now, let other threads recognize this
147 threadPtr->waitedUpon = 1;
149 while (!threadPtr->done) {
150 Tcl_ConditionWait (&threadPtr->cond, &threadPtr->threadMutex, NULL);
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.
162 Tcl_MutexUnlock (&threadPtr->threadMutex);
163 Tcl_MutexLock (&joinMutex);
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.
171 if (firstThreadPtr == threadPtr) {
172 firstThreadPtr = threadPtr->nextThreadPtr;
174 JoinableThread* prevThreadPtr;
176 for (prevThreadPtr = firstThreadPtr;
177 prevThreadPtr->nextThreadPtr != threadPtr;
178 prevThreadPtr = prevThreadPtr->nextThreadPtr)
182 prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
185 Tcl_MutexUnlock (&joinMutex);
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.
194 Tcl_MutexLock (&threadPtr->threadMutex);
195 Tcl_MutexUnlock (&threadPtr->threadMutex);
197 /* Copy the result to us, finalize the synchronisation objects, then
198 * free the structure and return.
201 *result = threadPtr->result;
203 Tcl_ConditionFinalize (&threadPtr->cond);
204 Tcl_MutexFinalize (&threadPtr->threadMutex);
205 ckfree ((VOID*) threadPtr);
211 *----------------------------------------------------------------------
213 * TclRememberJoinableThread --
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
224 * Allocates memory, adds it to the global list of all joinable
227 *----------------------------------------------------------------------
231 TclRememberJoinableThread(id)
232 Tcl_ThreadId id; /* The thread to remember as joinable */
234 JoinableThread* threadPtr;
236 threadPtr = (JoinableThread*) ckalloc (sizeof (JoinableThread));
239 threadPtr->waitedUpon = 0;
240 threadPtr->threadMutex = (Tcl_Mutex) NULL;
241 threadPtr->cond = (Tcl_Condition) NULL;
243 Tcl_MutexLock (&joinMutex);
245 threadPtr->nextThreadPtr = firstThreadPtr;
246 firstThreadPtr = threadPtr;
248 Tcl_MutexUnlock (&joinMutex);
252 *----------------------------------------------------------------------
254 * TclSignalExitThread --
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.
264 * Modifies the associated structure to hold the result.
266 *----------------------------------------------------------------------
270 TclSignalExitThread(id,result)
271 Tcl_ThreadId id; /* Id of the thread signaling its exit */
272 int result; /* The result from the thread */
274 JoinableThread* threadPtr;
276 Tcl_MutexLock (&joinMutex);
278 for (threadPtr = firstThreadPtr;
279 (threadPtr != (JoinableThread*) NULL) && (threadPtr->id != id);
280 threadPtr = threadPtr->nextThreadPtr)
284 if (threadPtr == (JoinableThread*) NULL) {
285 /* Thread not found. Not joinable. No problem, nothing to do.
288 Tcl_MutexUnlock (&joinMutex);
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.
298 Tcl_MutexLock (&threadPtr->threadMutex);
299 Tcl_MutexUnlock (&joinMutex);
302 threadPtr->result = result;
304 if (threadPtr->waitedUpon) {
305 Tcl_ConditionNotify (&threadPtr->cond);
308 Tcl_MutexUnlock (&threadPtr->threadMutex);
311 #endif /* WIN32 || MAC_TCL */