summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixThrd.c
diff options
context:
space:
mode:
authorstanton <stanton>1999-04-16 00:46:29 (GMT)
committerstanton <stanton>1999-04-16 00:46:29 (GMT)
commit97464e6cba8eb0008cf2727c15718671992b913f (patch)
treece9959f2747257d98d52ec8d18bf3b0de99b9535 /unix/tclUnixThrd.c
parenta8c96ddb94d1483a9de5e340b740cb74ef6cafa7 (diff)
downloadtcl-97464e6cba8eb0008cf2727c15718671992b913f.zip
tcl-97464e6cba8eb0008cf2727c15718671992b913f.tar.gz
tcl-97464e6cba8eb0008cf2727c15718671992b913f.tar.bz2
merged tcl 8.1 branch back into the main trunk
Diffstat (limited to 'unix/tclUnixThrd.c')
-rw-r--r--unix/tclUnixThrd.c682
1 files changed, 682 insertions, 0 deletions
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
new file mode 100644
index 0000000..b00bf42
--- /dev/null
+++ b/unix/tclUnixThrd.c
@@ -0,0 +1,682 @@
+/*
+ * tclUnixThrd.c --
+ *
+ * This file implements the UNIX-specific thread support.
+ *
+ * Copyright (c) 1991-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 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: @(#) tclUnixThrd.c 1.18 98/02/19 14:24:12
+ */
+
+#include "tclInt.h"
+
+#ifdef TCL_THREADS
+
+#include "tclPort.h"
+#include "pthread.h"
+
+/*
+ * masterLock is used to serialize creation of mutexes, condition
+ * variables, and thread local storage.
+ * This is the only place that can count on the ability to statically
+ * initialize the mutex.
+ */
+
+static pthread_mutex_t masterLock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * initLock is used to serialize initialization and finalization
+ * of Tcl. It cannot use any dyamically allocated storage.
+ */
+
+static pthread_mutex_t initLock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * These are for the critical sections inside this file.
+ */
+
+#define MASTER_LOCK pthread_mutex_lock(&masterLock)
+#define MASTER_UNLOCK pthread_mutex_unlock(&masterLock)
+
+#endif /* TCL_THREADS */
+
+
+
+#ifdef TCL_THREADS
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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() */
+{
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+ if (pthread_create((pthread_t *)idPtr, &attr, (void * (*)())proc, (void *)clientData) < 0) {
+ 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;
+{
+ pthread_exit((VOID *)status);
+}
+#endif /* TCL_THREADS */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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
+ return (Tcl_ThreadId) pthread_self();
+#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
+ pthread_mutex_lock(&initLock);
+#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
+ pthread_mutex_unlock(&initLock);
+#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
+ pthread_mutex_lock(&masterLock);
+#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
+ pthread_mutex_unlock(&masterLock);
+#endif
+}
+
+#ifdef TCL_THREADS
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpMutexInit --
+ * TclpMutexLock --
+ * TclpMutexUnlock --
+ *
+ * These procedures use an explicitly initialized mutex.
+ * These are used by memory allocators for their own mutex.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Initialize, Lock, and Unlock the mutex.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpMutexInit(mPtr)
+ TclpMutex *mPtr;
+{
+ pthread_mutex_init((pthread_mutex_t *)mPtr, NULL);
+}
+void
+TclpMutexLock(mPtr)
+ TclpMutex *mPtr;
+{
+ pthread_mutex_lock((pthread_mutex_t *)mPtr);
+}
+void
+TclpMutexUnlock(mPtr)
+ TclpMutex *mPtr;
+{
+ pthread_mutex_unlock((pthread_mutex_t *)mPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 **) */
+{
+ pthread_mutex_t *pmutexPtr;
+ if (*mutexPtr == NULL) {
+ MASTER_LOCK;
+ if (*mutexPtr == NULL) {
+ /*
+ * Double inside master lock check to avoid a race.
+ */
+
+ pmutexPtr = (pthread_mutex_t *)ckalloc(sizeof(pthread_mutex_t));
+ pthread_mutex_init(pmutexPtr, NULL);
+ *mutexPtr = (Tcl_Mutex)pmutexPtr;
+ TclRememberMutex(mutexPtr);
+ }
+ MASTER_UNLOCK;
+ }
+ pmutexPtr = *((pthread_mutex_t **)mutexPtr);
+ pthread_mutex_lock(pmutexPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 **) */
+{
+ pthread_mutex_t *pmutexPtr = *(pthread_mutex_t **)mutexPtr;
+ pthread_mutex_unlock(pmutexPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+{
+ pthread_mutex_t *pmutexPtr = *(pthread_mutex_t **)mutexPtr;
+ if (pmutexPtr != NULL) {
+ ckfree((char *)pmutexPtr);
+ *mutexPtr = NULL;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Will allocate memory the first time this process calls for
+ * this key. In this case it modifies its argument
+ * to hold the pointer to information about the key.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpThreadDataKeyInit(keyPtr)
+ Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk,
+ * really (pthread_key_t **) */
+{
+ pthread_key_t *pkeyPtr;
+
+ MASTER_LOCK;
+ if (*keyPtr == NULL) {
+ pkeyPtr = (pthread_key_t *)ckalloc(sizeof(pthread_key_t));
+ pthread_key_create(pkeyPtr, NULL);
+ *keyPtr = (Tcl_ThreadDataKey)pkeyPtr;
+ TclRememberDataKey(keyPtr);
+ }
+ MASTER_UNLOCK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 **) */
+{
+ pthread_key_t *pkeyPtr = *(pthread_key_t **)keyPtr;
+ if (pkeyPtr == NULL) {
+ return NULL;
+ } else {
+ return (VOID *)pthread_getspecific(*pkeyPtr);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 */
+{
+ pthread_key_t *pkeyPtr = *(pthread_key_t **)keyPtr;
+ pthread_setspecific(*pkeyPtr, data);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+{
+ VOID *result;
+ pthread_key_t *pkeyPtr;
+
+ if (*keyPtr != NULL) {
+ pkeyPtr = *(pthread_key_t **)keyPtr;
+ result = (VOID *)pthread_getspecific(*pkeyPtr);
+ if (result != NULL) {
+ ckfree((char *)result);
+ pthread_setspecific(*pkeyPtr, (void *)NULL);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * This assumes the master lock is held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The key is deallocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeThreadDataKey(keyPtr)
+ Tcl_ThreadDataKey *keyPtr;
+{
+ pthread_key_t *pkeyPtr;
+ if (*keyPtr != NULL) {
+ pkeyPtr = *(pthread_key_t **)keyPtr;
+ pthread_key_delete(*pkeyPtr);
+ ckfree((char *)pkeyPtr);
+ *keyPtr = NULL;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ConditionWait --
+ *
+ * This procedure is invoked to wait on a condition variable.
+ * The mutex is automically released as part of the wait, and
+ * automatically grabbed when the condition is signaled.
+ *
+ * The mutex must be held when this procedure is called.
+ *
+ * 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_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 */
+{
+ pthread_cond_t *pcondPtr;
+ pthread_mutex_t *pmutexPtr;
+ struct timespec ptime;
+
+ if (*condPtr == NULL) {
+ MASTER_LOCK;
+
+ /*
+ * Double check inside mutex to avoid race,
+ * then initialize condition variable if necessary.
+ */
+
+ if (*condPtr == NULL) {
+ pcondPtr = (pthread_cond_t *)ckalloc(sizeof(pthread_cond_t));
+ pthread_cond_init(pcondPtr, NULL);
+ *condPtr = (Tcl_Condition)pcondPtr;
+ TclRememberCondition(condPtr);
+ }
+ MASTER_UNLOCK;
+ }
+ pmutexPtr = *((pthread_mutex_t **)mutexPtr);
+ pcondPtr = *((pthread_cond_t **)condPtr);
+ if (timePtr == NULL) {
+ pthread_cond_wait(pcondPtr, pmutexPtr);
+ } else {
+ ptime.tv_sec = timePtr->sec + TclpGetSeconds();
+ ptime.tv_nsec = 1000 * timePtr->usec;
+ pthread_cond_timedwait(pcondPtr, pmutexPtr, &ptime);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+{
+ pthread_cond_t *pcondPtr = *((pthread_cond_t **)condPtr);
+ if (pcondPtr != NULL) {
+ pthread_cond_broadcast(pcondPtr);
+ } else {
+ /*
+ * Noone has used the condition variable, so there are no waiters.
+ */
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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;
+{
+ pthread_cond_t *pcondPtr = *(pthread_cond_t **)condPtr;
+ if (pcondPtr != NULL) {
+ pthread_cond_destroy(pcondPtr);
+ ckfree((char *)pcondPtr);
+ *condPtr = NULL;
+ }
+}
+
+
+
+#endif /* TCL_THREADS */
+