diff options
Diffstat (limited to 'win/tclWinThrd.c')
| -rw-r--r-- | win/tclWinThrd.c | 287 |
1 files changed, 207 insertions, 80 deletions
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index 66a685c..b9cde72 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.41 2005/11/04 00:06:51 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 @@ -24,10 +29,7 @@ */ static CRITICAL_SECTION masterLock; -static int init = 0; -#define MASTER_LOCK TclpMasterLock() -#define MASTER_UNLOCK TclpMasterUnlock() - +static int initialized = 0; /* * This is the master lock used to serialize initialization and finalization @@ -43,8 +45,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,20 +96,18 @@ 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 * the queue. */ -typedef struct WinCondition { +typedef struct { CRITICAL_SECTION condLock; /* Lock to serialize queuing on the * condition. */ struct ThreadSpecificData *firstPtr; /* Queue pointers */ @@ -117,16 +119,72 @@ typedef struct WinCondition { */ #ifdef USE_THREAD_ALLOC -static int once; static DWORD tlsKey; -typedef struct allocMutex { +typedef struct { Tcl_Mutex tlock; CRITICAL_SECTION wlock; } allocMutex; #endif /* USE_THREAD_ALLOC */ /* + * The per thread data passed from TclpThreadCreate + * to TclWinThreadStart. + */ + +typedef struct { + 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; + LPTHREAD_START_ROUTINE lpOrigStartAddress; + LPVOID lpOrigParameter; + + if (!winThreadPtr) { + return TCL_ERROR; + } + + _controlfp(winThreadPtr->fpControl, _MCW_EM | _MCW_RC | 0x03000000 /* _MCW_DN */ +#if !defined(_WIN64) + | _MCW_PC +#endif + ); + + lpOrigStartAddress = winThreadPtr->lpStartAddress; + lpOrigParameter = winThreadPtr->lpParameter; + + ckfree(winThreadPtr); + return lpOrigStartAddress(lpOrigParameter); +} + +/* *---------------------------------------------------------------------- * * TclpThreadCreate -- @@ -146,23 +204,33 @@ typedef struct allocMutex { int TclpThreadCreate( Tcl_ThreadId *idPtr, /* Return, the ID of the thread. */ - Tcl_ThreadCreateProc proc, /* Main() function 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) { @@ -260,7 +328,7 @@ TclpThreadExit( Tcl_ThreadId Tcl_GetCurrentThread(void) { - return (Tcl_ThreadId) GetCurrentThreadId(); + return (Tcl_ThreadId)(size_t)GetCurrentThreadId(); } /* @@ -285,7 +353,7 @@ Tcl_GetCurrentThread(void) void TclpInitLock(void) { - if (!init) { + if (!initialized) { /* * There is a fundamental race here that is solved by creating the * first Tcl interpreter in a single threaded environment. Once the @@ -293,7 +361,7 @@ TclpInitLock(void) * that create interpreters in parallel. */ - init = 1; + initialized = 1; InitializeCriticalSection(&joinLock); InitializeCriticalSection(&initLock); InitializeCriticalSection(&masterLock); @@ -347,7 +415,7 @@ TclpInitUnlock(void) void TclpMasterLock(void) { - if (!init) { + if (!initialized) { /* * There is a fundamental race here that is solved by creating the * first Tcl interpreter in a single threaded environment. Once the @@ -355,7 +423,7 @@ TclpMasterLock(void) * that create interpreters in parallel. */ - init = 1; + initialized = 1; InitializeCriticalSection(&joinLock); InitializeCriticalSection(&initLock); InitializeCriticalSection(&masterLock); @@ -410,7 +478,7 @@ Tcl_GetAllocMutex(void) { #ifdef TCL_THREADS if (!allocOnce) { - InitializeCriticalSection(&allocLock); + InitializeCriticalSection(&allocLock.crit); allocOnce = 1; } return &allocLockPtr; @@ -422,7 +490,7 @@ Tcl_GetAllocMutex(void) /* *---------------------------------------------------------------------- * - * TclpFinalizeLock + * TclFinalizeLock * * This procedure is used to destroy all private resources used in this * file. @@ -440,7 +508,7 @@ Tcl_GetAllocMutex(void) void TclFinalizeLock(void) { - MASTER_LOCK; + TclpMasterLock(); DeleteCriticalSection(&joinLock); /* @@ -448,11 +516,11 @@ TclFinalizeLock(void) */ DeleteCriticalSection(&masterLock); - init = 0; + initialized = 0; #ifdef TCL_THREADS if (allocOnce) { - DeleteCriticalSection(&allocLock); + DeleteCriticalSection(&allocLock.crit); allocOnce = 0; } #endif @@ -493,20 +561,21 @@ Tcl_MutexLock( Tcl_Mutex *mutexPtr) /* The lock */ { CRITICAL_SECTION *csPtr; + if (*mutexPtr == NULL) { - MASTER_LOCK; + TclpMasterLock(); /* * Double inside master lock check to avoid a race. */ if (*mutexPtr == NULL) { - csPtr = (CRITICAL_SECTION *) ckalloc(sizeof(CRITICAL_SECTION)); + csPtr = ckalloc(sizeof(CRITICAL_SECTION)); InitializeCriticalSection(csPtr); *mutexPtr = (Tcl_Mutex)csPtr; TclRememberMutex(mutexPtr); } - MASTER_UNLOCK; + TclpMasterUnlock(); } csPtr = *((CRITICAL_SECTION **)mutexPtr); EnterCriticalSection(csPtr); @@ -533,6 +602,7 @@ Tcl_MutexUnlock( Tcl_Mutex *mutexPtr) /* The lock */ { CRITICAL_SECTION *csPtr = *((CRITICAL_SECTION **)mutexPtr); + LeaveCriticalSection(csPtr); } @@ -558,9 +628,10 @@ TclpFinalizeMutex( Tcl_Mutex *mutexPtr) { CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr; + if (csPtr != NULL) { DeleteCriticalSection(csPtr); - ckfree((char *) csPtr); + ckfree(csPtr); *mutexPtr = NULL; } } @@ -591,7 +662,7 @@ void Tcl_ConditionWait( Tcl_Condition *condPtr, /* Really (WinCondition **) */ Tcl_Mutex *mutexPtr, /* Really (CRITICAL_SECTION **) */ - Tcl_Time *timePtr) /* Timeout on waiting period */ + const Tcl_Time *timePtr) /* Timeout on waiting period */ { WinCondition *winCondPtr; /* Per-condition queue head */ CRITICAL_SECTION *csPtr; /* Caller's Mutex, after casting */ @@ -600,21 +671,13 @@ Tcl_ConditionWait( 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. */ if (tsdPtr->flags == WIN_THREAD_UNINIT) { - MASTER_LOCK; + TclpMasterLock(); /* * Create the per-thread event and queue pointers. @@ -628,7 +691,7 @@ Tcl_ConditionWait( tsdPtr->flags = WIN_THREAD_RUNNING; doExit = 1; } - MASTER_UNLOCK; + TclpMasterUnlock(); if (doExit) { /* @@ -638,27 +701,26 @@ Tcl_ConditionWait( * and initializing that may drop back into the Master Lock. */ - Tcl_CreateThreadExitHandler(FinalizeConditionEvent, - (ClientData) tsdPtr); + Tcl_CreateThreadExitHandler(FinalizeConditionEvent, tsdPtr); } } if (*condPtr == NULL) { - MASTER_LOCK; + TclpMasterLock(); /* * Initialize the per-condition queue pointers and Mutex. */ 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; + TclpMasterUnlock(); } csPtr = *((CRITICAL_SECTION **)mutexPtr); winCondPtr = *((WinCondition **)condPtr); @@ -700,7 +762,8 @@ Tcl_ConditionWait( 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); @@ -765,9 +828,14 @@ Tcl_ConditionNotify( { 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 @@ -817,7 +885,8 @@ FinalizeConditionEvent( ClientData data) { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) data; - tsdPtr->flags = WIN_THREAD_DEAD; + + tsdPtr->flags = WIN_THREAD_UNINIT; CloseHandle(tsdPtr->condEvent); } @@ -855,11 +924,14 @@ TclpFinalizeCondition( if (winCondPtr != NULL) { DeleteCriticalSection(&winCondPtr->condLock); - ckfree((char *) winCondPtr); + ckfree(winCondPtr); *condPtr = NULL; } } + + + /* * Additions by AOL for specialized thread memory allocator. */ @@ -868,9 +940,9 @@ TclpFinalizeCondition( Tcl_Mutex * TclpNewAllocMutex(void) { - struct allocMutex *lockPtr; + allocMutex *lockPtr; - lockPtr = malloc(sizeof(struct allocMutex)); + lockPtr = malloc(sizeof(allocMutex)); if (lockPtr == NULL) { Tcl_Panic("could not allocate lock"); } @@ -892,27 +964,27 @@ TclpFreeAllocMutex( free(lockPtr); } -void * -TclpGetAllocCache(void) +void +TclpInitAllocCache(void) { - VOID *result; - - 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(); - once = 1; - if (tlsKey == TLS_OUT_OF_INDEXES) { - Tcl_Panic("could not allocate thread local storage"); - } + tlsKey = TlsAlloc(); + if (tlsKey == TLS_OUT_OF_INDEXES) { + Tcl_Panic("could not allocate thread local storage"); } +} +void * +TclpGetAllocCache(void) +{ + void *result; result = TlsGetValue(tlsKey); if ((result == NULL) && (GetLastError() != NO_ERROR)) { - Tcl_Panic("TlsGetValue failed from TclpGetAllocCache!"); + Tcl_Panic("TlsGetValue failed from TclpGetAllocCache"); } return result; } @@ -924,7 +996,7 @@ TclpSetAllocCache( BOOL success; success = TlsSetValue(tlsKey, ptr); if (!success) { - Tcl_Panic("TlsSetValue failed from TclpSetAllocCache!"); + Tcl_Panic("TlsSetValue failed from TclpSetAllocCache"); } } @@ -936,16 +1008,18 @@ TclpFreeAllocCache( if (ptr != NULL) { /* - * Called by us in TclpFinalizeThreadData when a thread exits and - * destroys the tsd key which stores allocator caches. + * Called by TclFinalizeThreadAlloc() and + * TclFinalizeThreadAllocThread() during Tcl_Finalize() or + * Tcl_FinalizeThread(). This function destroys the tsd key which + * stores allocator caches in thread local storage. */ TclFreeAllocCache(ptr); success = TlsSetValue(tlsKey, NULL); if (!success) { - Tcl_Panic("TlsSetValue failed from TclpFreeAllocCache!"); + Tcl_Panic("TlsSetValue failed from TclpFreeAllocCache"); } - } else if (once) { + } else { /* * Called by us in TclFinalizeThreadAlloc() during the library * finalization initiated from Tcl_Finalize() @@ -953,13 +1027,66 @@ TclpFreeAllocCache( 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 */ /* |
