diff options
Diffstat (limited to 'tcl/win/tclWinThrd.c')
-rw-r--r-- | tcl/win/tclWinThrd.c | 166 |
1 files changed, 148 insertions, 18 deletions
diff --git a/tcl/win/tclWinThrd.c b/tcl/win/tclWinThrd.c index 35fa53ffb48..edd6ff1e27d 100644 --- a/tcl/win/tclWinThrd.c +++ b/tcl/win/tclWinThrd.c @@ -44,6 +44,15 @@ static CRITICAL_SECTION allocLock; static Tcl_Mutex allocLockPtr = (Tcl_Mutex) &allocLock; /* + * 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. + */ + +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 @@ -93,8 +102,6 @@ typedef struct WinCondition { struct ThreadSpecificData *lastPtr; } WinCondition; -static void FinalizeConditionEvent(ClientData data); - /* *---------------------------------------------------------------------- @@ -124,19 +131,32 @@ Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) { HANDLE tHandle; -#ifdef __CYGWIN__ + EnterCriticalSection(&joinLock); + +#if defined(__MSVCRT__) || defined(__BORLANDC__) + tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize, proc, + clientData, 0, (unsigned *)idPtr); +#else tHandle = CreateThread(NULL, (DWORD) stackSize, - (LPTHREAD_START_ROUTINE) proc, (LPVOID) clientData, - (DWORD) 0, (LPDWORD)idPtr); + (LPTHREAD_START_ROUTINE) proc, (LPVOID) clientData, + (DWORD) 0, (LPDWORD)idPtr); +#endif + if (tHandle == NULL) { -#else - tHandle = (HANDLE) _beginthreadex(NULL, (unsigned)stackSize, proc, - clientData, (unsigned) 0, (unsigned *)idPtr); - if (tHandle == 0) { -#endif /* __CYGWIN__ */ + LeaveCriticalSection(&joinLock); return TCL_ERROR; } else { + if (flags & TCL_THREAD_JOINABLE) { + TclRememberJoinableThread (*idPtr); + } + + /* + * The only purpose of this is to decrement the reference count so the + * OS resources will be reaquired when the thread closes. + */ + CloseHandle(tHandle); + LeaveCriticalSection(&joinLock); return TCL_OK; } } @@ -144,6 +164,33 @@ Tcl_CreateThread(idPtr, proc, clientData, stackSize, flags) /* *---------------------------------------------------------------------- * + * Tcl_JoinThread -- + * + * This procedure waits upon the exit of the specified thread. + * + * Results: + * TCL_OK if the wait was successful, TCL_ERROR else. + * + * Side effects: + * The result area is set to the exit code of the thread we + * waited upon. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_JoinThread(id, result) + Tcl_ThreadId id; /* 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 (id, result); +} + +/* + *---------------------------------------------------------------------- + * * TclpThreadExit -- * * This procedure terminates the current thread. @@ -161,11 +208,15 @@ void TclpThreadExit(status) int status; { -#ifdef __CYGWIN__ - ExitThread((DWORD) status); -#else + EnterCriticalSection(&joinLock); + TclSignalExitThread (Tcl_GetCurrentThread (), status); + LeaveCriticalSection(&joinLock); + +#if defined(__MSVCRT__) || defined(__BORLANDC__) _endthreadex((unsigned) status); -#endif /* __CYGWIN__ */ +#else + ExitThread((DWORD) status); +#endif } @@ -222,6 +273,7 @@ TclpInitLock() * more threads that create interpreters in parallel. */ init = 1; + InitializeCriticalSection(&joinLock); InitializeCriticalSection(&initLock); InitializeCriticalSection(&masterLock); } @@ -284,6 +336,7 @@ TclpMasterLock() * more threads that create interpreters in parallel. */ init = 1; + InitializeCriticalSection(&joinLock); InitializeCriticalSection(&initLock); InitializeCriticalSection(&masterLock); } @@ -314,7 +367,12 @@ Tcl_Mutex * Tcl_GetAllocMutex() { #ifdef TCL_THREADS - InitializeCriticalSection(&allocLock); + static int once = 0; + + if (!once) { + InitializeCriticalSection(&allocLock); + once = 1; + } return &allocLockPtr; #else return NULL; @@ -323,6 +381,10 @@ Tcl_GetAllocMutex() #ifdef TCL_THREADS + +/* locally used prototype */ +static void FinalizeConditionEvent(ClientData data); + /* *---------------------------------------------------------------------- * @@ -439,6 +501,7 @@ TclpFinalizeMutex(mutexPtr) { CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr; if (csPtr != NULL) { + DeleteCriticalSection(csPtr); ckfree((char *)csPtr); *mutexPtr = NULL; } @@ -571,6 +634,9 @@ TclpFinalizeThreadData(keyPtr) VOID *result; DWORD *indexPtr; +#ifdef USE_THREAD_ALLOC + TclWinFreeAllocCache(); +#endif if (*keyPtr != NULL) { indexPtr = *(DWORD **)keyPtr; result = (VOID *)TlsGetValue(*indexPtr); @@ -620,7 +686,7 @@ TclpFinalizeThreadDataKey(keyPtr) * Tcl_ConditionWait -- * * This procedure is invoked to wait on a condition variable. - * The mutex is automically released as part of the wait, and + * 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. @@ -658,7 +724,7 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) } /* - * Self initialize the two parts of the contition. + * Self initialize the two parts of the condition. * The per-condition and per-thread parts need to be * handled independently. */ @@ -683,7 +749,7 @@ Tcl_ConditionWait(condPtr, mutexPtr, timePtr) if (doExit) { /* * Create a per-thread exit handler to clean up the condEvent. - * We must be careful do do this outside the Master Lock + * 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. @@ -907,8 +973,72 @@ TclpFinalizeCondition(condPtr) */ if (winCondPtr != NULL) { + DeleteCriticalSection(&winCondPtr->condLock); ckfree((char *)winCondPtr); *condPtr = NULL; } } + +/* + * Additions by AOL for specialized thread memory allocator. + */ +#ifdef USE_THREAD_ALLOC +static DWORD key; + +Tcl_Mutex * +TclpNewAllocMutex(void) +{ + struct lock { + Tcl_Mutex tlock; + CRITICAL_SECTION wlock; + } *lockPtr; + + lockPtr = malloc(sizeof(struct lock)); + if (lockPtr == NULL) { + panic("could not allocate lock"); + } + lockPtr->tlock = (Tcl_Mutex) &lockPtr->wlock; + InitializeCriticalSection(&lockPtr->wlock); + return &lockPtr->tlock; +} + +void * +TclpGetAllocCache(void) +{ + static int once = 0; + + if (!once) { + /* + * We need to make sure that TclWinFreeAllocCache is called + * on each thread that calls this, but only on threads that + * call this. + */ + key = TlsAlloc(); + once = 1; + if (key == TLS_OUT_OF_INDEXES) { + panic("could not allocate thread local storage"); + } + } + return TlsGetValue(key); +} + +void +TclpSetAllocCache(void *ptr) +{ + TlsSetValue(key, ptr); +} + +void +TclWinFreeAllocCache(void) +{ + void *ptr; + + ptr = TlsGetValue(key); + if (ptr != NULL) { + TlsSetValue(key, NULL); + TclFreeAllocCache(ptr); + } +} + +#endif /* USE_THREAD_ALLOC */ #endif /* TCL_THREADS */ |