diff options
Diffstat (limited to 'win/tclWinThrd.c')
-rw-r--r-- | win/tclWinThrd.c | 708 |
1 files changed, 272 insertions, 436 deletions
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index 13fd411..4a4e7f4 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -1,4 +1,4 @@ -/* +/* * tclWinThread.c -- * * This file implements the Windows-specific thread operations. @@ -6,25 +6,23 @@ * Copyright (c) 1998 by Sun Microsystems, Inc. * Copyright (c) 1999 by Scriptics Corporation * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclWinInt.h" -#include <fcntl.h> -#include <io.h> #include <sys/stat.h> /* - * This is the master lock used to serialize access to other - * serialization data structures. + * This is the master lock used to serialize access to other serialization + * data structures. */ static CRITICAL_SECTION masterLock; static int init = 0; -#define MASTER_LOCK TclpMasterLock() -#define MASTER_UNLOCK TclpMasterUnlock() +#define MASTER_LOCK TclpMasterLock() +#define MASTER_UNLOCK TclpMasterUnlock() /* @@ -35,38 +33,39 @@ static int init = 0; static CRITICAL_SECTION initLock; /* - * allocLock is used by Tcl's version of malloc for synchronization. - * For obvious reasons, cannot use any dyamically allocated storage. + * allocLock is used by Tcl's version of malloc for synchronization. For + * obvious reasons, cannot use any dyamically allocated storage. */ #ifdef TCL_THREADS -static CRITICAL_SECTION allocLock; -static Tcl_Mutex allocLockPtr = (Tcl_Mutex) &allocLock; +static struct Tcl_Mutex_ { + CRITICAL_SECTION crit; +} allocLock; +static Tcl_Mutex allocLockPtr = &allocLock; static int allocOnce = 0; #endif /* TCL_THREADS */ /* * The joinLock serializes Create- and ExitThread. This is necessary to - * prevent a race where a new joinable thread exits before the creating - * thread had the time to create the necessary data structures in the - * emulation layer. + * prevent a race where a new joinable thread exits before the creating thread + * had the time to create the necessary data structures in the emulation + * layer. */ static CRITICAL_SECTION joinLock; /* - * Condition variables are implemented with a combination of a - * per-thread Windows Event and a per-condition waiting queue. - * The idea is that each thread has its own Event that it waits - * on when it is doing a ConditionWait; it uses the same event for - * all condition variables because it only waits on one at a time. - * Each condition variable has a queue of waiting threads, and a - * mutex used to serialize access to this queue. - * - * Special thanks to David Nichols and - * Jim Davidson for advice on the Condition Variable implementation. + * Condition variables are implemented with a combination of a per-thread + * Windows Event and a per-condition waiting queue. The idea is that each + * thread has its own Event that it waits on when it is doing a ConditionWait; + * it uses the same event for all condition variables because it only waits on + * one at a time. Each condition variable has a queue of waiting threads, and + * a mutex used to serialize access to this queue. + * + * Special thanks to David Nichols and Jim Davidson for advice on the + * Condition Variable implementation. */ /* @@ -86,42 +85,42 @@ static Tcl_ThreadDataKey dataKey; #endif /* TCL_THREADS */ /* - * Additions by AOL for specialized thread memory allocator. - */ - -#if defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG) -static int once; -static DWORD tlsKey; - -typedef struct allocMutex { - Tcl_Mutex tlock; - CRITICAL_SECTION wlock; -} allocMutex; -#endif - -/* * State bits for the thread. - * WIN_THREAD_UNINIT Uninitialized. Must be zero because - * of the way ThreadSpecificData is created. + * WIN_THREAD_UNINIT Uninitialized. Must be zero because of the way + * ThreadSpecificData is created. * WIN_THREAD_RUNNING Running, not waiting. * WIN_THREAD_BLOCKED Waiting, or trying to wait. - */ + */ #define WIN_THREAD_UNINIT 0x0 #define WIN_THREAD_RUNNING 0x1 #define WIN_THREAD_BLOCKED 0x2 /* - * The per condition queue pointers and the - * Mutex used to serialize access to the queue. + * The per condition queue pointers and the Mutex used to serialize access to + * the queue. */ typedef struct WinCondition { - CRITICAL_SECTION condLock; /* Lock to serialize queuing on the condition */ + CRITICAL_SECTION condLock; /* Lock to serialize queuing on the + * condition. */ struct ThreadSpecificData *firstPtr; /* Queue pointers */ struct ThreadSpecificData *lastPtr; } WinCondition; +/* + * Additions by AOL for specialized thread memory allocator. + */ + +#ifdef USE_THREAD_ALLOC +static int once; +static DWORD tlsKey; + +typedef struct allocMutex { + Tcl_Mutex tlock; + CRITICAL_SECTION wlock; +} allocMutex; +#endif /* USE_THREAD_ALLOC */ /* *---------------------------------------------------------------------- @@ -131,8 +130,8 @@ typedef struct WinCondition { * This procedure creates a new thread. * * Results: - * TCL_OK if the thread could be created. The thread ID is - * returned in a parameter. + * TCL_OK if the thread could be created. The thread ID is returned in a + * parameter. * * Side effects: * A new thread is created. @@ -141,21 +140,25 @@ typedef struct WinCondition { */ int -TclpThreadCreate(idPtr, proc, clientData, stackSize, flags) - Tcl_ThreadId *idPtr; /* Return, the ID of the thread */ - Tcl_ThreadCreateProc proc; /* Main() function of the thread */ - ClientData clientData; /* The one argument to Main() */ - int stackSize; /* Size of stack for the new thread */ - int flags; /* Flags controlling behaviour of - * the new thread */ +TclpThreadCreate( + Tcl_ThreadId *idPtr, /* Return, the ID of the thread. */ + Tcl_ThreadCreateProc proc, /* Main() function of the thread. */ + ClientData clientData, /* The one argument to Main(). */ + int stackSize, /* Size of stack for the new thread. */ + int flags) /* Flags controlling behaviour of the new + * thread. */ { HANDLE tHandle; EnterCriticalSection(&joinLock); + *idPtr = 0; /* must initialize as Tcl_Thread is a pointer and + * on WIN64 sizeof void* != sizeof unsigned + */ + #if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__) tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize, proc, - clientData, 0, (unsigned *)idPtr); + clientData, 0, (unsigned *)idPtr); #else tHandle = CreateThread(NULL, (DWORD) stackSize, (LPTHREAD_START_ROUTINE) proc, (LPVOID) clientData, @@ -163,11 +166,11 @@ TclpThreadCreate(idPtr, proc, clientData, stackSize, flags) #endif if (tHandle == NULL) { - LeaveCriticalSection(&joinLock); + LeaveCriticalSection(&joinLock); return TCL_ERROR; } else { - if (flags & TCL_THREAD_JOINABLE) { - TclRememberJoinableThread (*idPtr); + if (flags & TCL_THREAD_JOINABLE) { + TclRememberJoinableThread(*idPtr); } /* @@ -199,13 +202,12 @@ TclpThreadCreate(idPtr, proc, clientData, stackSize, flags) */ int -Tcl_JoinThread(threadId, result) - Tcl_ThreadId threadId; /* Id of the thread to wait upon */ - int* result; /* Reference to the storage the result - * of the thread we wait upon will be - * written into. */ +Tcl_JoinThread( + Tcl_ThreadId threadId, /* Id of the thread to wait upon */ + int *result) /* Reference to the storage the result of the + * thread we wait upon will be written into. */ { - return TclJoinThread (threadId, result); + return TclJoinThread(threadId, result); } /* @@ -225,11 +227,11 @@ Tcl_JoinThread(threadId, result) */ void -TclpThreadExit(status) - int status; +TclpThreadExit( + int status) { EnterCriticalSection(&joinLock); - TclSignalExitThread (Tcl_GetCurrentThread (), status); + TclSignalExitThread(Tcl_GetCurrentThread(), status); LeaveCriticalSection(&joinLock); #if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__) @@ -256,9 +258,9 @@ TclpThreadExit(status) */ Tcl_ThreadId -Tcl_GetCurrentThread() +Tcl_GetCurrentThread(void) { - return (Tcl_ThreadId)GetCurrentThreadId(); + return (Tcl_ThreadId) GetCurrentThreadId(); } /* @@ -267,9 +269,9 @@ Tcl_GetCurrentThread() * TclpInitLock * * This procedure is used to grab a lock that serializes initialization - * and finalization of Tcl. On some platforms this may also initialize - * the mutex used to serialize creation of more mutexes and thread - * local storage keys. + * and finalization of Tcl. On some platforms this may also initialize + * the mutex used to serialize creation of more mutexes and thread local + * storage keys. * * Results: * None. @@ -281,15 +283,16 @@ Tcl_GetCurrentThread() */ void -TclpInitLock() +TclpInitLock(void) { if (!init) { /* - * There is a fundamental race here that is solved by creating - * the first Tcl interpreter in a single threaded environment. - * Once the interpreter has been created, it is safe to create - * more threads that create interpreters in parallel. + * There is a fundamental race here that is solved by creating the + * first Tcl interpreter in a single threaded environment. Once the + * interpreter has been created, it is safe to create more threads + * that create interpreters in parallel. */ + init = 1; InitializeCriticalSection(&joinLock); InitializeCriticalSection(&initLock); @@ -303,8 +306,8 @@ TclpInitLock() * * TclpInitUnlock * - * This procedure is used to release a lock that serializes initialization - * and finalization of Tcl. + * This procedure is used to release a lock that serializes + * initialization and finalization of Tcl. * * Results: * None. @@ -316,7 +319,7 @@ TclpInitLock() */ void -TclpInitUnlock() +TclpInitUnlock(void) { LeaveCriticalSection(&initLock); } @@ -326,11 +329,11 @@ TclpInitUnlock() * * TclpMasterLock * - * This procedure is used to grab a lock that serializes creation - * of mutexes, condition variables, and thread local storage keys. + * This procedure is used to grab a lock that serializes creation of + * mutexes, condition variables, and thread local storage keys. * - * This lock must be different than the initLock because the - * initLock is held during creation of syncronization objects. + * This lock must be different than the initLock because the initLock is + * held during creation of syncronization objects. * * Results: * None. @@ -342,15 +345,16 @@ TclpInitUnlock() */ void -TclpMasterLock() +TclpMasterLock(void) { if (!init) { /* - * There is a fundamental race here that is solved by creating - * the first Tcl interpreter in a single threaded environment. - * Once the interpreter has been created, it is safe to create - * more threads that create interpreters in parallel. + * There is a fundamental race here that is solved by creating the + * first Tcl interpreter in a single threaded environment. Once the + * interpreter has been created, it is safe to create more threads + * that create interpreters in parallel. */ + init = 1; InitializeCriticalSection(&joinLock); InitializeCriticalSection(&initLock); @@ -364,8 +368,8 @@ TclpMasterLock() * * TclpMasterUnlock * - * This procedure is used to release a lock that serializes creation - * and deletion of synchronization objects. + * This procedure is used to release a lock that serializes creation and + * deletion of synchronization objects. * * Results: * None. @@ -377,7 +381,7 @@ TclpMasterLock() */ void -TclpMasterUnlock() +TclpMasterUnlock(void) { LeaveCriticalSection(&masterLock); } @@ -387,13 +391,13 @@ TclpMasterUnlock() * * Tcl_GetAllocMutex * - * This procedure returns a pointer to a statically initialized - * mutex for use by the memory allocator. The alloctor must - * use this lock, because all other locks are allocated... + * This procedure returns a pointer to a statically initialized mutex for + * use by the memory allocator. The alloctor must use this lock, because + * all other locks are allocated... * * Results: - * A pointer to a mutex that is suitable for passing to - * Tcl_MutexLock and Tcl_MutexUnlock. + * A pointer to a mutex that is suitable for passing to Tcl_MutexLock and + * Tcl_MutexUnlock. * * Side effects: * None. @@ -402,11 +406,11 @@ TclpMasterUnlock() */ Tcl_Mutex * -Tcl_GetAllocMutex() +Tcl_GetAllocMutex(void) { #ifdef TCL_THREADS if (!allocOnce) { - InitializeCriticalSection(&allocLock); + InitializeCriticalSection(&allocLock.crit); allocOnce = 1; } return &allocLockPtr; @@ -420,74 +424,85 @@ Tcl_GetAllocMutex() * * TclpFinalizeLock * - * This procedure is used to destroy all private resources used in - * this file. + * This procedure is used to destroy all private resources used in this + * file. * * Results: * None. * * Side effects: - * Destroys everything private. TclpInitLock must be held - * entering this function. + * Destroys everything private. TclpInitLock must be held entering this + * function. * *---------------------------------------------------------------------- */ void -TclFinalizeLock () +TclFinalizeLock(void) { MASTER_LOCK; DeleteCriticalSection(&joinLock); - /* Destroy the critical section that we are holding! */ + + /* + * Destroy the critical section that we are holding! + */ + DeleteCriticalSection(&masterLock); init = 0; + #ifdef TCL_THREADS - DeleteCriticalSection(&allocLock); - allocOnce = 0; + if (allocOnce) { + DeleteCriticalSection(&allocLock.crit); + allocOnce = 0; + } #endif - /* Destroy the critical section that we are holding! */ + + LeaveCriticalSection(&initLock); + + /* + * Destroy the critical section that we were holding. + */ + DeleteCriticalSection(&initLock); } #ifdef TCL_THREADS /* locally used prototype */ -static void FinalizeConditionEvent(ClientData data); - +static void FinalizeConditionEvent(ClientData data); /* *---------------------------------------------------------------------- * * Tcl_MutexLock -- * - * This procedure is invoked to lock a mutex. This is a self - * initializing mutex that is automatically finalized during - * Tcl_Finalize. + * This procedure is invoked to lock a mutex. This is a self initializing + * mutex that is automatically finalized during Tcl_Finalize. * * Results: * None. * * Side effects: - * May block the current thread. The mutex is aquired when - * this returns. + * May block the current thread. The mutex is aquired when this returns. * *---------------------------------------------------------------------- */ void -Tcl_MutexLock(mutexPtr) - Tcl_Mutex *mutexPtr; /* The lock */ +Tcl_MutexLock( + Tcl_Mutex *mutexPtr) /* The lock */ { CRITICAL_SECTION *csPtr; + if (*mutexPtr == NULL) { MASTER_LOCK; - /* + /* * Double inside master lock check to avoid a race. */ if (*mutexPtr == NULL) { - csPtr = (CRITICAL_SECTION *)ckalloc(sizeof(CRITICAL_SECTION)); + csPtr = (CRITICAL_SECTION *) ckalloc(sizeof(CRITICAL_SECTION)); InitializeCriticalSection(csPtr); *mutexPtr = (Tcl_Mutex)csPtr; TclRememberMutex(mutexPtr); @@ -515,10 +530,11 @@ Tcl_MutexLock(mutexPtr) */ void -Tcl_MutexUnlock(mutexPtr) - Tcl_Mutex *mutexPtr; /* The lock */ +Tcl_MutexUnlock( + Tcl_Mutex *mutexPtr) /* The lock */ { CRITICAL_SECTION *csPtr = *((CRITICAL_SECTION **)mutexPtr); + LeaveCriticalSection(csPtr); } @@ -527,8 +543,8 @@ Tcl_MutexUnlock(mutexPtr) * * TclpFinalizeMutex -- * - * This procedure is invoked to clean up one mutex. This is only - * safe to call at the end of time. + * This procedure is invoked to clean up one mutex. This is only safe to + * call at the end of time. * * Results: * None. @@ -540,13 +556,14 @@ Tcl_MutexUnlock(mutexPtr) */ void -TclpFinalizeMutex(mutexPtr) - Tcl_Mutex *mutexPtr; +TclpFinalizeMutex( + Tcl_Mutex *mutexPtr) { CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr; + if (csPtr != NULL) { DeleteCriticalSection(csPtr); - ckfree((char *)csPtr); + ckfree((char *) csPtr); *mutexPtr = NULL; } } @@ -554,210 +571,11 @@ TclpFinalizeMutex(mutexPtr) /* *---------------------------------------------------------------------- * - * TclpThreadDataKeyInit -- - * - * This procedure initializes a thread specific data block key. - * Each thread has table of pointers to thread specific data. - * all threads agree on which table entry is used by each module. - * this is remembered in a "data key", that is just an index into - * this table. To allow self initialization, the interface - * passes a pointer to this key and the first thread to use - * the key fills in the pointer to the key. The key should be - * a process-wide static. - * - * Results: - * None. - * - * Side effects: - * Will allocate memory the first time this process calls for - * this key. In this case it modifies its argument - * to hold the pointer to information about the key. - * - *---------------------------------------------------------------------- - */ - -void -TclpThreadDataKeyInit(keyPtr) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, - * really (DWORD **) */ -{ - DWORD *indexPtr; - DWORD newKey; - - MASTER_LOCK; - if (*keyPtr == NULL) { - indexPtr = (DWORD *)ckalloc(sizeof(DWORD)); - newKey = TlsAlloc(); - if (newKey != TLS_OUT_OF_INDEXES) { - *indexPtr = newKey; - } else { - panic("TlsAlloc failed from TclpThreadDataKeyInit!"); /* this should be a fatal error */ - } - *keyPtr = (Tcl_ThreadDataKey)indexPtr; - TclRememberDataKey(keyPtr); - } - MASTER_UNLOCK; -} - -/* - *---------------------------------------------------------------------- - * - * TclpThreadDataKeyGet -- - * - * 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 * -TclpThreadDataKeyGet(keyPtr) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, - * really (DWORD **) */ -{ - DWORD *indexPtr = *(DWORD **)keyPtr; - LPVOID result; - if (indexPtr == NULL) { - return NULL; - } else { - result = TlsGetValue(*indexPtr); - if ((result == NULL) && (GetLastError() != NO_ERROR)) { - panic("TlsGetValue failed from TclpThreadDataKeyGet!"); - } - return result; - } -} - -/* - *---------------------------------------------------------------------- - * - * TclpThreadDataKeySet -- - * - * This procedure sets the pointer to a block of thread local storage. - * - * Results: - * None. - * - * Side effects: - * Sets up the thread so future calls to TclpThreadDataKeyGet with - * this key will return the data pointer. - * - *---------------------------------------------------------------------- - */ - -void -TclpThreadDataKeySet(keyPtr, data) - Tcl_ThreadDataKey *keyPtr; /* Identifier for the data chunk, - * really (pthread_key_t **) */ - VOID *data; /* Thread local storage */ -{ - DWORD *indexPtr = *(DWORD **)keyPtr; - BOOL success; - success = TlsSetValue(*indexPtr, (void *)data); - if (!success) { - panic("TlsSetValue failed from TclpThreadDataKeySet!"); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclpFinalizeThreadData -- - * - * This procedure cleans up the thread-local storage. This is - * called once for each thread. - * - * Results: - * None. - * - * Side effects: - * Frees up the memory. - * - *---------------------------------------------------------------------- - */ - -void -TclpFinalizeThreadData(keyPtr) - Tcl_ThreadDataKey *keyPtr; -{ - VOID *result; - DWORD *indexPtr; - BOOL success; - - if (*keyPtr != NULL) { - indexPtr = *(DWORD **)keyPtr; - result = (VOID *)TlsGetValue(*indexPtr); - if (result != NULL) { -#if defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG) - if (indexPtr == &tlsKey) { - TclpFreeAllocCache(result); - return; - } -#endif - ckfree((char *)result); - success = TlsSetValue(*indexPtr, (void *)NULL); - if (!success) { - panic("TlsSetValue failed from TclpFinalizeThreadData!"); - } - } else { - if (GetLastError() != NO_ERROR) { - panic("TlsGetValue failed from TclpFinalizeThreadData!"); - } - } - } -} - -/* - *---------------------------------------------------------------------- - * - * TclpFinalizeThreadDataKey -- - * - * This procedure is invoked to clean up one key. This is a - * process-wide storage identifier. The thread finalization code - * cleans up the thread local storage itself. - * - * This assumes the master lock is held. - * - * Results: - * None. - * - * Side effects: - * The key is deallocated. - * - *---------------------------------------------------------------------- - */ - -void -TclpFinalizeThreadDataKey(keyPtr) - Tcl_ThreadDataKey *keyPtr; -{ - DWORD *indexPtr; - BOOL success; - if (*keyPtr != NULL) { - indexPtr = *(DWORD **)keyPtr; - success = TlsFree(*indexPtr); - if (!success) { - panic("TlsFree failed from TclpFinalizeThreadDataKey!"); - } - ckfree((char *)indexPtr); - *keyPtr = NULL; - } -} - -/* - *---------------------------------------------------------------------- - * * Tcl_ConditionWait -- * - * This procedure is invoked to wait on a condition variable. - * The mutex is atomically released as part of the wait, and - * automatically grabbed when the condition is signaled. + * This procedure is invoked to wait on a condition variable. The mutex + * is atomically released as part of the wait, and automatically grabbed + * when the condition is signaled. * * The mutex must be held when this procedure is called. * @@ -765,18 +583,18 @@ TclpFinalizeThreadDataKey(keyPtr) * None. * * Side effects: - * May block the current thread. The mutex is aquired when - * this returns. Will allocate memory for a HANDLE - * and initialize this the first time this Tcl_Condition is used. + * May block the current thread. The mutex is aquired when this returns. + * Will allocate memory for a HANDLE and initialize this the first time + * this Tcl_Condition is used. * *---------------------------------------------------------------------- */ void -Tcl_ConditionWait(condPtr, mutexPtr, timePtr) - Tcl_Condition *condPtr; /* Really (WinCondition **) */ - Tcl_Mutex *mutexPtr; /* Really (CRITICAL_SECTION **) */ - Tcl_Time *timePtr; /* Timeout on waiting period */ +Tcl_ConditionWait( + Tcl_Condition *condPtr, /* Really (WinCondition **) */ + Tcl_Mutex *mutexPtr, /* Really (CRITICAL_SECTION **) */ + Tcl_Time *timePtr) /* Timeout on waiting period */ { WinCondition *winCondPtr; /* Per-condition queue head */ CRITICAL_SECTION *csPtr; /* Caller's Mutex, after casting */ @@ -786,21 +604,20 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* - * Self initialize the two parts of the condition. - * The per-condition and per-thread parts need to be - * handled independently. + * Self initialize the two parts of the condition. The per-condition and + * per-thread parts need to be handled independently. */ if (tsdPtr->flags == WIN_THREAD_UNINIT) { MASTER_LOCK; - /* + /* * Create the per-thread event and queue pointers. */ if (tsdPtr->flags == WIN_THREAD_UNINIT) { tsdPtr->condEvent = CreateEvent(NULL, TRUE /* manual reset */, - FALSE /* non signaled */, NULL); + FALSE /* non signaled */, NULL); tsdPtr->nextPtr = NULL; tsdPtr->prevPtr = NULL; tsdPtr->flags = WIN_THREAD_RUNNING; @@ -810,13 +627,12 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) if (doExit) { /* - * Create a per-thread exit handler to clean up the condEvent. - * We must be careful to do this outside the Master Lock - * because Tcl_CreateThreadExitHandler uses its own - * ThreadSpecificData, and initializing that may drop - * back into the Master Lock. + * Create a per-thread exit handler to clean up the condEvent. We + * must be careful to do this outside the Master Lock because + * Tcl_CreateThreadExitHandler uses its own ThreadSpecificData, + * and initializing that may drop back into the Master Lock. */ - + Tcl_CreateThreadExitHandler(FinalizeConditionEvent, (ClientData) tsdPtr); } @@ -830,11 +646,11 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) */ if (*condPtr == NULL) { - winCondPtr = (WinCondition *)ckalloc(sizeof(WinCondition)); + winCondPtr = (WinCondition *) ckalloc(sizeof(WinCondition)); InitializeCriticalSection(&winCondPtr->condLock); winCondPtr->firstPtr = NULL; winCondPtr->lastPtr = NULL; - *condPtr = (Tcl_Condition)winCondPtr; + *condPtr = (Tcl_Condition) winCondPtr; TclRememberCondition(condPtr); } MASTER_UNLOCK; @@ -848,8 +664,8 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) } /* - * Queue the thread on the condition, using - * the per-condition lock for serialization. + * Queue the thread on the condition, using the per-condition lock for + * serialization. */ tsdPtr->flags = WIN_THREAD_BLOCKED; @@ -858,22 +674,22 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) tsdPtr->prevPtr = winCondPtr->lastPtr; /* A: */ winCondPtr->lastPtr = tsdPtr; if (tsdPtr->prevPtr != NULL) { - tsdPtr->prevPtr->nextPtr = tsdPtr; + tsdPtr->prevPtr->nextPtr = tsdPtr; } if (winCondPtr->firstPtr == NULL) { - winCondPtr->firstPtr = tsdPtr; + winCondPtr->firstPtr = tsdPtr; } /* * Unlock the caller's mutex and wait for the condition, or a timeout. - * There is a minor issue here in that we don't count down the - * timeout if we get notified, but another thread grabs the condition - * before we do. In that race condition we'll wait again for the - * full timeout. Timed waits are dubious anyway. Either you have - * the locking protocol wrong and are masking a deadlock, - * or you are using conditions to pause your thread. + * There is a minor issue here in that we don't count down the timeout if + * we get notified, but another thread grabs the condition before we do. + * In that race condition we'll wait again for the full timeout. Timed + * waits are dubious anyway. Either you have the locking protocol wrong + * and are masking a deadlock, or you are using conditions to pause your + * thread. */ - + LeaveCriticalSection(csPtr); timeout = 0; while (!timeout && (tsdPtr->flags & WIN_THREAD_BLOCKED)) { @@ -886,32 +702,32 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) } /* - * Be careful on timeouts because the signal might arrive right around - * the time limit and someone else could have taken us off the queue. + * Be careful on timeouts because the signal might arrive right around the + * time limit and someone else could have taken us off the queue. */ - + if (timeout) { if (tsdPtr->flags & WIN_THREAD_RUNNING) { timeout = 0; } else { /* - * When dequeuing, we can leave the tsdPtr->nextPtr - * and tsdPtr->prevPtr with dangling pointers because - * they are reinitialilzed w/out reading them when the - * thread is enqueued later. + * When dequeuing, we can leave the tsdPtr->nextPtr and + * tsdPtr->prevPtr with dangling pointers because they are + * reinitialilzed w/out reading them when the thread is enqueued + * later. */ - if (winCondPtr->firstPtr == tsdPtr) { - winCondPtr->firstPtr = tsdPtr->nextPtr; - } else { - tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; - } - if (winCondPtr->lastPtr == tsdPtr) { - winCondPtr->lastPtr = tsdPtr->prevPtr; - } else { - tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; - } - tsdPtr->flags = WIN_THREAD_RUNNING; + if (winCondPtr->firstPtr == tsdPtr) { + winCondPtr->firstPtr = tsdPtr->nextPtr; + } else { + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; + } + if (winCondPtr->lastPtr == tsdPtr) { + winCondPtr->lastPtr = tsdPtr->prevPtr; + } else { + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; + } + tsdPtr->flags = WIN_THREAD_RUNNING; } } @@ -926,8 +742,8 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) * * This procedure is invoked to signal a condition variable. * - * The mutex must be held during this call to avoid races, - * but this interface does not enforce that. + * The mutex must be held during this call to avoid races, but this + * interface does not enforce that. * * Results: * None. @@ -939,13 +755,13 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) */ void -Tcl_ConditionNotify(condPtr) - Tcl_Condition *condPtr; +Tcl_ConditionNotify( + Tcl_Condition *condPtr) { WinCondition *winCondPtr; ThreadSpecificData *tsdPtr; - if (condPtr != NULL) { + if (*condPtr != NULL) { winCondPtr = *((WinCondition **)condPtr); if (winCondPtr == NULL) { @@ -953,9 +769,9 @@ Tcl_ConditionNotify(condPtr) } /* - * Loop through all the threads waiting on the condition - * and notify them (i.e., broadcast semantics). The queue - * manipulation is guarded by the per-condition coordinating mutex. + * Loop through all the threads waiting on the condition and notify + * them (i.e., broadcast semantics). The queue manipulation is guarded + * by the per-condition coordinating mutex. */ EnterCriticalSection(&winCondPtr->condLock); @@ -973,7 +789,7 @@ Tcl_ConditionNotify(condPtr) LeaveCriticalSection(&winCondPtr->condLock); } else { /* - * Noone has used the condition variable, so there are no waiters. + * No-one has used the condition variable, so there are no waiters. */ } } @@ -983,9 +799,9 @@ Tcl_ConditionNotify(condPtr) * * FinalizeConditionEvent -- * - * This procedure is invoked to clean up the per-thread - * event used to implement condition waiting. - * This is only safe to call at the end of time. + * This procedure is invoked to clean up the per-thread event used to + * implement condition waiting. This is only safe to call at the end of + * time. * * Results: * None. @@ -997,10 +813,11 @@ Tcl_ConditionNotify(condPtr) */ static void -FinalizeConditionEvent(data) - ClientData data; +FinalizeConditionEvent( + ClientData data) { - ThreadSpecificData *tsdPtr = (ThreadSpecificData *)data; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) data; + tsdPtr->flags = WIN_THREAD_UNINIT; CloseHandle(tsdPtr->condEvent); } @@ -1010,8 +827,8 @@ FinalizeConditionEvent(data) * * TclpFinalizeCondition -- * - * This procedure is invoked to clean up a condition variable. - * This is only safe to call at the end of time. + * This procedure is invoked to clean up a condition variable. This is + * only safe to call at the end of time. * * This assumes the Master Lock is held. * @@ -1025,30 +842,33 @@ FinalizeConditionEvent(data) */ void -TclpFinalizeCondition(condPtr) - Tcl_Condition *condPtr; +TclpFinalizeCondition( + Tcl_Condition *condPtr) { WinCondition *winCondPtr = *(WinCondition **)condPtr; /* - * Note - this is called long after the thread-local storage is - * reclaimed. The per-thread condition waiting event is - * reclaimed earlier in a per-thread exit handler, which is - * called before thread local storage is reclaimed. + * Note - this is called long after the thread-local storage is reclaimed. + * The per-thread condition waiting event is reclaimed earlier in a + * per-thread exit handler, which is called before thread local storage is + * reclaimed. */ if (winCondPtr != NULL) { DeleteCriticalSection(&winCondPtr->condLock); - ckfree((char *)winCondPtr); + ckfree((char *) winCondPtr); *condPtr = NULL; } } + + + /* * Additions by AOL for specialized thread memory allocator. */ +#ifdef USE_THREAD_ALLOC -#if defined(USE_THREAD_ALLOC) && !defined(TCL_MEM_DEBUG) Tcl_Mutex * TclpNewAllocMutex(void) { @@ -1056,7 +876,7 @@ TclpNewAllocMutex(void) lockPtr = malloc(sizeof(struct allocMutex)); if (lockPtr == NULL) { - panic("could not allocate lock"); + Tcl_Panic("could not allocate lock"); } lockPtr->tlock = (Tcl_Mutex) &lockPtr->wlock; InitializeCriticalSection(&lockPtr->wlock); @@ -1064,11 +884,14 @@ TclpNewAllocMutex(void) } void -TclpFreeAllocMutex(mutex) - Tcl_Mutex *mutex; /* The alloc mutex to free. */ +TclpFreeAllocMutex( + Tcl_Mutex *mutex) /* The alloc mutex to free. */ { - allocMutex* lockPtr = (allocMutex*) mutex; - if (!lockPtr) return; + allocMutex *lockPtr = (allocMutex *) mutex; + + if (!lockPtr) { + return; + } DeleteCriticalSection(&lockPtr->wlock); free(lockPtr); } @@ -1080,60 +903,73 @@ TclpGetAllocCache(void) if (!once) { /* - * We need to make sure that TclpFreeAllocCache is called - * on each thread that calls this, but only on threads that - * call this. + * We need to make sure that TclpFreeAllocCache is called on each + * thread that calls this, but only on threads that call this. */ - tlsKey = TlsAlloc(); + + tlsKey = TlsAlloc(); once = 1; if (tlsKey == TLS_OUT_OF_INDEXES) { - panic("could not allocate thread local storage"); + Tcl_Panic("could not allocate thread local storage"); } } result = TlsGetValue(tlsKey); if ((result == NULL) && (GetLastError() != NO_ERROR)) { - panic("TlsGetValue failed from TclpGetAllocCache!"); + Tcl_Panic("TlsGetValue failed from TclpGetAllocCache"); } return result; } void -TclpSetAllocCache(void *ptr) +TclpSetAllocCache( + void *ptr) { BOOL success; success = TlsSetValue(tlsKey, ptr); if (!success) { - panic("TlsSetValue failed from TclpSetAllocCache!"); + Tcl_Panic("TlsSetValue failed from TclpSetAllocCache"); } } void -TclpFreeAllocCache(void *ptr) +TclpFreeAllocCache( + void *ptr) { BOOL success; if (ptr != NULL) { - /* - * Called by the pthread lib when a thread exits - */ - TclFreeAllocCache(ptr); - success = TlsSetValue(tlsKey, NULL); - if (!success) { - panic("TlsSetValue failed from TclpFreeAllocCache!"); - } - } else if (once) { - /* - * Called by us in TclFinalizeThreadAlloc() during - * the library finalization initiated from Tcl_Finalize() - */ - success = TlsFree(tlsKey); - if (!success) { - Tcl_Panic("TlsFree failed from TclpFreeAllocCache!"); - } - once = 0; /* reset for next time. */ + /* + * Called by us in TclpFinalizeThreadData when a thread exits and + * destroys the tsd key which stores allocator caches. + */ + + TclFreeAllocCache(ptr); + success = TlsSetValue(tlsKey, NULL); + if (!success) { + Tcl_Panic("TlsSetValue failed from TclpFreeAllocCache"); + } + } else if (once) { + /* + * Called by us in TclFinalizeThreadAlloc() during the library + * finalization initiated from Tcl_Finalize() + */ + + success = TlsFree(tlsKey); + if (!success) { + Tcl_Panic("TlsFree failed from TclpFreeAllocCache"); + } + once = 0; /* reset for next time. */ } -} +} #endif /* USE_THREAD_ALLOC */ #endif /* TCL_THREADS */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |