539 lines
11 KiB
C
539 lines
11 KiB
C
|
/*
|
|||
|
* 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);
|
|||
|
TclThreadStorageKeySet(keyPtr, result);
|
|||
|
}
|
|||
|
#else /* TCL_THREADS */
|
|||
|
if (*keyPtr == NULL) {
|
|||
|
result = ckalloc(size);
|
|||
|
memset(result, 0, 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 global 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 global 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 global 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
|
|||
|
TclpGlobalLock();
|
|||
|
ForgetSyncObject(mutexPtr, &mutexRecord);
|
|||
|
TclpGlobalUnlock();
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TclRememberCondition
|
|||
|
*
|
|||
|
* Keep a list of condition variables used during finalization.
|
|||
|
* Assume global 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
|
|||
|
TclpGlobalLock();
|
|||
|
ForgetSyncObject(condPtr, &condRecord);
|
|||
|
TclpGlobalUnlock();
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* 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;
|
|||
|
|
|||
|
TclpGlobalLock();
|
|||
|
#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 global 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;
|
|||
|
|
|||
|
TclpGlobalUnlock();
|
|||
|
#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:
|
|||
|
*/
|