diff options
Diffstat (limited to 'win/tclWinThrd.c')
-rw-r--r-- | win/tclWinThrd.c | 493 |
1 files changed, 214 insertions, 279 deletions
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index 11d3870..1c9d483 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -5,18 +5,23 @@ * * Copyright (c) 1998 by Sun Microsystems, Inc. * Copyright (c) 1999 by Scriptics Corporation + * Copyright (c) 2008 by George Peter Staplin * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclWinThrd.c,v 1.39 2005/07/24 22:56:50 dkf Exp $ */ #include "tclWinInt.h" -#include <fcntl.h> -#include <io.h> -#include <sys/stat.h> +#include <float.h> + +/* Workaround for mingw versions which don't provide this in float.h */ +#ifndef _MCW_EM +# define _MCW_EM 0x0008001F /* Error masks */ +# define _MCW_RC 0x00000300 /* Rounding */ +# define _MCW_PC 0x00030000 /* Precision */ +_CRTIMP unsigned int __cdecl _controlfp (unsigned int unNew, unsigned int unMask); +#endif /* * This is the master lock used to serialize access to other serialization @@ -43,8 +48,10 @@ static CRITICAL_SECTION initLock; #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 */ @@ -92,13 +99,11 @@ static Tcl_ThreadDataKey dataKey; * ThreadSpecificData is created. * WIN_THREAD_RUNNING Running, not waiting. * WIN_THREAD_BLOCKED Waiting, or trying to wait. - * WIN_THREAD_DEAD Dying - no per-thread event anymore. */ #define WIN_THREAD_UNINIT 0x0 #define WIN_THREAD_RUNNING 0x1 #define WIN_THREAD_BLOCKED 0x2 -#define WIN_THREAD_DEAD 0x4 /* * The per condition queue pointers and the Mutex used to serialize access to @@ -127,6 +132,66 @@ typedef struct allocMutex { #endif /* USE_THREAD_ALLOC */ /* + * The per thread data passed from TclpThreadCreate + * to TclWinThreadStart. + */ + +typedef struct WinThread { + LPTHREAD_START_ROUTINE lpStartAddress; /* Original startup routine */ + LPVOID lpParameter; /* Original startup data */ + unsigned int fpControl; /* Floating point control word from the + * main thread */ +} WinThread; + + +/* + *---------------------------------------------------------------------- + * + * TclWinThreadStart -- + * + * This procedure is the entry point for all new threads created + * by Tcl on Windows. + * + * Results: + * Various, depending on the result of the wrapped thread start + * routine. + * + * Side effects: + * Arbitrary, since user code is executed. + * + *---------------------------------------------------------------------- + */ + +static DWORD WINAPI +TclWinThreadStart( + LPVOID lpParameter) /* The WinThread structure pointer passed + * from TclpThreadCreate */ +{ + WinThread *winThreadPtr = (WinThread *) lpParameter; + unsigned int fpmask; + LPTHREAD_START_ROUTINE lpOrigStartAddress; + LPVOID lpOrigParameter; + + if (!winThreadPtr) { + return TCL_ERROR; + } + + fpmask = _MCW_EM | _MCW_RC | _MCW_PC; + +#if defined(_MSC_VER) && _MSC_VER >= 1200 + fpmask |= _MCW_DN; +#endif + + _controlfp(winThreadPtr->fpControl, fpmask); + + lpOrigStartAddress = winThreadPtr->lpStartAddress; + lpOrigParameter = winThreadPtr->lpParameter; + + ckfree((char *)winThreadPtr); + return lpOrigStartAddress(lpOrigParameter); +} + +/* *---------------------------------------------------------------------- * * TclpThreadCreate -- @@ -144,25 +209,35 @@ typedef struct allocMutex { */ 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. */ { + WinThread *winThreadPtr; /* Per-thread startup info */ HANDLE tHandle; + winThreadPtr = (WinThread *)ckalloc(sizeof(WinThread)); + winThreadPtr->lpStartAddress = (LPTHREAD_START_ROUTINE) proc; + winThreadPtr->lpParameter = clientData; + winThreadPtr->fpControl = _controlfp(0, 0); + 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); + tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize, + (Tcl_ThreadCreateProc*) TclWinThreadStart, winThreadPtr, + 0, (unsigned *)idPtr); #else tHandle = CreateThread(NULL, (DWORD) stackSize, - (LPTHREAD_START_ROUTINE) proc, (LPVOID) clientData, - (DWORD) 0, (LPDWORD)idPtr); + TclWinThreadStart, winThreadPtr, 0, (LPDWORD)idPtr); #endif if (tHandle == NULL) { @@ -202,9 +277,9 @@ 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 +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); @@ -227,8 +302,8 @@ Tcl_JoinThread(threadId, result) */ void -TclpThreadExit(status) - int status; +TclpThreadExit( + int status) { EnterCriticalSection(&joinLock); TclSignalExitThread(Tcl_GetCurrentThread(), status); @@ -258,9 +333,9 @@ TclpThreadExit(status) */ Tcl_ThreadId -Tcl_GetCurrentThread() +Tcl_GetCurrentThread(void) { - return (Tcl_ThreadId) GetCurrentThreadId(); + return (Tcl_ThreadId)(size_t)GetCurrentThreadId(); } /* @@ -283,7 +358,7 @@ Tcl_GetCurrentThread() */ void -TclpInitLock() +TclpInitLock(void) { if (!init) { /* @@ -319,7 +394,7 @@ TclpInitLock() */ void -TclpInitUnlock() +TclpInitUnlock(void) { LeaveCriticalSection(&initLock); } @@ -345,7 +420,7 @@ TclpInitUnlock() */ void -TclpMasterLock() +TclpMasterLock(void) { if (!init) { /* @@ -381,7 +456,7 @@ TclpMasterLock() */ void -TclpMasterUnlock() +TclpMasterUnlock(void) { LeaveCriticalSection(&masterLock); } @@ -406,11 +481,11 @@ TclpMasterUnlock() */ Tcl_Mutex * -Tcl_GetAllocMutex() +Tcl_GetAllocMutex(void) { #ifdef TCL_THREADS if (!allocOnce) { - InitializeCriticalSection(&allocLock); + InitializeCriticalSection(&allocLock.crit); allocOnce = 1; } return &allocLockPtr; @@ -438,7 +513,7 @@ Tcl_GetAllocMutex() */ void -TclFinalizeLock() +TclFinalizeLock(void) { MASTER_LOCK; DeleteCriticalSection(&joinLock); @@ -452,7 +527,7 @@ TclFinalizeLock() #ifdef TCL_THREADS if (allocOnce) { - DeleteCriticalSection(&allocLock); + DeleteCriticalSection(&allocLock.crit); allocOnce = 0; } #endif @@ -469,7 +544,7 @@ TclFinalizeLock() #ifdef TCL_THREADS /* locally used prototype */ -static void FinalizeConditionEvent(ClientData data); +static void FinalizeConditionEvent(ClientData data); /* *---------------------------------------------------------------------- @@ -489,10 +564,11 @@ static void FinalizeConditionEvent(ClientData data); */ void -Tcl_MutexLock(mutexPtr) - Tcl_Mutex *mutexPtr; /* The lock */ +Tcl_MutexLock( + Tcl_Mutex *mutexPtr) /* The lock */ { CRITICAL_SECTION *csPtr; + if (*mutexPtr == NULL) { MASTER_LOCK; @@ -501,7 +577,7 @@ Tcl_MutexLock(mutexPtr) */ if (*mutexPtr == NULL) { - csPtr = (CRITICAL_SECTION *) ckalloc(sizeof(CRITICAL_SECTION)); + csPtr = ckalloc(sizeof(CRITICAL_SECTION)); InitializeCriticalSection(csPtr); *mutexPtr = (Tcl_Mutex)csPtr; TclRememberMutex(mutexPtr); @@ -529,10 +605,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); } @@ -554,13 +631,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(csPtr); *mutexPtr = NULL; } } @@ -568,206 +646,6 @@ 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) { - Tcl_Panic("TlsAlloc failed from TclpThreadDataKeyInit!"); - /* This should have been a fatal error. */ - } - - *indexPtr = newKey; - *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; - } - result = TlsGetValue(*indexPtr); - if ((result == NULL) && (GetLastError() != NO_ERROR)) { - Tcl_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) { - Tcl_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 /* USE_THREAD_ALLOC && !TCL_MEM_DEBUG */ - - ckfree((char *)result); - success = TlsSetValue(*indexPtr, (void *)NULL); - if (!success) { - Tcl_Panic("TlsSetValue failed from TclpFinalizeThreadData!"); - } - } else if (GetLastError() != NO_ERROR) { - Tcl_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) { - Tcl_Panic("TlsFree failed from TclpFinalizeThreadDataKey!"); - } - ckfree((char *)indexPtr); - *keyPtr = NULL; - } -} - -/* - *---------------------------------------------------------------------- - * * Tcl_ConditionWait -- * * This procedure is invoked to wait on a condition variable. The mutex @@ -788,10 +666,10 @@ TclpFinalizeThreadDataKey(keyPtr) */ 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 **) */ + const Tcl_Time *timePtr) /* Timeout on waiting period */ { WinCondition *winCondPtr; /* Per-condition queue head */ CRITICAL_SECTION *csPtr; /* Caller's Mutex, after casting */ @@ -800,14 +678,6 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) int doExit = 0; /* True if we need to do exit setup */ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (tsdPtr->flags & WIN_THREAD_DEAD) { - /* - * No more per-thread event on which to wait. - */ - - return; - } - /* * Self initialize the two parts of the condition. The per-condition and * per-thread parts need to be handled independently. @@ -838,8 +708,7 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) * and initializing that may drop back into the Master Lock. */ - Tcl_CreateThreadExitHandler(FinalizeConditionEvent, - (ClientData) tsdPtr); + Tcl_CreateThreadExitHandler(FinalizeConditionEvent, tsdPtr); } } @@ -851,11 +720,11 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) */ if (*condPtr == NULL) { - winCondPtr = (WinCondition *)ckalloc(sizeof(WinCondition)); + winCondPtr = ckalloc(sizeof(WinCondition)); InitializeCriticalSection(&winCondPtr->condLock); winCondPtr->firstPtr = NULL; winCondPtr->lastPtr = NULL; - *condPtr = (Tcl_Condition)winCondPtr; + *condPtr = (Tcl_Condition) winCondPtr; TclRememberCondition(condPtr); } MASTER_UNLOCK; @@ -900,7 +769,8 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) while (!timeout && (tsdPtr->flags & WIN_THREAD_BLOCKED)) { ResetEvent(tsdPtr->condEvent); LeaveCriticalSection(&winCondPtr->condLock); - if (WaitForSingleObject(tsdPtr->condEvent, wtime) == WAIT_TIMEOUT) { + if (WaitForSingleObjectEx(tsdPtr->condEvent, wtime, + TRUE) == WAIT_TIMEOUT) { timeout = 1; } EnterCriticalSection(&winCondPtr->condLock); @@ -960,14 +830,19 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) */ void -Tcl_ConditionNotify(condPtr) - Tcl_Condition *condPtr; +Tcl_ConditionNotify( + Tcl_Condition *condPtr) { WinCondition *winCondPtr; ThreadSpecificData *tsdPtr; + if (*condPtr != NULL) { winCondPtr = *((WinCondition **)condPtr); + if (winCondPtr == NULL) { + return; + } + /* * Loop through all the threads waiting on the condition and notify * them (i.e., broadcast semantics). The queue manipulation is guarded @@ -1013,11 +888,12 @@ Tcl_ConditionNotify(condPtr) */ static void -FinalizeConditionEvent(data) - ClientData data; +FinalizeConditionEvent( + ClientData data) { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) data; - tsdPtr->flags = WIN_THREAD_DEAD; + + tsdPtr->flags = WIN_THREAD_UNINIT; CloseHandle(tsdPtr->condEvent); } @@ -1041,8 +917,8 @@ FinalizeConditionEvent(data) */ void -TclpFinalizeCondition(condPtr) - Tcl_Condition *condPtr; +TclpFinalizeCondition( + Tcl_Condition *condPtr) { WinCondition *winCondPtr = *(WinCondition **)condPtr; @@ -1055,11 +931,14 @@ TclpFinalizeCondition(condPtr) if (winCondPtr != NULL) { DeleteCriticalSection(&winCondPtr->condLock); - ckfree((char *)winCondPtr); + ckfree(winCondPtr); *condPtr = NULL; } } + + + /* * Additions by AOL for specialized thread memory allocator. */ @@ -1080,8 +959,8 @@ 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; @@ -1095,7 +974,7 @@ TclpFreeAllocMutex(mutex) void * TclpGetAllocCache(void) { - VOID *result; + void *result; if (!once) { /* @@ -1112,23 +991,25 @@ TclpGetAllocCache(void) result = TlsGetValue(tlsKey); if ((result == NULL) && (GetLastError() != NO_ERROR)) { - Tcl_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) { - Tcl_Panic("TlsSetValue failed from TclpSetAllocCache!"); + Tcl_Panic("TlsSetValue failed from TclpSetAllocCache"); } } void -TclpFreeAllocCache(void *ptr) +TclpFreeAllocCache( + void *ptr) { BOOL success; @@ -1141,7 +1022,7 @@ TclpFreeAllocCache(void *ptr) TclFreeAllocCache(ptr); success = TlsSetValue(tlsKey, NULL); if (!success) { - panic("TlsSetValue failed from TclpFreeAllocCache!"); + Tcl_Panic("TlsSetValue failed from TclpFreeAllocCache"); } } else if (once) { /* @@ -1151,14 +1032,68 @@ TclpFreeAllocCache(void *ptr) success = TlsFree(tlsKey); if (!success) { - Tcl_Panic("TlsFree failed from TclpFreeAllocCache!"); + Tcl_Panic("TlsFree failed from TclpFreeAllocCache"); } once = 0; /* reset for next time. */ } } - #endif /* USE_THREAD_ALLOC */ + + +void * +TclpThreadCreateKey(void) +{ + DWORD *key; + + key = TclpSysAlloc(sizeof *key, 0); + if (key == NULL) { + Tcl_Panic("unable to allocate thread key!"); + } + + *key = TlsAlloc(); + + if (*key == TLS_OUT_OF_INDEXES) { + Tcl_Panic("unable to allocate thread-local storage"); + } + + return key; +} + +void +TclpThreadDeleteKey( + void *keyPtr) +{ + DWORD *key = keyPtr; + + if (!TlsFree(*key)) { + Tcl_Panic("unable to delete key"); + } + + TclpSysFree(keyPtr); +} + +void +TclpThreadSetMasterTSD( + void *tsdKeyPtr, + void *ptr) +{ + DWORD *key = tsdKeyPtr; + + if (!TlsSetValue(*key, ptr)) { + Tcl_Panic("unable to set master TSD value"); + } +} + +void * +TclpThreadGetMasterTSD( + void *tsdKeyPtr) +{ + DWORD *key = tsdKeyPtr; + + return TlsGetValue(*key); +} + #endif /* TCL_THREADS */ /* |