Import Tcl 8.5.15 (as of svn r89086)
This commit is contained in:
528
generic/tclThread.c
Normal file
528
generic/tclThread.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 */
|
||||
char **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(char *objPtr, SyncObjRecord *recPtr);
|
||||
static void RememberSyncObject(char *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 = TclpThreadDataKeyGet(keyPtr);
|
||||
|
||||
if (result == NULL) {
|
||||
result = ckalloc((size_t) size);
|
||||
memset(result, 0, (size_t) size);
|
||||
TclpThreadDataKeySet(keyPtr, result);
|
||||
}
|
||||
#else /* TCL_THREADS */
|
||||
if (*keyPtr == NULL) {
|
||||
result = ckalloc((size_t) size);
|
||||
memset(result, 0, (size_t) size);
|
||||
*keyPtr = (Tcl_ThreadDataKey)result;
|
||||
RememberSyncObject((char *) keyPtr, &keyRecord);
|
||||
}
|
||||
result = * (void **) 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, really
|
||||
* (pthread_key_t **) */
|
||||
{
|
||||
#ifdef TCL_THREADS
|
||||
return TclpThreadDataKeyGet(keyPtr);
|
||||
#else /* TCL_THREADS */
|
||||
char *result = *(char **) keyPtr;
|
||||
return result;
|
||||
#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(
|
||||
char *objPtr, /* Pointer to sync object */
|
||||
SyncObjRecord *recPtr) /* Record of sync objects */
|
||||
{
|
||||
char **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 = (char **) ckalloc(recPtr->max * sizeof(char *));
|
||||
for (i=0,j=0 ; i<recPtr->num ; i++) {
|
||||
if (recPtr->list[i] != NULL) {
|
||||
newList[j++] = recPtr->list[i];
|
||||
}
|
||||
}
|
||||
if (recPtr->list != NULL) {
|
||||
ckfree((char *) 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(
|
||||
char *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((char *)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((char *) 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((char *) 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((char *) condPtr, &condRecord);
|
||||
TclpMasterUnlock();
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclFinalizeThreadData --
|
||||
*
|
||||
* This function cleans up the thread-local storage. This is called once
|
||||
* for each thread.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* Frees up all thread local storage.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
TclFinalizeThreadData(void)
|
||||
{
|
||||
TclpFinalizeThreadDataThread();
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* 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 = (void *) *keyPtr;
|
||||
ckfree(blockPtr);
|
||||
}
|
||||
ckfree((char *) 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((char *) 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((char *) 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 **) */
|
||||
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:
|
||||
*/
|
||||
Reference in New Issue
Block a user