/* * 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. * * RCS: @(#) $Id: tclThread.c,v 1.6 2002/12/10 00:34:15 hobbs Exp $ */ #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; static SyncObjRecord mutexRecord; static SyncObjRecord condRecord; /* * Prototypes of functions used only in this file */ static void RememberSyncObject _ANSI_ARGS_((char *objPtr, SyncObjRecord *recPtr)); static void ForgetSyncObject _ANSI_ARGS_((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 procedures 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 procedure 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(keyPtr, size) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk */ int size; /* Size of storage block */ { VOID *result; #ifdef TCL_THREADS /* * See if this is the first thread to init this key. */ if (*keyPtr == NULL) { TclpThreadDataKeyInit(keyPtr); } /* * Initialize the key for this thread. */ result = TclpThreadDataKeyGet(keyPtr); if (result == NULL) { result = (VOID *)ckalloc((size_t)size); memset(result, 0, (size_t)size); TclpThreadDataKeySet(keyPtr, result); } #else if (*keyPtr == NULL) { result = (VOID *)ckalloc((size_t)size); memset((char *)result, 0, (size_t)size); *keyPtr = (Tcl_ThreadDataKey)result; TclRememberDataKey(keyPtr); } result = *(VOID **)keyPtr; #endif return result; } /* *---------------------------------------------------------------------- * * TclThreadDataKeyGet -- * * 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 * TclThreadDataKeyGet(keyPtr) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, * really (pthread_key_t **) */ { #ifdef TCL_THREADS return (VOID *)TclpThreadDataKeyGet(keyPtr); #else char *result = *(char **)keyPtr; return (VOID *)result; #endif /* TCL_THREADS */ } /* *---------------------------------------------------------------------- * * TclThreadDataKeySet -- * * This procedure sets a thread local storage pointer. * * Results: * None. * * Side effects: * The assigned value will be returned by TclpThreadDataKeyGet. * *---------------------------------------------------------------------- */ void TclThreadDataKeySet(keyPtr, data) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, * really (pthread_key_t **) */ VOID *data; /* Thread local storage */ { #ifdef TCL_THREADS if (*keyPtr == NULL) { TclpThreadDataKeyInit(keyPtr); } TclpThreadDataKeySet(keyPtr, data); #else *keyPtr = (Tcl_ThreadDataKey)data; #endif /* TCL_THREADS */ } /* *---------------------------------------------------------------------- * * RememberSyncObject * * Keep a list of (mutexes/condition variable/data key) * used during finalization. * * Results: * None. * * Side effects: * Add to the appropriate list. * *---------------------------------------------------------------------- */ static void RememberSyncObject(objPtr, recPtr) char *objPtr; /* Pointer to sync object */ SyncObjRecord *recPtr; /* Record of sync objects */ { char **newList; int i, j; /* * Save the pointer to the allocated object so it can be finalized. * 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. * * Results: * None. * * Side effects: * Remove from the appropriate list. * *---------------------------------------------------------------------- */ static void ForgetSyncObject(objPtr, recPtr) 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. * * Results: * None. * * Side effects: * Add to the mutex list. * *---------------------------------------------------------------------- */ void TclRememberMutex(mutexPtr) 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(mutexPtr) Tcl_Mutex *mutexPtr; { #ifdef TCL_THREADS TclpFinalizeMutex(mutexPtr); #endif ForgetSyncObject((char *)mutexPtr, &mutexRecord); } /* *---------------------------------------------------------------------- * * TclRememberDataKey * * Keep a list of thread data keys used during finalization. * * Results: * None. * * Side effects: * Add to the key list. * *---------------------------------------------------------------------- */ void TclRememberDataKey(keyPtr) Tcl_ThreadDataKey *keyPtr; { RememberSyncObject((char *)keyPtr, &keyRecord); } /* *---------------------------------------------------------------------- * * TclRememberCondition * * Keep a list of condition variables used during finalization. * * Results: * None. * * Side effects: * Add to the condition variable list. * *---------------------------------------------------------------------- */ void TclRememberCondition(condPtr) 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(condPtr) Tcl_Condition *condPtr; { #ifdef TCL_THREADS TclpFinalizeCondition(condPtr); #endif ForgetSyncObject((char *)condPtr, &condRecord); } /* *---------------------------------------------------------------------- * * TclFinalizeThreadData -- * * 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 TclFinalizeThreadData() { int i; Tcl_ThreadDataKey *keyPtr; TclpMasterLock(); for (i=0 ; i<keyRecord.num ; i++) { keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i]; #ifdef TCL_THREADS TclpFinalizeThreadData(keyPtr); #else if (*keyPtr != NULL) { ckfree((char *)*keyPtr); *keyPtr = NULL; } #endif } TclpMasterUnlock(); } /* *---------------------------------------------------------------------- * * TclFinalizeSynchronization -- * * This procedure cleans up all synchronization objects: * mutexes, condition variables, and thread-local storage. * * Results: * None. * * Side effects: * Frees up the memory. * *---------------------------------------------------------------------- */ void TclFinalizeSynchronization() { #ifdef TCL_THREADS Tcl_ThreadDataKey *keyPtr; Tcl_Mutex *mutexPtr; Tcl_Condition *condPtr; int i; TclpMasterLock(); for (i=0 ; i<keyRecord.num ; i++) { keyPtr = (Tcl_ThreadDataKey *)keyRecord.list[i]; TclpFinalizeThreadDataKey(keyPtr); } if (keyRecord.list != NULL) { ckfree((char *)keyRecord.list); keyRecord.list = NULL; } keyRecord.max = 0; keyRecord.num = 0; 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(); #else if (keyRecord.list != NULL) { ckfree((char *)keyRecord.list); keyRecord.list = NULL; } keyRecord.max = 0; keyRecord.num = 0; #endif } /* *---------------------------------------------------------------------- * * Tcl_ExitThread -- * * This procedure 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(status) int status; { Tcl_FinalizeThread(); #ifdef TCL_THREADS TclpThreadExit(status); #endif } #ifndef TCL_THREADS /* *---------------------------------------------------------------------- * * Tcl_ConditionWait, et al. -- * * These noop procedures 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(condPtr, mutexPtr, timePtr) 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(condPtr) Tcl_Condition *condPtr; { } #undef Tcl_MutexLock void Tcl_MutexLock(mutexPtr) Tcl_Mutex *mutexPtr; { } #undef Tcl_MutexUnlock void Tcl_MutexUnlock(mutexPtr) Tcl_Mutex *mutexPtr; { } #endif