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 ; i<recPtr->num ; 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 ; i<recPtr->num ; 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<keyRecord.num ; i++) {
sl@0: 	keyPtr = (Tcl_ThreadDataKey *) keyRecord.list[i];
sl@0: #ifdef TCL_THREADS
sl@0: 	TclpFinalizeThreadData(keyPtr);
sl@0: #else
sl@0: 	if (*keyPtr != NULL) {
sl@0: 	    ckfree((char *)*keyPtr);
sl@0: 	    *keyPtr = NULL;
sl@0: 	}
sl@0: #endif
sl@0:     }
sl@0:     TclpMasterUnlock();
sl@0: }
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * TclFinalizeSynchronization --
sl@0:  *
sl@0:  *      This procedure cleans up all synchronization objects:
sl@0:  *      mutexes, condition variables, and thread-local storage.
sl@0:  *
sl@0:  * Results:
sl@0:  *	None.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	Frees up the memory.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: void
sl@0: TclFinalizeSynchronization()
sl@0: {
sl@0: #ifdef TCL_THREADS
sl@0:     Tcl_ThreadDataKey *keyPtr;
sl@0:     Tcl_Mutex *mutexPtr;
sl@0:     Tcl_Condition *condPtr;
sl@0:     int i;
sl@0: 
sl@0:     TclpMasterLock();
sl@0:     for (i=0 ; i<keyRecord.num ; i++) {
sl@0: 	keyPtr = (Tcl_ThreadDataKey *)keyRecord.list[i];
sl@0: 	TclpFinalizeThreadDataKey(keyPtr);
sl@0:     }
sl@0:     if (keyRecord.list != NULL) {
sl@0: 	ckfree((char *)keyRecord.list);
sl@0: 	keyRecord.list = NULL;
sl@0:     }
sl@0:     keyRecord.max = 0;
sl@0:     keyRecord.num = 0;
sl@0: 
sl@0:     for (i=0 ; i<mutexRecord.num ; i++) {
sl@0: 	mutexPtr = (Tcl_Mutex *)mutexRecord.list[i];
sl@0: 	if (mutexPtr != NULL) {
sl@0: 	    TclpFinalizeMutex(mutexPtr);
sl@0: 	}
sl@0:     }
sl@0:     if (mutexRecord.list != NULL) {
sl@0: 	ckfree((char *)mutexRecord.list);
sl@0: 	mutexRecord.list = NULL;
sl@0:     }
sl@0:     mutexRecord.max = 0;
sl@0:     mutexRecord.num = 0;
sl@0: 
sl@0:     for (i=0 ; i<condRecord.num ; i++) {
sl@0: 	condPtr = (Tcl_Condition *)condRecord.list[i];
sl@0: 	if (condPtr != NULL) {
sl@0: 	    TclpFinalizeCondition(condPtr);
sl@0: 	}
sl@0:     }
sl@0:     if (condRecord.list != NULL) {
sl@0: 	ckfree((char *)condRecord.list);
sl@0: 	condRecord.list = NULL;
sl@0:     }
sl@0:     condRecord.max = 0;
sl@0:     condRecord.num = 0;
sl@0: 
sl@0:     TclpMasterUnlock();
sl@0: #else
sl@0:     if (keyRecord.list != NULL) {
sl@0: 	ckfree((char *)keyRecord.list);
sl@0: 	keyRecord.list = NULL;
sl@0:     }
sl@0:     keyRecord.max = 0;
sl@0:     keyRecord.num = 0;
sl@0: #endif
sl@0: }
sl@0: 
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * Tcl_ExitThread --
sl@0:  *
sl@0:  *	This procedure is called to terminate the current thread.
sl@0:  *	This should be used by extensions that create threads with
sl@0:  *	additional interpreters in them.
sl@0:  *
sl@0:  * Results:
sl@0:  *	None.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	All thread exit handlers are invoked, then the thread dies.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: EXPORT_C void
sl@0: Tcl_ExitThread(status)
sl@0:     int status;
sl@0: {
sl@0:     Tcl_FinalizeThread();
sl@0: #ifdef TCL_THREADS
sl@0:     TclpThreadExit(status);
sl@0: #endif
sl@0: }
sl@0: 
sl@0: #ifndef TCL_THREADS
sl@0: 
sl@0: /*
sl@0:  *----------------------------------------------------------------------
sl@0:  *
sl@0:  * Tcl_ConditionWait, et al. --
sl@0:  *
sl@0:  *	These noop procedures are provided so the stub table does
sl@0:  *	not have to be conditionalized for threads.  The real
sl@0:  *	implementations of these functions live in the platform
sl@0:  *	specific files.
sl@0:  *
sl@0:  * Results:
sl@0:  *	None.
sl@0:  *
sl@0:  * Side effects:
sl@0:  *	None.
sl@0:  *
sl@0:  *----------------------------------------------------------------------
sl@0:  */
sl@0: 
sl@0: #undef Tcl_ConditionWait
sl@0: EXPORT_C void
sl@0: Tcl_ConditionWait(condPtr, mutexPtr, timePtr)
sl@0:     Tcl_Condition *condPtr;	/* Really (pthread_cond_t **) */
sl@0:     Tcl_Mutex *mutexPtr;	/* Really (pthread_mutex_t **) */
sl@0:     Tcl_Time *timePtr;		/* Timeout on waiting period */
sl@0: {
sl@0: }
sl@0: 
sl@0: #undef Tcl_ConditionNotify
sl@0: EXPORT_C void
sl@0: Tcl_ConditionNotify(condPtr)
sl@0:     Tcl_Condition *condPtr;
sl@0: {
sl@0: }
sl@0: 
sl@0: #undef Tcl_MutexLock
sl@0: EXPORT_C void
sl@0: Tcl_MutexLock(mutexPtr)
sl@0:     Tcl_Mutex *mutexPtr;
sl@0: {
sl@0: }
sl@0: 
sl@0: #undef Tcl_MutexUnlock
sl@0: EXPORT_C void
sl@0: Tcl_MutexUnlock(mutexPtr)
sl@0:     Tcl_Mutex *mutexPtr;
sl@0: {
sl@0: }
sl@0: #endif