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