1 /*
2 * tclThread.c --
3 *
4 * This file implements Platform independent thread operations.
5 * Most of the real work is done in the platform dependent files.
6 *
7 * Copyright (c) 1998 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclThread.c,v 1.6 2002/12/10 00:34:15 hobbs Exp $
13 */
14
15 #include "tclInt.h"
16
17 /*
18 * There are three classes of synchronization objects:
19 * mutexes, thread data keys, and condition variables.
20 * The following are used to record the memory used for these
21 * objects so they can be finalized.
22 *
23 * These statics are guarded by the mutex in the caller of
24 * TclRememberThreadData, e.g., TclpThreadDataKeyInit
25 */
26
27 typedef struct {
28 int num; /* Number of objects remembered */
29 int max; /* Max size of the array */
30 char **list; /* List of pointers */
31 } SyncObjRecord;
32
33 static SyncObjRecord keyRecord;
34 static SyncObjRecord mutexRecord;
35 static SyncObjRecord condRecord;
36
37 /*
38 * Prototypes of functions used only in this file
39 */
40
41 static void RememberSyncObject _ANSI_ARGS_((char *objPtr,
42 SyncObjRecord *recPtr));
43 static void ForgetSyncObject _ANSI_ARGS_((char *objPtr,
44 SyncObjRecord *recPtr));
45
46 /*
47 * Several functions are #defined to nothing in tcl.h if TCL_THREADS is not
48 * specified. Here we undo that so the procedures are defined in the
49 * stubs table.
50 */
51 #ifndef TCL_THREADS
52 #undef Tcl_MutexLock
53 #undef Tcl_MutexUnlock
54 #undef Tcl_MutexFinalize
55 #undef Tcl_ConditionNotify
56 #undef Tcl_ConditionWait
57 #undef Tcl_ConditionFinalize
58 #endif
59
60
61 /*
62 *----------------------------------------------------------------------
63 *
64 * Tcl_GetThreadData --
65 *
66 * This procedure allocates and initializes a chunk of thread
67 * local storage.
68 *
69 * Results:
70 * A thread-specific pointer to the data structure.
71 *
72 * Side effects:
73 * Will allocate memory the first time this thread calls for
74 * this chunk of storage.
75 *
76 *----------------------------------------------------------------------
77 */
78
79 VOID *
Tcl_GetThreadData(keyPtr,size)80 Tcl_GetThreadData(keyPtr, size)
81 Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk */
82 int size; /* Size of storage block */
83 {
84 VOID *result;
85 #ifdef TCL_THREADS
86
87 /*
88 * See if this is the first thread to init this key.
89 */
90
91 if (*keyPtr == NULL) {
92 TclpThreadDataKeyInit(keyPtr);
93 }
94
95 /*
96 * Initialize the key for this thread.
97 */
98
99 result = TclpThreadDataKeyGet(keyPtr);
100 if (result == NULL) {
101 result = (VOID *)ckalloc((size_t)size);
102 memset(result, 0, (size_t)size);
103 TclpThreadDataKeySet(keyPtr, result);
104 }
105 #else
106 if (*keyPtr == NULL) {
107 result = (VOID *)ckalloc((size_t)size);
108 memset((char *)result, 0, (size_t)size);
109 *keyPtr = (Tcl_ThreadDataKey)result;
110 TclRememberDataKey(keyPtr);
111 }
112 result = *(VOID **)keyPtr;
113 #endif
114 return result;
115 }
116
117 /*
118 *----------------------------------------------------------------------
119 *
120 * TclThreadDataKeyGet --
121 *
122 * This procedure returns a pointer to a block of thread local storage.
123 *
124 * Results:
125 * A thread-specific pointer to the data structure, or NULL
126 * if the memory has not been assigned to this key for this thread.
127 *
128 * Side effects:
129 * None.
130 *
131 *----------------------------------------------------------------------
132 */
133
134 VOID *
TclThreadDataKeyGet(keyPtr)135 TclThreadDataKeyGet(keyPtr)
136 Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk,
137 * really (pthread_key_t **) */
138 {
139 #ifdef TCL_THREADS
140 return (VOID *)TclpThreadDataKeyGet(keyPtr);
141 #else
142 char *result = *(char **)keyPtr;
143 return (VOID *)result;
144 #endif /* TCL_THREADS */
145 }
146
147
148 /*
149 *----------------------------------------------------------------------
150 *
151 * TclThreadDataKeySet --
152 *
153 * This procedure sets a thread local storage pointer.
154 *
155 * Results:
156 * None.
157 *
158 * Side effects:
159 * The assigned value will be returned by TclpThreadDataKeyGet.
160 *
161 *----------------------------------------------------------------------
162 */
163
164 void
TclThreadDataKeySet(keyPtr,data)165 TclThreadDataKeySet(keyPtr, data)
166 Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk,
167 * really (pthread_key_t **) */
168 VOID *data; /* Thread local storage */
169 {
170 #ifdef TCL_THREADS
171 if (*keyPtr == NULL) {
172 TclpThreadDataKeyInit(keyPtr);
173 }
174 TclpThreadDataKeySet(keyPtr, data);
175 #else
176 *keyPtr = (Tcl_ThreadDataKey)data;
177 #endif /* TCL_THREADS */
178 }
179
180
181
182 /*
183 *----------------------------------------------------------------------
184 *
185 * RememberSyncObject
186 *
187 * Keep a list of (mutexes/condition variable/data key)
188 * used during finalization.
189 *
190 * Results:
191 * None.
192 *
193 * Side effects:
194 * Add to the appropriate list.
195 *
196 *----------------------------------------------------------------------
197 */
198
199 static void
RememberSyncObject(objPtr,recPtr)200 RememberSyncObject(objPtr, recPtr)
201 char *objPtr; /* Pointer to sync object */
202 SyncObjRecord *recPtr; /* Record of sync objects */
203 {
204 char **newList;
205 int i, j;
206
207 /*
208 * Save the pointer to the allocated object so it can be finalized.
209 * Grow the list of pointers if necessary, copying only non-NULL
210 * pointers to the new list.
211 */
212
213 if (recPtr->num >= recPtr->max) {
214 recPtr->max += 8;
215 newList = (char **)ckalloc(recPtr->max * sizeof(char *));
216 for (i=0,j=0 ; i<recPtr->num ; i++) {
217 if (recPtr->list[i] != NULL) {
218 newList[j++] = recPtr->list[i];
219 }
220 }
221 if (recPtr->list != NULL) {
222 ckfree((char *)recPtr->list);
223 }
224 recPtr->list = newList;
225 recPtr->num = j;
226 }
227 recPtr->list[recPtr->num] = objPtr;
228 recPtr->num++;
229 }
230
231 /*
232 *----------------------------------------------------------------------
233 *
234 * ForgetSyncObject
235 *
236 * Remove a single object from the list.
237 *
238 * Results:
239 * None.
240 *
241 * Side effects:
242 * Remove from the appropriate list.
243 *
244 *----------------------------------------------------------------------
245 */
246
247 static void
ForgetSyncObject(objPtr,recPtr)248 ForgetSyncObject(objPtr, recPtr)
249 char *objPtr; /* Pointer to sync object */
250 SyncObjRecord *recPtr; /* Record of sync objects */
251 {
252 int i;
253
254 for (i=0 ; i<recPtr->num ; i++) {
255 if (objPtr == recPtr->list[i]) {
256 recPtr->list[i] = NULL;
257 return;
258 }
259 }
260 }
261
262 /*
263 *----------------------------------------------------------------------
264 *
265 * TclRememberMutex
266 *
267 * Keep a list of mutexes used during finalization.
268 *
269 * Results:
270 * None.
271 *
272 * Side effects:
273 * Add to the mutex list.
274 *
275 *----------------------------------------------------------------------
276 */
277
278 void
TclRememberMutex(mutexPtr)279 TclRememberMutex(mutexPtr)
280 Tcl_Mutex *mutexPtr;
281 {
282 RememberSyncObject((char *)mutexPtr, &mutexRecord);
283 }
284
285 /*
286 *----------------------------------------------------------------------
287 *
288 * Tcl_MutexFinalize
289 *
290 * Finalize a single mutex and remove it from the
291 * list of remembered objects.
292 *
293 * Results:
294 * None.
295 *
296 * Side effects:
297 * Remove the mutex from the list.
298 *
299 *----------------------------------------------------------------------
300 */
301
302 void
Tcl_MutexFinalize(mutexPtr)303 Tcl_MutexFinalize(mutexPtr)
304 Tcl_Mutex *mutexPtr;
305 {
306 #ifdef TCL_THREADS
307 TclpFinalizeMutex(mutexPtr);
308 #endif
309 ForgetSyncObject((char *)mutexPtr, &mutexRecord);
310 }
311
312 /*
313 *----------------------------------------------------------------------
314 *
315 * TclRememberDataKey
316 *
317 * Keep a list of thread data keys used during finalization.
318 *
319 * Results:
320 * None.
321 *
322 * Side effects:
323 * Add to the key list.
324 *
325 *----------------------------------------------------------------------
326 */
327
328 void
TclRememberDataKey(keyPtr)329 TclRememberDataKey(keyPtr)
330 Tcl_ThreadDataKey *keyPtr;
331 {
332 RememberSyncObject((char *)keyPtr, &keyRecord);
333 }
334
335 /*
336 *----------------------------------------------------------------------
337 *
338 * TclRememberCondition
339 *
340 * Keep a list of condition variables used during finalization.
341 *
342 * Results:
343 * None.
344 *
345 * Side effects:
346 * Add to the condition variable list.
347 *
348 *----------------------------------------------------------------------
349 */
350
351 void
TclRememberCondition(condPtr)352 TclRememberCondition(condPtr)
353 Tcl_Condition *condPtr;
354 {
355 RememberSyncObject((char *)condPtr, &condRecord);
356 }
357
358 /*
359 *----------------------------------------------------------------------
360 *
361 * Tcl_ConditionFinalize
362 *
363 * Finalize a single condition variable and remove it from the
364 * list of remembered objects.
365 *
366 * Results:
367 * None.
368 *
369 * Side effects:
370 * Remove the condition variable from the list.
371 *
372 *----------------------------------------------------------------------
373 */
374
375 void
Tcl_ConditionFinalize(condPtr)376 Tcl_ConditionFinalize(condPtr)
377 Tcl_Condition *condPtr;
378 {
379 #ifdef TCL_THREADS
380 TclpFinalizeCondition(condPtr);
381 #endif
382 ForgetSyncObject((char *)condPtr, &condRecord);
383 }
384
385 /*
386 *----------------------------------------------------------------------
387 *
388 * TclFinalizeThreadData --
389 *
390 * This procedure cleans up the thread-local storage. This is
391 * called once for each thread.
392 *
393 * Results:
394 * None.
395 *
396 * Side effects:
397 * Frees up all thread local storage.
398 *
399 *----------------------------------------------------------------------
400 */
401
402 void
TclFinalizeThreadData()403 TclFinalizeThreadData()
404 {
405 int i;
406 Tcl_ThreadDataKey *keyPtr;
407
408 TclpMasterLock();
409 for (i=0 ; i<keyRecord.num ; i++) {
410 keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
411 #ifdef TCL_THREADS
412 TclpFinalizeThreadData(keyPtr);
413 #else
414 if (*keyPtr != NULL) {
415 ckfree((char *)*keyPtr);
416 *keyPtr = NULL;
417 }
418 #endif
419 }
420 TclpMasterUnlock();
421 }
422
423 /*
424 *----------------------------------------------------------------------
425 *
426 * TclFinalizeSynchronization --
427 *
428 * This procedure cleans up all synchronization objects:
429 * mutexes, condition variables, and thread-local storage.
430 *
431 * Results:
432 * None.
433 *
434 * Side effects:
435 * Frees up the memory.
436 *
437 *----------------------------------------------------------------------
438 */
439
440 void
TclFinalizeSynchronization()441 TclFinalizeSynchronization()
442 {
443 #ifdef TCL_THREADS
444 Tcl_ThreadDataKey *keyPtr;
445 Tcl_Mutex *mutexPtr;
446 Tcl_Condition *condPtr;
447 int i;
448
449 TclpMasterLock();
450 for (i=0 ; i<keyRecord.num ; i++) {
451 keyPtr = (Tcl_ThreadDataKey *)keyRecord.list[i];
452 TclpFinalizeThreadDataKey(keyPtr);
453 }
454 if (keyRecord.list != NULL) {
455 ckfree((char *)keyRecord.list);
456 keyRecord.list = NULL;
457 }
458 keyRecord.max = 0;
459 keyRecord.num = 0;
460
461 for (i=0 ; i<mutexRecord.num ; i++) {
462 mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
463 if (mutexPtr != NULL) {
464 TclpFinalizeMutex(mutexPtr);
465 }
466 }
467 if (mutexRecord.list != NULL) {
468 ckfree((char *)mutexRecord.list);
469 mutexRecord.list = NULL;
470 }
471 mutexRecord.max = 0;
472 mutexRecord.num = 0;
473
474 for (i=0 ; i<condRecord.num ; i++) {
475 condPtr = (Tcl_Condition *)condRecord.list[i];
476 if (condPtr != NULL) {
477 TclpFinalizeCondition(condPtr);
478 }
479 }
480 if (condRecord.list != NULL) {
481 ckfree((char *)condRecord.list);
482 condRecord.list = NULL;
483 }
484 condRecord.max = 0;
485 condRecord.num = 0;
486
487 TclpMasterUnlock();
488 #else
489 if (keyRecord.list != NULL) {
490 ckfree((char *)keyRecord.list);
491 keyRecord.list = NULL;
492 }
493 keyRecord.max = 0;
494 keyRecord.num = 0;
495 #endif
496 }
497
498
499 /*
500 *----------------------------------------------------------------------
501 *
502 * Tcl_ExitThread --
503 *
504 * This procedure is called to terminate the current thread.
505 * This should be used by extensions that create threads with
506 * additional interpreters in them.
507 *
508 * Results:
509 * None.
510 *
511 * Side effects:
512 * All thread exit handlers are invoked, then the thread dies.
513 *
514 *----------------------------------------------------------------------
515 */
516
517 void
Tcl_ExitThread(status)518 Tcl_ExitThread(status)
519 int status;
520 {
521 Tcl_FinalizeThread();
522 #ifdef TCL_THREADS
523 TclpThreadExit(status);
524 #endif
525 }
526
527 #ifndef TCL_THREADS
528
529 /*
530 *----------------------------------------------------------------------
531 *
532 * Tcl_ConditionWait, et al. --
533 *
534 * These noop procedures are provided so the stub table does
535 * not have to be conditionalized for threads. The real
536 * implementations of these functions live in the platform
537 * specific files.
538 *
539 * Results:
540 * None.
541 *
542 * Side effects:
543 * None.
544 *
545 *----------------------------------------------------------------------
546 */
547
548 #undef Tcl_ConditionWait
549 void
Tcl_ConditionWait(condPtr,mutexPtr,timePtr)550 Tcl_ConditionWait(condPtr, mutexPtr, timePtr)
551 Tcl_Condition *condPtr; /* Really (pthread_cond_t **) */
552 Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */
553 Tcl_Time *timePtr; /* Timeout on waiting period */
554 {
555 }
556
557 #undef Tcl_ConditionNotify
558 void
Tcl_ConditionNotify(condPtr)559 Tcl_ConditionNotify(condPtr)
560 Tcl_Condition *condPtr;
561 {
562 }
563
564 #undef Tcl_MutexLock
565 void
Tcl_MutexLock(mutexPtr)566 Tcl_MutexLock(mutexPtr)
567 Tcl_Mutex *mutexPtr;
568 {
569 }
570
571 #undef Tcl_MutexUnlock
572 void
Tcl_MutexUnlock(mutexPtr)573 Tcl_MutexUnlock(mutexPtr)
574 Tcl_Mutex *mutexPtr;
575 {
576 }
577 #endif
578