sl@0: /* sl@0: * tclThread.c -- sl@0: * sl@0: * This file implements Platform independent thread operations. sl@0: * Most of the real work is done in the platform dependent files. sl@0: * sl@0: * Copyright (c) 1998 by Sun Microsystems, Inc. sl@0: * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclThread.c,v 1.6.2.1 2004/05/06 01:02:59 davygrvy Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #if defined(__SYMBIAN32__) sl@0: #include "tclSymbianGlobals.h" sl@0: #endif sl@0: sl@0: /* sl@0: * There are three classes of synchronization objects: sl@0: * mutexes, thread data keys, and condition variables. sl@0: * The following are used to record the memory used for these sl@0: * objects so they can be finalized. sl@0: * sl@0: * These statics are guarded by the mutex in the caller of sl@0: * TclRememberThreadData, e.g., TclpThreadDataKeyInit sl@0: */ sl@0: sl@0: #ifndef __SYMBIAN32__ sl@0: typedef struct { sl@0: int num; /* Number of objects remembered */ sl@0: int max; /* Max size of the array */ sl@0: char **list; /* List of pointers */ sl@0: } SyncObjRecord; sl@0: #endif sl@0: #if !defined(__SYMBIAN32__) || !defined(__WINSCW__) sl@0: static SyncObjRecord keyRecord = {0, 0, NULL}; sl@0: #endif sl@0: static SyncObjRecord mutexRecord = {0, 0, NULL}; sl@0: static SyncObjRecord condRecord = {0, 0, NULL}; sl@0: sl@0: /* sl@0: * Prototypes of functions used only in this file sl@0: */ sl@0: sl@0: static void RememberSyncObject _ANSI_ARGS_((char *objPtr, sl@0: SyncObjRecord *recPtr)); sl@0: static void ForgetSyncObject _ANSI_ARGS_((char *objPtr, sl@0: SyncObjRecord *recPtr)); sl@0: sl@0: /* sl@0: * Several functions are #defined to nothing in tcl.h if TCL_THREADS is not sl@0: * specified. Here we undo that so the procedures are defined in the sl@0: * stubs table. sl@0: */ sl@0: #ifndef TCL_THREADS sl@0: #undef Tcl_MutexLock sl@0: #undef Tcl_MutexUnlock sl@0: #undef Tcl_MutexFinalize sl@0: #undef Tcl_ConditionNotify sl@0: #undef Tcl_ConditionWait sl@0: #undef Tcl_ConditionFinalize sl@0: #endif sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetThreadData -- sl@0: * sl@0: * This procedure allocates and initializes a chunk of thread sl@0: * local storage. sl@0: * sl@0: * Results: sl@0: * A thread-specific pointer to the data structure. sl@0: * sl@0: * Side effects: sl@0: * Will allocate memory the first time this thread calls for sl@0: * this chunk of storage. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C VOID * sl@0: Tcl_GetThreadData(keyPtr, size) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk */ sl@0: int size; /* Size of storage block */ sl@0: { sl@0: VOID *result; sl@0: #ifdef TCL_THREADS sl@0: sl@0: /* sl@0: * See if this is the first thread to init this key. sl@0: */ sl@0: sl@0: if (*keyPtr == NULL) { sl@0: TclpThreadDataKeyInit(keyPtr); sl@0: } sl@0: sl@0: /* sl@0: * Initialize the key for this thread. sl@0: */ sl@0: sl@0: result = TclpThreadDataKeyGet(keyPtr); sl@0: if (result == NULL) { sl@0: result = (VOID *)ckalloc((size_t)size); sl@0: memset(result, 0, (size_t)size); sl@0: TclpThreadDataKeySet(keyPtr, result); sl@0: } sl@0: #else sl@0: if (*keyPtr == NULL) { sl@0: result = (VOID *)ckalloc((size_t)size); sl@0: memset((char *)result, 0, (size_t)size); sl@0: *keyPtr = (Tcl_ThreadDataKey)result; sl@0: TclRememberDataKey(keyPtr); sl@0: } sl@0: result = *(VOID **)keyPtr; sl@0: #endif sl@0: return result; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclThreadDataKeyGet -- sl@0: * sl@0: * This procedure returns a pointer to a block of thread local storage. sl@0: * sl@0: * Results: sl@0: * A thread-specific pointer to the data structure, or NULL sl@0: * if the memory has not been assigned to this key for this thread. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: VOID * sl@0: TclThreadDataKeyGet(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: { sl@0: #ifdef TCL_THREADS sl@0: return (VOID *)TclpThreadDataKeyGet(keyPtr); sl@0: #else sl@0: char *result = *(char **)keyPtr; sl@0: return (VOID *)result; sl@0: #endif /* TCL_THREADS */ sl@0: } sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclThreadDataKeySet -- sl@0: * sl@0: * This procedure sets a thread local storage pointer. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * The assigned value will be returned by TclpThreadDataKeyGet. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclThreadDataKeySet(keyPtr, data) sl@0: Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, sl@0: * really (pthread_key_t **) */ sl@0: VOID *data; /* Thread local storage */ sl@0: { sl@0: #ifdef TCL_THREADS sl@0: if (*keyPtr == NULL) { sl@0: TclpThreadDataKeyInit(keyPtr); sl@0: } sl@0: TclpThreadDataKeySet(keyPtr, data); sl@0: #else sl@0: *keyPtr = (Tcl_ThreadDataKey)data; sl@0: #endif /* TCL_THREADS */ sl@0: } sl@0: sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * RememberSyncObject sl@0: * sl@0: * Keep a list of (mutexes/condition variable/data key) sl@0: * used during finalization. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Add to the appropriate list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: RememberSyncObject(objPtr, recPtr) sl@0: char *objPtr; /* Pointer to sync object */ sl@0: SyncObjRecord *recPtr; /* Record of sync objects */ sl@0: { sl@0: char **newList; sl@0: int i, j; sl@0: sl@0: /* sl@0: * Save the pointer to the allocated object so it can be finalized. sl@0: * Grow the list of pointers if necessary, copying only non-NULL sl@0: * pointers to the new list. sl@0: */ sl@0: sl@0: if (recPtr->num >= recPtr->max) { sl@0: recPtr->max += 8; sl@0: newList = (char **)ckalloc(recPtr->max * sizeof(char *)); sl@0: for (i=0,j=0 ; inum ; i++) { sl@0: if (recPtr->list[i] != NULL) { sl@0: newList[j++] = recPtr->list[i]; sl@0: } sl@0: } sl@0: if (recPtr->list != NULL) { sl@0: ckfree((char *)recPtr->list); sl@0: } sl@0: recPtr->list = newList; sl@0: recPtr->num = j; sl@0: } sl@0: recPtr->list[recPtr->num] = objPtr; sl@0: recPtr->num++; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * ForgetSyncObject sl@0: * sl@0: * Remove a single object from the list. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Remove from the appropriate list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: ForgetSyncObject(objPtr, recPtr) sl@0: char *objPtr; /* Pointer to sync object */ sl@0: SyncObjRecord *recPtr; /* Record of sync objects */ sl@0: { sl@0: int i; sl@0: sl@0: for (i=0 ; inum ; i++) { sl@0: if (objPtr == recPtr->list[i]) { sl@0: recPtr->list[i] = NULL; sl@0: return; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclRememberMutex sl@0: * sl@0: * Keep a list of mutexes used during finalization. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Add to the mutex list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclRememberMutex(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; sl@0: { sl@0: RememberSyncObject((char *)mutexPtr, &mutexRecord); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_MutexFinalize sl@0: * sl@0: * Finalize a single mutex and remove it from the sl@0: * list of remembered objects. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Remove the mutex from the list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_MutexFinalize(mutexPtr) sl@0: Tcl_Mutex *mutexPtr; sl@0: { sl@0: #ifdef TCL_THREADS sl@0: TclpFinalizeMutex(mutexPtr); sl@0: #endif sl@0: ForgetSyncObject((char *)mutexPtr, &mutexRecord); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclRememberDataKey sl@0: * sl@0: * Keep a list of thread data keys used during finalization. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Add to the key list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclRememberDataKey(keyPtr) sl@0: Tcl_ThreadDataKey *keyPtr; sl@0: { sl@0: RememberSyncObject((char *)keyPtr, &keyRecord); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclRememberCondition sl@0: * sl@0: * Keep a list of condition variables used during finalization. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Add to the condition variable list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclRememberCondition(condPtr) sl@0: Tcl_Condition *condPtr; sl@0: { sl@0: RememberSyncObject((char *)condPtr, &condRecord); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_ConditionFinalize sl@0: * sl@0: * Finalize a single condition variable and remove it from the sl@0: * list of remembered objects. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Remove the condition variable from the list. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_ConditionFinalize(condPtr) sl@0: Tcl_Condition *condPtr; sl@0: { sl@0: #ifdef TCL_THREADS sl@0: TclpFinalizeCondition(condPtr); sl@0: #endif sl@0: ForgetSyncObject((char *)condPtr, &condRecord); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclFinalizeThreadData -- sl@0: * sl@0: * This procedure cleans up the thread-local storage. This is sl@0: * called once for each thread. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Frees up all thread local storage. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclFinalizeThreadData() sl@0: { sl@0: int i; sl@0: Tcl_ThreadDataKey *keyPtr; sl@0: sl@0: TclpMasterLock(); sl@0: for (i=0 ; i