Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclThreadJoin.c @ 25

Last change on this file since 25 was 25, checked in by landauf, 16 years ago

added tcl to libs

File size: 9.3 KB
Line 
1/*
2 * tclThreadJoin.c --
3 *
4 *      This file implements a platform independent emulation layer for the
5 *      handling of joinable threads. The Windows platform uses this code to
6 *      provide the functionality of joining threads.  This code is currently
7 *      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 of
12 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tclThreadJoin.c,v 1.7 2005/11/07 15:15:06 dkf Exp $
15 */
16
17#include "tclInt.h"
18
19#ifdef WIN32
20
21/*
22 * The information about each joinable thread is remembered in a structure as
23 * defined below.
24 */
25
26typedef struct JoinableThread {
27    Tcl_ThreadId  id;           /* The id of the joinable thread. */
28    int result;                 /* A place for the result after the demise of
29                                 * the thread. */
30    int done;                   /* Boolean flag. Initialized to 0 and set to 1
31                                 * after the exit of the thread. This allows a
32                                 * thread requesting a join to detect when
33                                 * waiting is not necessary. */
34    int waitedUpon;             /* Boolean flag. Initialized to 0 and set to 1
35                                 * by the thread waiting for this one via
36                                 * Tcl_JoinThread.  Used to lock any other
37                                 * thread trying to wait on this one. */
38    Tcl_Mutex threadMutex;      /* The mutex used to serialize access to this
39                                 * structure. */
40    Tcl_Condition cond;         /* This is the condition a thread has to wait
41                                 * upon to get notified of the end of the
42                                 * described thread. It is signaled indirectly
43                                 * by Tcl_ExitThread. */
44    struct JoinableThread *nextThreadPtr;
45                                /* Reference to the next thread in the list of
46                                 * joinable threads. */
47} JoinableThread;
48
49/*
50 * The following variable is used to maintain the global list of all joinable
51 * threads. Usage by a thread is allowed only if the thread acquired the
52 * 'joinMutex'.
53 */
54
55TCL_DECLARE_MUTEX(joinMutex)
56
57static JoinableThread* firstThreadPtr;
58
59/*
60 *----------------------------------------------------------------------
61 *
62 * TclJoinThread --
63 *
64 *      This procedure waits for the exit of the thread with the specified id
65 *      and returns its result.
66 *
67 * Results:
68 *      A standard tcl result signaling the overall success/failure of the
69 *      operation and an integer result delivered by the thread which was
70 *      waited upon.
71 *
72 * Side effects:
73 *      Deallocates the memory allocated by TclRememberJoinableThread.
74 *      Removes the data associated to the thread waited upon from the list of
75 *      joinable threads.
76 *
77 *----------------------------------------------------------------------
78 */
79
80int
81TclJoinThread(
82    Tcl_ThreadId id,            /* The id of the thread to wait upon. */
83    int *result)                /* Reference to a location for the result of
84                                 * the thread we are waiting upon. */
85{
86    JoinableThread *threadPtr;
87
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    Tcl_MutexLock(&joinMutex);
105
106    threadPtr = firstThreadPtr;
107    while (threadPtr!=NULL && threadPtr->id!=id) {
108        threadPtr = threadPtr->nextThreadPtr;
109    }
110
111    if (threadPtr == NULL) {
112        /*
113         * Thread not found. Either not joinable, or already waited upon and
114         * exited. Whatever, an error is in order.
115         */
116
117        Tcl_MutexUnlock(&joinMutex);
118        return TCL_ERROR;
119    }
120
121    /*
122     * [1] If we don't lock the structure before giving up exclusive access to
123     * the list some other thread just completing its wait on the same thread
124     * can delete the structure from under us, leaving us with a dangling
125     * pointer.
126     */
127
128    Tcl_MutexLock(&threadPtr->threadMutex);
129    Tcl_MutexUnlock(&joinMutex);
130
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 done
134     * with the structure. And in that case we are done with it rather quickly
135     * as 'waitedUpon' will be set and we will have to error out.
136     */
137
138    if (threadPtr->waitedUpon) {
139        Tcl_MutexUnlock(&threadPtr->threadMutex);
140        return TCL_ERROR;
141    }
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    /*
154     * We have to release the structure before trying to access the list again
155     * or we can run into deadlock with a thread at [1] (see above) because of
156     * us holding the structure and the other holding the list.  There is no
157     * problem with dangling pointers here as 'waitedUpon == 1' is still valid
158     * and any other thread will error out and not come to this place. IOW,
159     * the fact that we are here also means that no other thread came here
160     * before us and is able to delete the structure.
161     */
162
163    Tcl_MutexUnlock(&threadPtr->threadMutex);
164    Tcl_MutexLock(&joinMutex);
165
166    /*
167     * We have to search the list again as its structure may (may, almost
168     * certainly) have changed while we were waiting. Especially now is the
169     * time to compute the predecessor in the list. Any earlier result can be
170     * dangling by now.
171     */
172
173    if (firstThreadPtr == threadPtr) {
174        firstThreadPtr = threadPtr->nextThreadPtr;
175    } else {
176        JoinableThread *prevThreadPtr = firstThreadPtr;
177
178        while (prevThreadPtr->nextThreadPtr != threadPtr) {
179            prevThreadPtr = prevThreadPtr->nextThreadPtr;
180        }
181        prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
182    }
183
184    Tcl_MutexUnlock(&joinMutex);
185
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].  So
190     * we have to acquire the mutex one more time to wait for that thread to
191     * finish. We can (and have to) release the mutex immediately.
192     */
193
194    Tcl_MutexLock(&threadPtr->threadMutex);
195    Tcl_MutexUnlock(&threadPtr->threadMutex);
196
197    /*
198     * Copy the result to us, finalize the synchronisation objects, then free
199     * the structure and return.
200     */
201
202    *result = threadPtr->result;
203
204    Tcl_ConditionFinalize(&threadPtr->cond);
205    Tcl_MutexFinalize(&threadPtr->threadMutex);
206    ckfree((char *) threadPtr);
207
208    return TCL_OK;
209}
210
211/*
212 *----------------------------------------------------------------------
213 *
214 * TclRememberJoinableThread --
215 *
216 *      This procedure remebers a thread as joinable. Only a call to
217 *      TclJoinThread will remove the structre created (and initialized) here.
218 *      IOW, not waiting upon a joinable thread will cause memory leaks.
219 *
220 * Results:
221 *      None.
222 *
223 * Side effects:
224 *      Allocates memory, adds it to the global list of all joinable threads.
225 *
226 *----------------------------------------------------------------------
227 */
228
229void
230TclRememberJoinableThread(
231    Tcl_ThreadId id)            /* The thread to remember as joinable */
232{
233    JoinableThread *threadPtr;
234
235    threadPtr = (JoinableThread *) ckalloc(sizeof(JoinableThread));
236    threadPtr->id = id;
237    threadPtr->done = 0;
238    threadPtr->waitedUpon = 0;
239    threadPtr->threadMutex = (Tcl_Mutex) NULL;
240    threadPtr->cond = (Tcl_Condition) NULL;
241
242    Tcl_MutexLock(&joinMutex);
243
244    threadPtr->nextThreadPtr = firstThreadPtr;
245    firstThreadPtr = threadPtr;
246
247    Tcl_MutexUnlock(&joinMutex);
248}
249
250/*
251 *----------------------------------------------------------------------
252 *
253 * TclSignalExitThread --
254 *
255 *      This procedure signals that the specified thread is done with its
256 *      work. If the thread is joinable this signal is propagated to the
257 *      thread waiting upon it.
258 *
259 * Results:
260 *      None.
261 *
262 * Side effects:
263 *      Modifies the associated structure to hold the result.
264 *
265 *----------------------------------------------------------------------
266 */
267
268void
269TclSignalExitThread(
270    Tcl_ThreadId id,            /* Id of the thread signaling its exit. */
271    int result)                 /* The result from the thread. */
272{
273    JoinableThread *threadPtr;
274
275    Tcl_MutexLock(&joinMutex);
276
277    threadPtr = firstThreadPtr;
278    while ((threadPtr != NULL) && (threadPtr->id != id)) {
279        threadPtr = threadPtr->nextThreadPtr;
280    }
281
282    if (threadPtr == NULL) {
283        /*
284         * Thread not found. Not joinable. No problem, nothing to do.
285         */
286
287        Tcl_MutexUnlock(&joinMutex);
288        return;
289    }
290
291    /*
292     * Switch over the exclusive access from the list to the structure, then
293     * store the result, set the flag and notify the waiting thread, provided
294     * that it exists. The order of lock/unlock ensures that a thread entering
295     * '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#endif /* WIN32 */
311
312/*
313 * Local Variables:
314 * mode: c
315 * c-basic-offset: 4
316 * fill-column: 78
317 * End:
318 */
Note: See TracBrowser for help on using the repository browser.