OSDN Git Service

2003-07-09 Chris Demetriou <cgd@broadcom.com>
[pf3gnuchains/pf3gnuchains3x.git] / tcl / mac / tclMacThrd.c
1 /* 
2  * tclMacThrd.c --
3  *
4  *      This file implements the Mac-specific thread support.
5  *
6  * Copyright (c) 1991-1994 The Regents of the University of California.
7  * Copyright (c) 1994-1998 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  * SCCS:  @(#) tclMacThrd.c 1.2 98/02/23 16:48:07
13  */
14
15 #include "tclInt.h"
16 #include "tclPort.h"
17 #include "tclMacInt.h"
18 #include <Threads.h>
19 #include <Gestalt.h>
20
21 #define TCL_MAC_THRD_DEFAULT_STACK (256*1024)
22
23
24 typedef struct TclMacThrdData {
25     ThreadID threadID;
26     VOID *data;
27     struct TclMacThrdData *next;
28 } TclMacThrdData;
29
30 /*
31  * This is an array of the Thread Data Keys.  It is a process-wide table.
32  * Its size is originally set to 32, but it can grow if needed.
33  */
34  
35 static TclMacThrdData **tclMacDataKeyArray;
36 #define TCL_MAC_INITIAL_KEYSIZE 32
37
38 /*
39  * These two bits of data store the current maximum number of keys
40  * and the keyCounter (which is the number of occupied slots in the
41  * KeyData array.
42  * 
43  */
44  
45 static int maxNumKeys = 0;
46 static int keyCounter = 0;
47
48 /*
49  * Prototypes for functions used only in this file
50  */
51  
52 TclMacThrdData *GetThreadDataStruct(Tcl_ThreadDataKey keyVal);
53 TclMacThrdData *RemoveThreadDataStruct(Tcl_ThreadDataKey keyVal);
54
55 /*
56  * Note: The race evoked by the emulation layer for joinable threads
57  * (see ../win/tclWinThrd.c) cannot occur on this platform due to
58  * the cooperative implementation of multithreading.
59  */
60 \f
61 /*
62  *----------------------------------------------------------------------
63  *
64  * TclMacHaveThreads --
65  *
66  *      Do we have the Thread Manager?
67  *
68  * Results:
69  *      1 if the ThreadManager is present, 0 otherwise.
70  *
71  * Side effects:
72  *      If this is the first time this is called, the return is cached.
73  *
74  *----------------------------------------------------------------------
75  */
76
77 int
78 TclMacHaveThreads(void)
79 {
80     static initialized = false;
81     static int tclMacHaveThreads = false;
82     long response = 0;
83     OSErr err = noErr;
84     
85     if (!initialized) {
86         err = Gestalt(gestaltThreadMgrAttr, &response);
87         if (err == noErr) {
88             tclMacHaveThreads = response | (1 << gestaltThreadMgrPresent);
89         }
90     }
91
92     return tclMacHaveThreads;
93 }
94 \f
95 /*
96  *----------------------------------------------------------------------
97  *
98  * Tcl_CreateThread --
99  *
100  *      This procedure creates a new thread.
101  *
102  * Results:
103  *      TCL_OK if the thread could be created.  The thread ID is
104  *      returned in a parameter.
105  *
106  * Side effects:
107  *      A new thread is created.
108  *
109  *----------------------------------------------------------------------
110  */
111
112 int
113 Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags)
114     Tcl_ThreadId *idPtr;                /* Return, the ID of the thread */
115     Tcl_ThreadCreateProc proc;          /* Main() function of the thread */
116     ClientData clientData;              /* The one argument to Main() */
117     int stackSize;                      /* Size of stack for the new thread */
118     int flags;                          /* Flags controlling behaviour of
119                                          * the new thread */
120 {
121     if (!TclMacHaveThreads()) {
122         return TCL_ERROR;
123     }
124
125     if (stackSize == TCL_THREAD_STACK_DEFAULT) {
126         stackSize = TCL_MAC_THRD_DEFAULT_STACK;
127     }
128
129 #if TARGET_CPU_68K && TARGET_RT_MAC_CFM
130     {
131         ThreadEntryProcPtr entryProc;
132         entryProc = NewThreadEntryUPP(proc);
133         
134         NewThread(kCooperativeThread, entryProc, (void *) clientData, 
135             stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr);
136     }
137 #else
138     NewThread(kCooperativeThread, proc, (void *) clientData, 
139         stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr);
140 #endif        
141     if ((ThreadID) *idPtr == kNoThreadID) {
142         return TCL_ERROR;
143     } else {
144         if (flags & TCL_THREAD_JOINABLE) {
145             TclRememberJoinableThread (*idPtr);
146         }
147
148         return TCL_OK;
149     }
150
151 }
152 \f
153 /*
154  *----------------------------------------------------------------------
155  *
156  * Tcl_JoinThread --
157  *
158  *      This procedure waits upon the exit of the specified thread.
159  *
160  * Results:
161  *      TCL_OK if the wait was successful, TCL_ERROR else.
162  *
163  * Side effects:
164  *      The result area is set to the exit code of the thread we
165  *      waited upon.
166  *
167  *----------------------------------------------------------------------
168  */
169
170 int
171 Tcl_JoinThread(id, result)
172     Tcl_ThreadId id;    /* Id of the thread to wait upon */
173     int*     result;    /* Reference to the storage the result
174                          * of the thread we wait upon will be
175                          * written into. */
176 {
177     if (!TclMacHaveThreads()) {
178         return TCL_ERROR;
179     }
180
181     return TclJoinThread (id, result);
182 }
183 \f
184 /*
185  *----------------------------------------------------------------------
186  *
187  * TclpThreadExit --
188  *
189  *      This procedure terminates the current thread.
190  *
191  * Results:
192  *      None.
193  *
194  * Side effects:
195  *      This procedure terminates the current thread.
196  *
197  *----------------------------------------------------------------------
198  */
199
200 void
201 TclpThreadExit(status)
202     int status;
203 {
204     ThreadID curThread;
205     
206     if (!TclMacHaveThreads()) {
207         return;
208     }
209     
210     GetCurrentThread(&curThread);
211     TclSignalExitThread ((Tcl_ThreadId) curThread, status);
212
213     DisposeThread(curThread, NULL, false);
214 }
215
216 \f
217 /*
218  *----------------------------------------------------------------------
219  *
220  * Tcl_GetCurrentThread --
221  *
222  *      This procedure returns the ID of the currently running thread.
223  *
224  * Results:
225  *      A thread ID.
226  *
227  * Side effects:
228  *      None.
229  *
230  *----------------------------------------------------------------------
231  */
232
233 Tcl_ThreadId
234 Tcl_GetCurrentThread()
235 {
236 #ifdef TCL_THREADS
237     ThreadID curThread;
238
239     if (!TclMacHaveThreads()) {
240         return (Tcl_ThreadId) 0;
241     } else {
242         GetCurrentThread(&curThread);
243         return (Tcl_ThreadId) curThread;
244     }
245 #else
246     return (Tcl_ThreadId) 0;
247 #endif
248 }
249
250 \f
251 /*
252  *----------------------------------------------------------------------
253  *
254  * TclpInitLock
255  *
256  *      This procedure is used to grab a lock that serializes initialization
257  *      and finalization of Tcl.  On some platforms this may also initialize
258  *      the mutex used to serialize creation of more mutexes and thread
259  *      local storage keys.
260  *
261  * Results:
262  *      None.
263  *
264  * Side effects:
265  *      Acquire the initialization mutex.
266  *
267  *----------------------------------------------------------------------
268  */
269
270 void
271 TclpInitLock()
272 {
273 #ifdef TCL_THREADS
274     /* There is nothing to do on the Mac. */;
275 #endif
276 }
277
278 \f
279 /*
280  *----------------------------------------------------------------------
281  *
282  * TclpInitUnlock
283  *
284  *      This procedure is used to release a lock that serializes initialization
285  *      and finalization of Tcl.
286  *
287  * Results:
288  *      None.
289  *
290  * Side effects:
291  *      Release the initialization mutex.
292  *
293  *----------------------------------------------------------------------
294  */
295
296 void
297 TclpInitUnlock()
298 {
299 #ifdef TCL_THREADS
300     /* There is nothing to do on the Mac */;
301 #endif
302 }
303 \f
304 /*
305  *----------------------------------------------------------------------
306  *
307  * TclpMasterLock
308  *
309  *      This procedure is used to grab a lock that serializes creation
310  *      and finalization of serialization objects.  This interface is
311  *      only needed in finalization; it is hidden during
312  *      creation of the objects.
313  *
314  *      This lock must be different than the initLock because the
315  *      initLock is held during creation of syncronization objects.
316  *
317  * Results:
318  *      None.
319  *
320  * Side effects:
321  *      Acquire the master mutex.
322  *
323  *----------------------------------------------------------------------
324  */
325
326 void
327 TclpMasterLock()
328 {
329 #ifdef TCL_THREADS
330     /* There is nothing to do on the Mac */;
331 #endif
332 }
333
334 \f
335 /*
336  *----------------------------------------------------------------------
337  *
338  * TclpMasterUnlock
339  *
340  *      This procedure is used to release a lock that serializes creation
341  *      and finalization of synchronization objects.
342  *
343  * Results:
344  *      None.
345  *
346  * Side effects:
347  *      Release the master mutex.
348  *
349  *----------------------------------------------------------------------
350  */
351
352 void
353 TclpMasterUnlock()
354 {
355 #ifdef TCL_THREADS
356     /* There is nothing to do on the Mac */
357 #endif
358 }
359
360 \f
361 /*
362  *----------------------------------------------------------------------
363  *
364  * Tcl_GetAllocMutex
365  *
366  *      This procedure returns a pointer to a statically initialized
367  *      mutex for use by the memory allocator.  The alloctor must
368  *      use this lock, because all other locks are allocated...
369  *
370  * Results:
371  *      A pointer to a mutex that is suitable for passing to
372  *      Tcl_MutexLock and Tcl_MutexUnlock.
373  *
374  * Side effects:
375  *      None.
376  *
377  *----------------------------------------------------------------------
378  */
379
380 Tcl_Mutex *
381 Tcl_GetAllocMutex()
382 {
383     /* There is nothing to do on the Mac */
384     return NULL;
385 }
386
387 #ifdef TCL_THREADS
388 \f
389 /*
390  *----------------------------------------------------------------------
391  *
392  * Tcl_MutexLock --
393  *
394  *      This procedure is invoked to lock a mutex.  This procedure
395  *      handles initializing the mutex, if necessary.  The caller
396  *      can rely on the fact that Tcl_Mutex is an opaque pointer.
397  *      This routine will change that pointer from NULL after first use.
398  *
399  * Results:
400  *      None.
401  *
402  * Side effects:
403  *      May block the current thread.  The mutex is aquired when
404  *      this returns.  Will allocate memory for a pthread_mutex_t
405  *      and initialize this the first time this Tcl_Mutex is used.
406  *
407  *----------------------------------------------------------------------
408  */
409
410 void
411 Tcl_MutexLock(mutexPtr)
412     Tcl_Mutex *mutexPtr;        /* Really (pthread_mutex_t **) */
413 {
414 /* There is nothing to do on the Mac */
415 }
416
417 \f
418 /*
419  *----------------------------------------------------------------------
420  *
421  * TclpMutexUnlock --
422  *
423  *      This procedure is invoked to unlock a mutex.  The mutex must
424  *      have been locked by Tcl_MutexLock.
425  *
426  * Results:
427  *      None.
428  *
429  * Side effects:
430  *      The mutex is released when this returns.
431  *
432  *----------------------------------------------------------------------
433  */
434
435 void
436 Tcl_MutexUnlock(mutexPtr)
437     Tcl_Mutex *mutexPtr;        /* Really (pthread_mutex_t **) */
438 {
439 /* There is nothing to do on the Mac */
440 }
441
442 \f
443 /*
444  *----------------------------------------------------------------------
445  *
446  * TclpFinalizeMutex --
447  *
448  *      This procedure is invoked to clean up one mutex.  This is only
449  *      safe to call at the end of time.
450  *
451  *      This assumes the Master Lock is held.
452  *
453  * Results:
454  *      None.
455  *
456  * Side effects:
457  *      The mutex list is deallocated.
458  *
459  *----------------------------------------------------------------------
460  */
461
462 void
463 TclpFinalizeMutex(mutexPtr)
464     Tcl_Mutex *mutexPtr;
465 {
466 /* There is nothing to do on the Mac */
467 }
468
469 \f
470 /*
471  *----------------------------------------------------------------------
472  *
473  * TclpThreadDataKeyInit --
474  *
475  *      This procedure initializes a thread specific data block key.
476  *      Each thread has table of pointers to thread specific data.
477  *      all threads agree on which table entry is used by each module.
478  *      this is remembered in a "data key", that is just an index into
479  *      this table.  To allow self initialization, the interface
480  *      passes a pointer to this key and the first thread to use
481  *      the key fills in the pointer to the key.  The key should be
482  *      a process-wide static.
483  *
484  *      There is no system-wide support for thread specific data on the 
485  *      Mac.  So we implement this as an array of pointers.  The keys are
486  *      allocated sequentially, and each key maps to a slot in the table.
487  *      The table element points to a linked list of the instances of
488  *      the data for each thread.
489  *
490  * Results:
491  *      None.
492  *
493  * Side effects:
494  *      Will bump the key counter if this is the first time this key
495  *      has been initialized.  May grow the DataKeyArray if that is
496  *      necessary.
497  *
498  *----------------------------------------------------------------------
499  */
500
501 void
502 TclpThreadDataKeyInit(keyPtr)
503     Tcl_ThreadDataKey *keyPtr;  /* Identifier for the data chunk,
504                                  * really (pthread_key_t **) */
505 {
506             
507     if (*keyPtr == NULL) {
508         keyCounter += 1;
509         *keyPtr = (Tcl_ThreadDataKey) keyCounter;
510         if (keyCounter > maxNumKeys) {
511             TclMacThrdData **newArray;
512             int i, oldMax = maxNumKeys;
513              
514             maxNumKeys = maxNumKeys + TCL_MAC_INITIAL_KEYSIZE;
515              
516             newArray = (TclMacThrdData **) 
517                     ckalloc(maxNumKeys * sizeof(TclMacThrdData *));
518              
519             for (i = 0; i < oldMax; i++) {
520                 newArray[i] = tclMacDataKeyArray[i];
521             }
522             for (i = oldMax; i < maxNumKeys; i++) {
523                 newArray[i] = NULL;
524             }
525              
526             if (tclMacDataKeyArray != NULL) {
527                 ckfree((char *) tclMacDataKeyArray);
528             }
529             tclMacDataKeyArray = newArray;
530              
531         }             
532         /* TclRememberDataKey(keyPtr); */
533     }
534 }
535 \f
536 /*
537  *----------------------------------------------------------------------
538  *
539  * TclpThreadDataKeyGet --
540  *
541  *      This procedure returns a pointer to a block of thread local storage.
542  *
543  * Results:
544  *      A thread-specific pointer to the data structure, or NULL
545  *      if the memory has not been assigned to this key for this thread.
546  *
547  * Side effects:
548  *      None.
549  *
550  *----------------------------------------------------------------------
551  */
552
553 VOID *
554 TclpThreadDataKeyGet(keyPtr)
555     Tcl_ThreadDataKey *keyPtr;  /* Identifier for the data chunk,
556                                  * really (pthread_key_t **) */
557 {
558     TclMacThrdData *dataPtr;
559     
560     dataPtr = GetThreadDataStruct(*keyPtr);
561     
562     if (dataPtr == NULL) {
563         return NULL;
564     } else {
565         return dataPtr->data;
566     }
567 }
568
569 \f
570 /*
571  *----------------------------------------------------------------------
572  *
573  * TclpThreadDataKeySet --
574  *
575  *      This procedure sets the pointer to a block of thread local storage.
576  *
577  * Results:
578  *      None.
579  *
580  * Side effects:
581  *      Sets up the thread so future calls to TclpThreadDataKeyGet with
582  *      this key will return the data pointer.
583  *
584  *----------------------------------------------------------------------
585  */
586
587 void
588 TclpThreadDataKeySet(keyPtr, data)
589     Tcl_ThreadDataKey *keyPtr;  /* Identifier for the data chunk,
590                                  * really (pthread_key_t **) */
591     VOID *data;                 /* Thread local storage */
592 {
593     TclMacThrdData *dataPtr;
594     ThreadID curThread;
595     
596     dataPtr = GetThreadDataStruct(*keyPtr);
597     
598     /* 
599      * Is it legal to reset the thread data like this?
600      * And if so, who owns the memory?
601      */
602      
603     if (dataPtr != NULL) {
604         dataPtr->data = data;
605     } else {
606         dataPtr = (TclMacThrdData *) ckalloc(sizeof(TclMacThrdData));
607         GetCurrentThread(&curThread);
608         dataPtr->threadID = curThread;
609         dataPtr->data = data;
610         dataPtr->next = tclMacDataKeyArray[(int) *keyPtr - 1];
611         tclMacDataKeyArray[(int) *keyPtr - 1] = dataPtr;
612    }
613 }
614 \f
615 /*
616  *----------------------------------------------------------------------
617  *
618  * TclpFinalizeThreadData --
619  *
620  *      This procedure cleans up the thread-local storage.  This is
621  *      called once for each thread.
622  *
623  * Results:
624  *      None.
625  *
626  * Side effects:
627  *      Frees up all thread local storage.
628  *
629  *----------------------------------------------------------------------
630  */
631
632 void
633 TclpFinalizeThreadData(keyPtr)
634     Tcl_ThreadDataKey *keyPtr;
635 {
636     TclMacThrdData *dataPtr;
637     
638     if (*keyPtr != NULL) {
639         dataPtr = RemoveThreadDataStruct(*keyPtr);
640         
641         if ((dataPtr != NULL) && (dataPtr->data != NULL)) {
642             ckfree((char *) dataPtr->data);
643             ckfree((char *) dataPtr);
644         }
645     }
646 }
647 \f
648 /*
649  *----------------------------------------------------------------------
650  *
651  * TclpFinalizeThreadDataKey --
652  *
653  *      This procedure is invoked to clean up one key.  This is a
654  *      process-wide storage identifier.  The thread finalization code
655  *      cleans up the thread local storage itself.
656  *
657  *      On the Mac, there is really nothing to do here, since the key
658  *      is just an array index.  But we set the key to 0 just in case
659  *      someone else is relying on that.
660  *
661  * Results:
662  *      None.
663  *
664  * Side effects:
665  *      The keyPtr value is set to 0.
666  *
667  *----------------------------------------------------------------------
668  */
669
670 void
671 TclpFinalizeThreadDataKey(keyPtr)
672     Tcl_ThreadDataKey *keyPtr;
673 {
674     ckfree((char *) tclMacDataKeyArray[(int) *keyPtr - 1]);
675     tclMacDataKeyArray[(int) *keyPtr - 1] = NULL;
676     *keyPtr = NULL;
677 }
678
679 \f
680 /*
681  *----------------------------------------------------------------------
682  *
683  * GetThreadDataStruct --
684  *
685  *      This procedure gets the data structure corresponding to
686  *      keyVal for the current process.
687  *
688  * Results:
689  *      The requested key data.
690  *
691  * Side effects:
692  *      None.
693  *
694  *----------------------------------------------------------------------
695  */
696
697 TclMacThrdData *
698 GetThreadDataStruct(keyVal)
699     Tcl_ThreadDataKey keyVal;
700 {
701     ThreadID curThread;
702     TclMacThrdData *dataPtr;
703     
704     /*
705      * The keyPtr will only be greater than keyCounter is someone
706      * has passed us a key without getting the value from 
707      * TclpInitDataKey.
708      */
709      
710     if ((int) keyVal <= 0)  {
711         return NULL;
712     } else if ((int) keyVal > keyCounter) {
713         panic("illegal data key value");
714     }
715     
716     GetCurrentThread(&curThread);
717     
718     for (dataPtr = tclMacDataKeyArray[(int) keyVal - 1]; dataPtr != NULL;
719             dataPtr = dataPtr->next) {
720         if (dataPtr->threadID ==  curThread) {
721             break;
722         }
723     }
724     
725     return dataPtr;
726 }
727
728 \f
729 /*
730  *----------------------------------------------------------------------
731  *
732  * RemoveThreadDataStruct --
733  *
734  *      This procedure removes the data structure corresponding to
735  *      keyVal for the current process from the list kept for keyVal.
736  *
737  * Results:
738  *      The requested key data is removed from the list, and a pointer 
739  *      to it is returned.
740  *
741  * Side effects:
742  *      None.
743  *
744  *----------------------------------------------------------------------
745  */
746
747 TclMacThrdData *
748 RemoveThreadDataStruct(keyVal)
749     Tcl_ThreadDataKey keyVal;
750 {
751     ThreadID curThread;
752     TclMacThrdData *dataPtr, *prevPtr;
753     
754      
755     if ((int) keyVal <= 0)  {
756         return NULL;
757     } else if ((int) keyVal > keyCounter) {
758         panic("illegal data key value");
759     }
760     
761     GetCurrentThread(&curThread);
762     
763     for (dataPtr = tclMacDataKeyArray[(int) keyVal - 1], prevPtr = NULL; 
764             dataPtr != NULL;
765             prevPtr = dataPtr, dataPtr = dataPtr->next) {
766         if (dataPtr->threadID == curThread) {
767             break;
768         }
769     }
770     
771     if (dataPtr == NULL) {
772         /* No body */
773     } else if ( prevPtr == NULL) {
774         tclMacDataKeyArray[(int) keyVal - 1] = dataPtr->next;
775     } else {
776         prevPtr->next = dataPtr->next;
777     }
778     
779     return dataPtr; 
780 }
781 \f
782 /*
783  *----------------------------------------------------------------------
784  *
785  * Tcl_ConditionWait --
786  *
787  *      This procedure is invoked to wait on a condition variable.
788  *      On the Mac, mutexes are no-ops, and we just yield.  After
789  *      all, it is the application's job to loop till the condition 
790  *      variable is changed...
791  *
792  *
793  * Results:
794  *      None.
795  *
796  * Side effects:
797  *      Will block the current thread till someone else yields.
798  *
799  *----------------------------------------------------------------------
800  */
801
802 void
803 Tcl_ConditionWait(condPtr, mutexPtr, timePtr)
804     Tcl_Condition *condPtr;     /* Really (pthread_cond_t **) */
805     Tcl_Mutex *mutexPtr;        /* Really (pthread_mutex_t **) */
806     Tcl_Time *timePtr;          /* Timeout on waiting period */
807 {
808     if (TclMacHaveThreads()) {
809         YieldToAnyThread();
810     }
811 }
812 \f
813 /*
814  *----------------------------------------------------------------------
815  *
816  * Tcl_ConditionNotify --
817  *
818  *      This procedure is invoked to signal a condition variable.
819  *
820  *      The mutex must be held during this call to avoid races,
821  *      but this interface does not enforce that.
822  *
823  * Results:
824  *      None.
825  *
826  * Side effects:
827  *      May unblock another thread.
828  *
829  *----------------------------------------------------------------------
830  */
831
832 void
833 Tcl_ConditionNotify(condPtr)
834     Tcl_Condition *condPtr;
835 {
836     if (TclMacHaveThreads()) {
837          YieldToAnyThread();
838     }
839 }
840
841 \f
842 /*
843  *----------------------------------------------------------------------
844  *
845  * TclpFinalizeCondition --
846  *
847  *      This procedure is invoked to clean up a condition variable.
848  *      This is only safe to call at the end of time.
849  *
850  *      This assumes the Master Lock is held.
851  *
852  * Results:
853  *      None.
854  *
855  * Side effects:
856  *      The condition variable is deallocated.
857  *
858  *----------------------------------------------------------------------
859  */
860
861 void
862 TclpFinalizeCondition(condPtr)
863     Tcl_Condition *condPtr;
864 {
865     /* Nothing to do on the Mac */
866 }
867
868
869
870 #endif /* TCL_THREADS */
871