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