summaryrefslogtreecommitdiffstats
path: root/generic/tclThread.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclThread.c')
-rw-r--r--generic/tclThread.c538
1 files changed, 538 insertions, 0 deletions
diff --git a/generic/tclThread.c b/generic/tclThread.c
new file mode 100644
index 0000000..198fa6a
--- /dev/null
+++ b/generic/tclThread.c
@@ -0,0 +1,538 @@
+/*
+ * tclThread.c --
+ *
+ * This file implements Platform independent thread operations. Most of
+ * the real work is done in the platform dependent files.
+ *
+ * Copyright (c) 1998 by Sun Microsystems, Inc.
+ * Copyright (c) 2008 by George Peter Staplin
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tclInt.h"
+
+/*
+ * There are three classes of synchronization objects: mutexes, thread data
+ * keys, and condition variables. The following are used to record the memory
+ * used for these objects so they can be finalized.
+ *
+ * These statics are guarded by the mutex in the caller of
+ * TclRememberThreadData, e.g., TclpThreadDataKeyInit
+ */
+
+typedef struct {
+ int num; /* Number of objects remembered */
+ int max; /* Max size of the array */
+ void **list; /* List of pointers */
+} SyncObjRecord;
+
+static SyncObjRecord keyRecord = {0, 0, NULL};
+static SyncObjRecord mutexRecord = {0, 0, NULL};
+static SyncObjRecord condRecord = {0, 0, NULL};
+
+/*
+ * Prototypes of functions used only in this file.
+ */
+
+static void ForgetSyncObject(void *objPtr, SyncObjRecord *recPtr);
+static void RememberSyncObject(void *objPtr,
+ SyncObjRecord *recPtr);
+
+/*
+ * Several functions are #defined to nothing in tcl.h if TCL_THREADS is not
+ * specified. Here we undo that so the functions are defined in the stubs
+ * table.
+ */
+
+#ifndef TCL_THREADS
+#undef Tcl_MutexLock
+#undef Tcl_MutexUnlock
+#undef Tcl_MutexFinalize
+#undef Tcl_ConditionNotify
+#undef Tcl_ConditionWait
+#undef Tcl_ConditionFinalize
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GetThreadData --
+ *
+ * This function allocates and initializes a chunk of thread local
+ * storage.
+ *
+ * Results:
+ * A thread-specific pointer to the data structure.
+ *
+ * Side effects:
+ * Will allocate memory the first time this thread calls for this chunk
+ * of storage.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void *
+Tcl_GetThreadData(
+ Tcl_ThreadDataKey *keyPtr, /* Identifier for the data chunk */
+ int size) /* Size of storage block */
+{
+ void *result;
+#ifdef TCL_THREADS
+ /*
+ * Initialize the key for this thread.
+ */
+
+ result = TclThreadStorageKeyGet(keyPtr);
+
+ if (result == NULL) {
+ result = ckalloc(size);
+ memset(result, 0, (size_t) size);
+ TclThreadStorageKeySet(keyPtr, result);
+ }
+#else /* TCL_THREADS */
+ if (*keyPtr == NULL) {
+ result = ckalloc(size);
+ memset(result, 0, (size_t)size);
+ *keyPtr = result;
+ RememberSyncObject(keyPtr, &keyRecord);
+ } else {
+ result = *keyPtr;
+ }
+#endif /* TCL_THREADS */
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclThreadDataKeyGet --
+ *
+ * This function 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 *
+TclThreadDataKeyGet(
+ Tcl_ThreadDataKey *keyPtr) /* Identifier for the data chunk. */
+
+{
+#ifdef TCL_THREADS
+ return TclThreadStorageKeyGet(keyPtr);
+#else /* TCL_THREADS */
+ return *keyPtr;
+#endif /* TCL_THREADS */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RememberSyncObject
+ *
+ * Keep a list of (mutexes/condition variable/data key) used during
+ * finalization.
+ *
+ * Assume master lock is held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Add to the appropriate list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RememberSyncObject(
+ void *objPtr, /* Pointer to sync object */
+ SyncObjRecord *recPtr) /* Record of sync objects */
+{
+ void **newList;
+ int i, j;
+
+
+ /*
+ * Reuse any free slot in the list.
+ */
+
+ for (i=0 ; i < recPtr->num ; ++i) {
+ if (recPtr->list[i] == NULL) {
+ recPtr->list[i] = objPtr;
+ return;
+ }
+ }
+
+ /*
+ * Grow the list of pointers if necessary, copying only non-NULL
+ * pointers to the new list.
+ */
+
+ if (recPtr->num >= recPtr->max) {
+ recPtr->max += 8;
+ newList = ckalloc(recPtr->max * sizeof(void *));
+ for (i=0,j=0 ; i<recPtr->num ; i++) {
+ if (recPtr->list[i] != NULL) {
+ newList[j++] = recPtr->list[i];
+ }
+ }
+ if (recPtr->list != NULL) {
+ ckfree(recPtr->list);
+ }
+ recPtr->list = newList;
+ recPtr->num = j;
+ }
+
+ recPtr->list[recPtr->num] = objPtr;
+ recPtr->num++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ForgetSyncObject
+ *
+ * Remove a single object from the list.
+ * Assume master lock is held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Remove from the appropriate list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ForgetSyncObject(
+ void *objPtr, /* Pointer to sync object */
+ SyncObjRecord *recPtr) /* Record of sync objects */
+{
+ int i;
+
+ for (i=0 ; i<recPtr->num ; i++) {
+ if (objPtr == recPtr->list[i]) {
+ recPtr->list[i] = NULL;
+ return;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclRememberMutex
+ *
+ * Keep a list of mutexes used during finalization.
+ * Assume master lock is held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Add to the mutex list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclRememberMutex(
+ Tcl_Mutex *mutexPtr)
+{
+ RememberSyncObject(mutexPtr, &mutexRecord);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_MutexFinalize --
+ *
+ * Finalize a single mutex and remove it from the list of remembered
+ * objects.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Remove the mutex from the list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_MutexFinalize(
+ Tcl_Mutex *mutexPtr)
+{
+#ifdef TCL_THREADS
+ TclpFinalizeMutex(mutexPtr);
+#endif
+ TclpMasterLock();
+ ForgetSyncObject(mutexPtr, &mutexRecord);
+ TclpMasterUnlock();
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclRememberCondition
+ *
+ * Keep a list of condition variables used during finalization.
+ * Assume master lock is held.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Add to the condition variable list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclRememberCondition(
+ Tcl_Condition *condPtr)
+{
+ RememberSyncObject(condPtr, &condRecord);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ConditionFinalize --
+ *
+ * Finalize a single condition variable and remove it from the list of
+ * remembered objects.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Remove the condition variable from the list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_ConditionFinalize(
+ Tcl_Condition *condPtr)
+{
+#ifdef TCL_THREADS
+ TclpFinalizeCondition(condPtr);
+#endif
+ TclpMasterLock();
+ ForgetSyncObject(condPtr, &condRecord);
+ TclpMasterUnlock();
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclFinalizeThreadData --
+ *
+ * This function cleans up the thread-local storage. Secondary, it cleans
+ * thread alloc cache.
+ * This is called once for each thread before thread exits.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees up all thread local storage.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclFinalizeThreadData(int quick)
+{
+ TclFinalizeThreadDataThread();
+#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC)
+ if (!quick) {
+ /*
+ * Quick exit principle makes it useless to terminate allocators
+ */
+ TclFinalizeThreadAllocThread();
+ }
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclFinalizeSynchronization --
+ *
+ * This function cleans up all synchronization objects: mutexes,
+ * condition variables, and thread-local storage.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees up the memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclFinalizeSynchronization(void)
+{
+ int i;
+ void *blockPtr;
+ Tcl_ThreadDataKey *keyPtr;
+#ifdef TCL_THREADS
+ Tcl_Mutex *mutexPtr;
+ Tcl_Condition *condPtr;
+
+ TclpMasterLock();
+#endif
+
+ /*
+ * If we're running unthreaded, the TSD blocks are simply stored inside
+ * their thread data keys. Free them here.
+ */
+
+ if (keyRecord.list != NULL) {
+ for (i=0 ; i<keyRecord.num ; i++) {
+ keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
+ blockPtr = *keyPtr;
+ ckfree(blockPtr);
+ }
+ ckfree(keyRecord.list);
+ keyRecord.list = NULL;
+ }
+ keyRecord.max = 0;
+ keyRecord.num = 0;
+
+#ifdef TCL_THREADS
+ /*
+ * Call thread storage master cleanup.
+ */
+
+ TclFinalizeThreadStorage();
+
+ for (i=0 ; i<mutexRecord.num ; i++) {
+ mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
+ if (mutexPtr != NULL) {
+ TclpFinalizeMutex(mutexPtr);
+ }
+ }
+ if (mutexRecord.list != NULL) {
+ ckfree(mutexRecord.list);
+ mutexRecord.list = NULL;
+ }
+ mutexRecord.max = 0;
+ mutexRecord.num = 0;
+
+ for (i=0 ; i<condRecord.num ; i++) {
+ condPtr = (Tcl_Condition *) condRecord.list[i];
+ if (condPtr != NULL) {
+ TclpFinalizeCondition(condPtr);
+ }
+ }
+ if (condRecord.list != NULL) {
+ ckfree(condRecord.list);
+ condRecord.list = NULL;
+ }
+ condRecord.max = 0;
+ condRecord.num = 0;
+
+ TclpMasterUnlock();
+#endif /* TCL_THREADS */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ExitThread --
+ *
+ * This function is called to terminate the current thread. This should
+ * be used by extensions that create threads with additional interpreters
+ * in them.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All thread exit handlers are invoked, then the thread dies.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_ExitThread(
+ int status)
+{
+ Tcl_FinalizeThread();
+#ifdef TCL_THREADS
+ TclpThreadExit(status);
+#endif
+}
+
+#ifndef TCL_THREADS
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_ConditionWait, et al. --
+ *
+ * These noop functions are provided so the stub table does not have to
+ * be conditionalized for threads. The real implementations of these
+ * functions live in the platform specific files.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+#undef Tcl_ConditionWait
+void
+Tcl_ConditionWait(
+ Tcl_Condition *condPtr, /* Really (pthread_cond_t **) */
+ Tcl_Mutex *mutexPtr, /* Really (pthread_mutex_t **) */
+ const Tcl_Time *timePtr) /* Timeout on waiting period */
+{
+}
+
+#undef Tcl_ConditionNotify
+void
+Tcl_ConditionNotify(
+ Tcl_Condition *condPtr)
+{
+}
+
+#undef Tcl_MutexLock
+void
+Tcl_MutexLock(
+ Tcl_Mutex *mutexPtr)
+{
+}
+
+#undef Tcl_MutexUnlock
+void
+Tcl_MutexUnlock(
+ Tcl_Mutex *mutexPtr)
+{
+}
+#endif /* !TCL_THREADS */
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */