sl@0: /* sl@0: * tclMacThrd.c -- sl@0: * sl@0: * This file implements the Mac-specific thread support. sl@0: * sl@0: * Copyright (c) 1991-1994 The Regents of the University of California. sl@0: * Copyright (c) 1994-1998 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * SCCS: @(#) tclMacThrd.c 1.2 98/02/23 16:48:07 sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: #include "tclMacInt.h" sl@0: #include sl@0: #include sl@0: sl@0: #define TCL_MAC_THRD_DEFAULT_STACK (256*1024) sl@0: sl@0: sl@0: typedef struct TclMacThrdData { sl@0: ThreadID threadID; sl@0: VOID *data; sl@0: struct TclMacThrdData *next; sl@0: } TclMacThrdData; sl@0: sl@0: /* sl@0: * This is an array of the Thread Data Keys. It is a process-wide table. sl@0: * Its size is originally set to 32, but it can grow if needed. sl@0: */ sl@0: sl@0: static TclMacThrdData **tclMacDataKeyArray; sl@0: #define TCL_MAC_INITIAL_KEYSIZE 32 sl@0: sl@0: /* sl@0: * These two bits of data store the current maximum number of keys sl@0: * and the keyCounter (which is the number of occupied slots in the sl@0: * KeyData array. sl@0: * sl@0: */ sl@0: sl@0: static int maxNumKeys = 0; sl@0: static int keyCounter = 0; sl@0: sl@0: /* sl@0: * Prototypes for functions used only in this file sl@0: */ sl@0: sl@0: TclMacThrdData *GetThreadDataStruct(Tcl_ThreadDataKey keyVal); sl@0: TclMacThrdData *RemoveThreadDataStruct(Tcl_ThreadDataKey keyVal); sl@0: sl@0: /* sl@0: * Note: The race evoked by the emulation layer for joinable threads sl@0: * (see ../win/tclWinThrd.c) cannot occur on this platform due to sl@0: * the cooperative implementation of multithreading. sl@0: */ sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclMacHaveThreads -- sl@0: * sl@0: * Do we have the Thread Manager? sl@0: * sl@0: * Results: sl@0: * 1 if the ThreadManager is present, 0 otherwise. sl@0: * sl@0: * Side effects: sl@0: * If this is the first time this is called, the return is cached. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclMacHaveThreads(void) sl@0: { sl@0: static initialized = false; sl@0: static int tclMacHaveThreads = false; sl@0: long response = 0; sl@0: OSErr err = noErr; sl@0: sl@0: if (!initialized) { sl@0: err = Gestalt(gestaltThreadMgrAttr, &response); sl@0: if (err == noErr) { sl@0: tclMacHaveThreads = response | (1 << gestaltThreadMgrPresent); sl@0: } sl@0: } sl@0: sl@0: return tclMacHaveThreads; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_CreateThread -- sl@0: * sl@0: * This procedure creates a new thread. sl@0: * sl@0: * Results: sl@0: * TCL_OK if the thread could be created. The thread ID is sl@0: * returned in a parameter. sl@0: * sl@0: * Side effects: sl@0: * A new thread is created. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) sl@0: Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ sl@0: Tcl_ThreadCreateProc proc; /* Main() function of the thread */ sl@0: ClientData clientData; /* The one argument to Main() */ sl@0: int stackSize; /* Size of stack for the new thread */ sl@0: int flags; /* Flags controlling behaviour of sl@0: * the new thread */ sl@0: { sl@0: if (!TclMacHaveThreads()) { sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: if (stackSize == TCL_THREAD_STACK_DEFAULT) { sl@0: stackSize = TCL_MAC_THRD_DEFAULT_STACK; sl@0: } sl@0: sl@0: #if TARGET_CPU_68K && TARGET_RT_MAC_CFM sl@0: { sl@0: ThreadEntryProcPtr entryProc; sl@0: entryProc = NewThreadEntryUPP(proc); sl@0: sl@0: NewThread(kCooperativeThread, entryProc, (void *) clientData, sl@0: stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr); sl@0: } sl@0: #else sl@0: NewThread(kCooperativeThread, proc, (void *) clientData, sl@0: stackSize, kCreateIfNeeded, NULL, (ThreadID *) idPtr); sl@0: #endif sl@0: if ((ThreadID) *idPtr == kNoThreadID) { sl@0: return TCL_ERROR; sl@0: } else { sl@0: if (flags & TCL_THREAD_JOINABLE) { sl@0: TclRememberJoinableThread (*idPtr); sl@0: } sl@0: sl@0: return TCL_OK; sl@0: } sl@0: sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_JoinThread -- sl@0: * sl@0: * This procedure waits upon the exit of the specified thread. sl@0: * sl@0: * Results: sl@0: * TCL_OK if the wait was successful, TCL_ERROR else. sl@0: * sl@0: * Side effects: sl@0: * The result area is set to the exit code of the thread we sl@0: * waited upon. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: Tcl_JoinThread(threadId, result) sl@0: Tcl_ThreadId threadId; /* Id of the thread to wait upon */ sl@0: int* result; /* Reference to the storage the result sl@0: * of the thread we wait upon will be sl@0: * written into. */ sl@0: { sl@0: if (!TclMacHaveThreads()) { sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: return TclJoinThread (threadId, result); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadExit -- sl@0: * sl@0: * This procedure terminates the current thread. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * This procedure terminates the current thread. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpThreadExit(status) sl@0: int status; sl@0: { sl@0: ThreadID curThread; sl@0: sl@0: if (!TclMacHaveThreads()) { sl@0: return; sl@0: } sl@0: sl@0: GetCurrentThread(&curThread); sl@0: TclSignalExitThread ((Tcl_ThreadId) curThread, status); sl@0: sl@0: DisposeThread(curThread, NULL, false); sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetCurrentThread -- sl@0: * sl@0: * This procedure returns the ID of the currently running thread. sl@0: * sl@0: * Results: sl@0: * A thread ID. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_ThreadId sl@0: Tcl_GetCurrentThread() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: ThreadID curThread; sl@0: sl@0: if (!TclMacHaveThreads()) { sl@0: return (Tcl_ThreadId) 0; sl@0: } else { sl@0: GetCurrentThread(&curThread); sl@0: return (Tcl_ThreadId) curThread; sl@0: } sl@0: #else sl@0: return (Tcl_ThreadId) 0; sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpInitLock sl@0: * sl@0: * This procedure is used to grab a lock that serializes initialization sl@0: * and finalization of Tcl. On some platforms this may also initialize sl@0: * the mutex used to serialize creation of more mutexes and thread sl@0: * local storage keys. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Acquire the initialization mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpInitLock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: /* There is nothing to do on the Mac. */; sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpInitUnlock sl@0: * sl@0: * This procedure is used to release a lock that serializes initialization sl@0: * and finalization of Tcl. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Release the initialization mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpInitUnlock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: /* There is nothing to do on the Mac */; sl@0: #endif sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMasterLock sl@0: * sl@0: * This procedure is used to grab a lock that serializes creation sl@0: * and finalization of serialization objects. This interface is sl@0: * only needed in finalization; it is hidden during sl@0: * creation of the objects. sl@0: * sl@0: * This lock must be different than the initLock because the sl@0: * initLock is held during creation of syncronization objects. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Acquire the master mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpMasterLock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: /* There is nothing to do on the Mac */; sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMasterUnlock sl@0: * sl@0: * This procedure is used to release a lock that serializes creation sl@0: * and finalization of synchronization objects. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Release the master mutex. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpMasterUnlock() sl@0: { sl@0: #ifdef TCL_THREADS sl@0: /* There is nothing to do on the Mac */ sl@0: #endif sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetAllocMutex sl@0: * sl@0: * This procedure returns a pointer to a statically initialized sl@0: * mutex for use by the memory allocator. The alloctor must sl@0: * use this lock, because all other locks are allocated... sl@0: * sl@0: * Results: sl@0: * A pointer to a mutex that is suitable for passing to sl@0: * Tcl_MutexLock and Tcl_MutexUnlock. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Mutex * sl@0: Tcl_GetAllocMutex() sl@0: { sl@0: /* There is nothing to do on the Mac */ sl@0: return NULL; sl@0: } sl@0: sl@0: #ifdef TCL_THREADS sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_MutexLock -- sl@0: * sl@0: * This procedure is invoked to lock a mutex. This procedure sl@0: * handles initializing the mutex, if necessary. The caller sl@0: * can rely on the fact that Tcl_Mutex is an opaque pointer. sl@0: * This routine will change that pointer from NULL after first use. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May block the current thread. The mutex is aquired when sl@0: * this returns. Will allocate memory for a pthread_mutex_t sl@0: * and initialize this the first time this Tcl_Mutex is used. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: Tcl_MutexLock(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ sl@0: { sl@0: /* There is nothing to do on the Mac */ sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMutexUnlock -- sl@0: * sl@0: * This procedure is invoked to unlock a mutex. The mutex must sl@0: * have been locked by Tcl_MutexLock. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The mutex is released when this returns. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: Tcl_MutexUnlock(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ sl@0: { sl@0: /* There is nothing to do on the Mac */ sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeMutex -- sl@0: * sl@0: * This procedure is invoked to clean up one mutex. This is only sl@0: * safe to call at the end of time. sl@0: * sl@0: * This assumes the Master Lock is held. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The mutex list is deallocated. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeMutex(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; sl@0: { sl@0: /* There is nothing to do on the Mac */ sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadDataKeyInit -- sl@0: * sl@0: * This procedure initializes a thread specific data block key. sl@0: * Each thread has table of pointers to thread specific data. sl@0: * all threads agree on which table entry is used by each module. sl@0: * this is remembered in a "data key", that is just an index into sl@0: * this table. To allow self initialization, the interface sl@0: * passes a pointer to this key and the first thread to use sl@0: * the key fills in the pointer to the key. The key should be sl@0: * a process-wide static. sl@0: * sl@0: * There is no system-wide support for thread specific data on the sl@0: * Mac. So we implement this as an array of pointers. The keys are sl@0: * allocated sequentially, and each key maps to a slot in the table. sl@0: * The table element points to a linked list of the instances of sl@0: * the data for each thread. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Will bump the key counter if this is the first time this key sl@0: * has been initialized. May grow the DataKeyArray if that is sl@0: * necessary. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpThreadDataKeyInit(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: { sl@0: sl@0: if (*keyPtr == NULL) { sl@0: keyCounter += 1; sl@0: *keyPtr = (Tcl_ThreadDataKey) keyCounter; sl@0: if (keyCounter > maxNumKeys) { sl@0: TclMacThrdData **newArray; sl@0: int i, oldMax = maxNumKeys; sl@0: sl@0: maxNumKeys = maxNumKeys + TCL_MAC_INITIAL_KEYSIZE; sl@0: sl@0: newArray = (TclMacThrdData **) sl@0: ckalloc(maxNumKeys * sizeof(TclMacThrdData *)); sl@0: sl@0: for (i = 0; i < oldMax; i++) { sl@0: newArray[i] = tclMacDataKeyArray[i]; sl@0: } sl@0: for (i = oldMax; i < maxNumKeys; i++) { sl@0: newArray[i] = NULL; sl@0: } sl@0: sl@0: if (tclMacDataKeyArray != NULL) { sl@0: ckfree((char *) tclMacDataKeyArray); sl@0: } sl@0: tclMacDataKeyArray = newArray; sl@0: sl@0: } sl@0: /* TclRememberDataKey(keyPtr); */ sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadDataKeyGet -- sl@0: * sl@0: * This procedure returns a pointer to a block of thread local storage. sl@0: * sl@0: * Results: sl@0: * A thread-specific pointer to the data structure, or NULL sl@0: * if the memory has not been assigned to this key for this thread. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: VOID * sl@0: TclpThreadDataKeyGet(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: { sl@0: TclMacThrdData *dataPtr; sl@0: sl@0: dataPtr = GetThreadDataStruct(*keyPtr); sl@0: sl@0: if (dataPtr == NULL) { sl@0: return NULL; sl@0: } else { sl@0: return dataPtr->data; sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpThreadDataKeySet -- sl@0: * sl@0: * This procedure sets the pointer to a block of thread local storage. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Sets up the thread so future calls to TclpThreadDataKeyGet with sl@0: * this key will return the data pointer. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpThreadDataKeySet(keyPtr, data) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: VOID *data; /* Thread local storage */ sl@0: { sl@0: TclMacThrdData *dataPtr; sl@0: ThreadID curThread; sl@0: sl@0: dataPtr = GetThreadDataStruct(*keyPtr); sl@0: sl@0: /* sl@0: * Is it legal to reset the thread data like this? sl@0: * And if so, who owns the memory? sl@0: */ sl@0: sl@0: if (dataPtr != NULL) { sl@0: dataPtr->data = data; sl@0: } else { sl@0: dataPtr = (TclMacThrdData *) ckalloc(sizeof(TclMacThrdData)); sl@0: GetCurrentThread(&curThread); sl@0: dataPtr->threadID = curThread; sl@0: dataPtr->data = data; sl@0: dataPtr->next = tclMacDataKeyArray[(int) *keyPtr - 1]; sl@0: tclMacDataKeyArray[(int) *keyPtr - 1] = dataPtr; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeThreadData -- sl@0: * sl@0: * This procedure cleans up the thread-local storage. This is sl@0: * called once for each thread. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Frees up all thread local storage. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeThreadData(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; sl@0: { sl@0: TclMacThrdData *dataPtr; sl@0: sl@0: if (*keyPtr != NULL) { sl@0: dataPtr = RemoveThreadDataStruct(*keyPtr); sl@0: sl@0: if ((dataPtr != NULL) && (dataPtr->data != NULL)) { sl@0: ckfree((char *) dataPtr->data); sl@0: ckfree((char *) dataPtr); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeThreadDataKey -- sl@0: * sl@0: * This procedure is invoked to clean up one key. This is a sl@0: * process-wide storage identifier. The thread finalization code sl@0: * cleans up the thread local storage itself. sl@0: * sl@0: * On the Mac, there is really nothing to do here, since the key sl@0: * is just an array index. But we set the key to 0 just in case sl@0: * someone else is relying on that. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The keyPtr value is set to 0. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeThreadDataKey(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; sl@0: { sl@0: ckfree((char *) tclMacDataKeyArray[(int) *keyPtr - 1]); sl@0: tclMacDataKeyArray[(int) *keyPtr - 1] = NULL; sl@0: *keyPtr = NULL; sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * GetThreadDataStruct -- sl@0: * sl@0: * This procedure gets the data structure corresponding to sl@0: * keyVal for the current process. sl@0: * sl@0: * Results: sl@0: * The requested key data. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TclMacThrdData * sl@0: GetThreadDataStruct(keyVal) sl@0: Tcl_ThreadDataKey keyVal; sl@0: { sl@0: ThreadID curThread; sl@0: TclMacThrdData *dataPtr; sl@0: sl@0: /* sl@0: * The keyPtr will only be greater than keyCounter is someone sl@0: * has passed us a key without getting the value from sl@0: * TclpInitDataKey. sl@0: */ sl@0: sl@0: if ((int) keyVal <= 0) { sl@0: return NULL; sl@0: } else if ((int) keyVal > keyCounter) { sl@0: panic("illegal data key value"); sl@0: } sl@0: sl@0: GetCurrentThread(&curThread); sl@0: sl@0: for (dataPtr = tclMacDataKeyArray[(int) keyVal - 1]; dataPtr != NULL; sl@0: dataPtr = dataPtr->next) { sl@0: if (dataPtr->threadID == curThread) { sl@0: break; sl@0: } sl@0: } sl@0: sl@0: return dataPtr; sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * RemoveThreadDataStruct -- sl@0: * sl@0: * This procedure removes the data structure corresponding to sl@0: * keyVal for the current process from the list kept for keyVal. sl@0: * sl@0: * Results: sl@0: * The requested key data is removed from the list, and a pointer sl@0: * to it is returned. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TclMacThrdData * sl@0: RemoveThreadDataStruct(keyVal) sl@0: Tcl_ThreadDataKey keyVal; sl@0: { sl@0: ThreadID curThread; sl@0: TclMacThrdData *dataPtr, *prevPtr; sl@0: sl@0: sl@0: if ((int) keyVal <= 0) { sl@0: return NULL; sl@0: } else if ((int) keyVal > keyCounter) { sl@0: panic("illegal data key value"); sl@0: } sl@0: sl@0: GetCurrentThread(&curThread); sl@0: sl@0: for (dataPtr = tclMacDataKeyArray[(int) keyVal - 1], prevPtr = NULL; sl@0: dataPtr != NULL; sl@0: prevPtr = dataPtr, dataPtr = dataPtr->next) { sl@0: if (dataPtr->threadID == curThread) { sl@0: break; sl@0: } sl@0: } sl@0: sl@0: if (dataPtr == NULL) { sl@0: /* No body */ sl@0: } else if ( prevPtr == NULL) { sl@0: tclMacDataKeyArray[(int) keyVal - 1] = dataPtr->next; sl@0: } else { sl@0: prevPtr->next = dataPtr->next; sl@0: } sl@0: sl@0: return dataPtr; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_ConditionWait -- sl@0: * sl@0: * This procedure is invoked to wait on a condition variable. sl@0: * On the Mac, mutexes are no-ops, and we just yield. After sl@0: * all, it is the application's job to loop till the condition sl@0: * variable is changed... sl@0: * sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Will block the current thread till someone else yields. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: Tcl_ConditionWait(condPtr, mutexPtr, timePtr) sl@0: Tcl_Condition *condPtr; /* Really (pthread_cond_t **) */ sl@0: Tcl_Mutex *mutexPtr; /* Really (pthread_mutex_t **) */ sl@0: Tcl_Time *timePtr; /* Timeout on waiting period */ sl@0: { sl@0: if (TclMacHaveThreads()) { sl@0: YieldToAnyThread(); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_ConditionNotify -- sl@0: * sl@0: * This procedure is invoked to signal a condition variable. sl@0: * sl@0: * The mutex must be held during this call to avoid races, sl@0: * but this interface does not enforce that. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May unblock another thread. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: Tcl_ConditionNotify(condPtr) sl@0: Tcl_Condition *condPtr; sl@0: { sl@0: if (TclMacHaveThreads()) { sl@0: YieldToAnyThread(); sl@0: } sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizeCondition -- sl@0: * sl@0: * This procedure is invoked to clean up a condition variable. sl@0: * This is only safe to call at the end of time. sl@0: * sl@0: * This assumes the Master Lock is held. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The condition variable is deallocated. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizeCondition(condPtr) sl@0: Tcl_Condition *condPtr; sl@0: { sl@0: /* Nothing to do on the Mac */ sl@0: } sl@0: sl@0: sl@0: sl@0: #endif /* TCL_THREADS */ sl@0: