OSDN Git Service

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