/* * 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.13 2005/08/11 22:06:47 kennykb 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 = {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 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 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(keyPtr, size) 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 = (VOID *)ckalloc((size_t)size); memset(result, 0, (size_t)size); TclpThreadDataKeySet(keyPtr, result); } #else /* TCL_THREADS */ if (*keyPtr == NULL) { result = (VOID *)ckalloc((size_t)size); memset((char *)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(keyPtr) Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, really * (pthread_key_t **) */ { #ifdef TCL_THREADS return (VOID *)TclpThreadDataKeyGet(keyPtr); #else /* TCL_THREADS */ char *result = *(char **)keyPtr; return (VOID *)result; #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 ; inum ; 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 ; inum ; 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); } /* *---------------------------------------------------------------------- * * 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 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() { 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() { #ifdef TCL_THREADS void* blockPtr; Tcl_ThreadDataKey *keyPtr; Tcl_Mutex *mutexPtr; Tcl_Condition *condPtr; int i; TclpMasterLock(); /* * If we're running unthreaded, the TSD blocks are simply stored * inside their thread data keys. Free them here. */ for (i=0 ; i