summaryrefslogtreecommitdiffstats
path: root/win/tclWinThrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinThrd.c')
-rw-r--r--win/tclWinThrd.c262
1 files changed, 108 insertions, 154 deletions
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index e8d4d4d..2413a78 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -3,9 +3,8 @@
*
* This file implements the Windows-specific thread operations.
*
- * Copyright © 1998 Sun Microsystems, Inc.
- * Copyright © 1999 Scriptics Corporation
- * Copyright © 2008 George Peter Staplin
+ * 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.
@@ -13,6 +12,8 @@
#include "tclWinInt.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 */
@@ -22,15 +23,18 @@ _CRTIMP unsigned int __cdecl _controlfp (unsigned int unNew, unsigned int unMask
#endif
/*
- * This is the global lock used to serialize access to other serialization
+ * This is the master lock used to serialize access to other serialization
* data structures.
*/
-static CRITICAL_SECTION globalLock;
-static int initialized = 0;
+static CRITICAL_SECTION masterLock;
+static int init = 0;
+#define MASTER_LOCK TclpMasterLock()
+#define MASTER_UNLOCK TclpMasterUnlock()
+
/*
- * This is the global lock used to serialize initialization and finalization
+ * This is the master lock used to serialize initialization and finalization
* of Tcl as a whole.
*/
@@ -38,10 +42,10 @@ static CRITICAL_SECTION initLock;
/*
* allocLock is used by Tcl's version of malloc for synchronization. For
- * obvious reasons, cannot use any dynamically allocated storage.
+ * obvious reasons, cannot use any dyamically allocated storage.
*/
-#if TCL_THREADS
+#ifdef TCL_THREADS
static struct Tcl_Mutex_ {
CRITICAL_SECTION crit;
@@ -76,7 +80,7 @@ static CRITICAL_SECTION joinLock;
* The per-thread event and queue pointers.
*/
-#if TCL_THREADS
+#ifdef TCL_THREADS
typedef struct ThreadSpecificData {
HANDLE condEvent; /* Per-thread condition event */
@@ -105,7 +109,7 @@ static Tcl_ThreadDataKey dataKey;
* the queue.
*/
-typedef struct {
+typedef struct WinCondition {
CRITICAL_SECTION condLock; /* Lock to serialize queuing on the
* condition. */
struct ThreadSpecificData *firstPtr; /* Queue pointers */
@@ -117,9 +121,10 @@ typedef struct {
*/
#ifdef USE_THREAD_ALLOC
+static int once;
static DWORD tlsKey;
-typedef struct {
+typedef struct allocMutex {
Tcl_Mutex tlock;
CRITICAL_SECTION wlock;
} allocMutex;
@@ -130,7 +135,7 @@ typedef struct {
* to TclWinThreadStart.
*/
-typedef struct {
+typedef struct WinThread {
LPTHREAD_START_ROUTINE lpStartAddress; /* Original startup routine */
LPVOID lpParameter; /* Original startup data */
unsigned int fpControl; /* Floating point control word from the
@@ -162,6 +167,7 @@ TclWinThreadStart(
* from TclpThreadCreate */
{
WinThread *winThreadPtr = (WinThread *) lpParameter;
+ unsigned int fpmask;
LPTHREAD_START_ROUTINE lpOrigStartAddress;
LPVOID lpOrigParameter;
@@ -169,16 +175,18 @@ TclWinThreadStart(
return TCL_ERROR;
}
- _controlfp(winThreadPtr->fpControl, _MCW_EM | _MCW_RC | 0x03000000 /* _MCW_DN */
-#if !defined(_WIN64)
- | _MCW_PC
+ 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(winThreadPtr);
+ ckfree((char *)winThreadPtr);
return lpOrigStartAddress(lpOrigParameter);
}
@@ -202,13 +210,13 @@ TclWinThreadStart(
int
TclpThreadCreate(
Tcl_ThreadId *idPtr, /* Return, the ID of the thread. */
- Tcl_ThreadCreateProc *proc, /* Main() function of the thread. */
- void *clientData, /* The one argument to Main(). */
- TCL_HASH_TYPE stackSize, /* Size of stack for the new 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 */
+ WinThread *winThreadPtr; /* Per-thread startup info */
HANDLE tHandle;
winThreadPtr = (WinThread *)ckalloc(sizeof(WinThread));
@@ -219,14 +227,15 @@ TclpThreadCreate(
EnterCriticalSection(&joinLock);
*idPtr = 0; /* must initialize as Tcl_Thread is a pointer and
- * on WIN64 sizeof void* != sizeof unsigned */
+ * on WIN64 sizeof void* != sizeof unsigned
+ */
-#if defined(_MSC_VER) || defined(__MSVCRT__)
- tHandle = (HANDLE) _beginthreadex(NULL, (unsigned)stackSize,
+#if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__)
+ tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize,
(Tcl_ThreadCreateProc*) TclWinThreadStart, winThreadPtr,
0, (unsigned *)idPtr);
#else
- tHandle = CreateThread(NULL, (DWORD)stackSize,
+ tHandle = CreateThread(NULL, (DWORD) stackSize,
TclWinThreadStart, winThreadPtr, 0, (LPDWORD)idPtr);
#endif
@@ -240,7 +249,7 @@ TclpThreadCreate(
/*
* The only purpose of this is to decrement the reference count so the
- * OS resources will be reacquired when the thread closes.
+ * OS resources will be reaquired when the thread closes.
*/
CloseHandle(tHandle);
@@ -299,7 +308,7 @@ TclpThreadExit(
TclSignalExitThread(Tcl_GetCurrentThread(), status);
LeaveCriticalSection(&joinLock);
-#if defined(_MSC_VER) || defined(__MSVCRT__)
+#if defined(_MSC_VER) || defined(__MSVCRT__) || defined(__BORLANDC__)
_endthreadex((unsigned) status);
#else
ExitThread((DWORD) status);
@@ -325,7 +334,7 @@ TclpThreadExit(
Tcl_ThreadId
Tcl_GetCurrentThread(void)
{
- return (Tcl_ThreadId)INT2PTR(GetCurrentThreadId());
+ return (Tcl_ThreadId) INT2PTR(GetCurrentThreadId());
}
/*
@@ -350,7 +359,7 @@ Tcl_GetCurrentThread(void)
void
TclpInitLock(void)
{
- if (!initialized) {
+ if (!init) {
/*
* There is a fundamental race here that is solved by creating the
* first Tcl interpreter in a single threaded environment. Once the
@@ -358,10 +367,10 @@ TclpInitLock(void)
* that create interpreters in parallel.
*/
- initialized = 1;
+ init = 1;
InitializeCriticalSection(&joinLock);
InitializeCriticalSection(&initLock);
- InitializeCriticalSection(&globalLock);
+ InitializeCriticalSection(&masterLock);
}
EnterCriticalSection(&initLock);
}
@@ -392,27 +401,27 @@ TclpInitUnlock(void)
/*
*----------------------------------------------------------------------
*
- * TclpGlobalLock
+ * TclpMasterLock
*
* 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 synchronization objects.
+ * held during creation of syncronization objects.
*
* Results:
* None.
*
* Side effects:
- * Acquire the global mutex.
+ * Acquire the master mutex.
*
*----------------------------------------------------------------------
*/
void
-TclpGlobalLock(void)
+TclpMasterLock(void)
{
- if (!initialized) {
+ if (!init) {
/*
* There is a fundamental race here that is solved by creating the
* first Tcl interpreter in a single threaded environment. Once the
@@ -420,18 +429,18 @@ TclpGlobalLock(void)
* that create interpreters in parallel.
*/
- initialized = 1;
+ init = 1;
InitializeCriticalSection(&joinLock);
InitializeCriticalSection(&initLock);
- InitializeCriticalSection(&globalLock);
+ InitializeCriticalSection(&masterLock);
}
- EnterCriticalSection(&globalLock);
+ EnterCriticalSection(&masterLock);
}
/*
*----------------------------------------------------------------------
*
- * TclpGlobalUnlock
+ * TclpMasterUnlock
*
* This procedure is used to release a lock that serializes creation and
* deletion of synchronization objects.
@@ -440,15 +449,15 @@ TclpGlobalLock(void)
* None.
*
* Side effects:
- * Release the global mutex.
+ * Release the master mutex.
*
*----------------------------------------------------------------------
*/
void
-TclpGlobalUnlock(void)
+TclpMasterUnlock(void)
{
- LeaveCriticalSection(&globalLock);
+ LeaveCriticalSection(&masterLock);
}
/*
@@ -457,7 +466,7 @@ TclpGlobalUnlock(void)
* Tcl_GetAllocMutex
*
* This procedure returns a pointer to a statically initialized mutex for
- * use by the memory allocator. The allocator must use this lock, because
+ * use by the memory allocator. The alloctor must use this lock, because
* all other locks are allocated...
*
* Results:
@@ -473,7 +482,7 @@ TclpGlobalUnlock(void)
Tcl_Mutex *
Tcl_GetAllocMutex(void)
{
-#if TCL_THREADS
+#ifdef TCL_THREADS
if (!allocOnce) {
InitializeCriticalSection(&allocLock.crit);
allocOnce = 1;
@@ -487,7 +496,7 @@ Tcl_GetAllocMutex(void)
/*
*----------------------------------------------------------------------
*
- * TclFinalizeLock
+ * TclpFinalizeLock
*
* This procedure is used to destroy all private resources used in this
* file.
@@ -505,17 +514,17 @@ Tcl_GetAllocMutex(void)
void
TclFinalizeLock(void)
{
- TclpGlobalLock();
+ MASTER_LOCK;
DeleteCriticalSection(&joinLock);
/*
* Destroy the critical section that we are holding!
*/
- DeleteCriticalSection(&globalLock);
- initialized = 0;
+ DeleteCriticalSection(&masterLock);
+ init = 0;
-#if TCL_THREADS
+#ifdef TCL_THREADS
if (allocOnce) {
DeleteCriticalSection(&allocLock.crit);
allocOnce = 0;
@@ -531,10 +540,10 @@ TclFinalizeLock(void)
DeleteCriticalSection(&initLock);
}
-#if TCL_THREADS
+#ifdef TCL_THREADS
/* locally used prototype */
-static void FinalizeConditionEvent(void *data);
+static void FinalizeConditionEvent(ClientData data);
/*
*----------------------------------------------------------------------
@@ -548,7 +557,7 @@ static void FinalizeConditionEvent(void *data);
* None.
*
* Side effects:
- * May block the current thread. The mutex is acquired when this returns.
+ * May block the current thread. The mutex is aquired when this returns.
*
*----------------------------------------------------------------------
*/
@@ -560,19 +569,19 @@ Tcl_MutexLock(
CRITICAL_SECTION *csPtr;
if (*mutexPtr == NULL) {
- TclpGlobalLock();
+ MASTER_LOCK;
/*
- * Double inside global lock check to avoid a race.
+ * 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);
}
- TclpGlobalUnlock();
+ MASTER_UNLOCK;
}
csPtr = *((CRITICAL_SECTION **)mutexPtr);
EnterCriticalSection(csPtr);
@@ -628,7 +637,7 @@ TclpFinalizeMutex(
if (csPtr != NULL) {
DeleteCriticalSection(csPtr);
- ckfree(csPtr);
+ ckfree((char *) csPtr);
*mutexPtr = NULL;
}
}
@@ -648,7 +657,7 @@ TclpFinalizeMutex(
* None.
*
* Side effects:
- * May block the current thread. The mutex is acquired when this returns.
+ * 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.
*
@@ -659,7 +668,7 @@ void
Tcl_ConditionWait(
Tcl_Condition *condPtr, /* Really (WinCondition **) */
Tcl_Mutex *mutexPtr, /* Really (CRITICAL_SECTION **) */
- const Tcl_Time *timePtr) /* Timeout on waiting period */
+ Tcl_Time *timePtr) /* Timeout on waiting period */
{
WinCondition *winCondPtr; /* Per-condition queue head */
CRITICAL_SECTION *csPtr; /* Caller's Mutex, after casting */
@@ -674,57 +683,58 @@ Tcl_ConditionWait(
*/
if (tsdPtr->flags == WIN_THREAD_UNINIT) {
- TclpGlobalLock();
+ MASTER_LOCK;
/*
* Create the per-thread event and queue pointers.
*/
if (tsdPtr->flags == WIN_THREAD_UNINIT) {
- tsdPtr->condEvent = CreateEventW(NULL, TRUE /* manual reset */,
+ tsdPtr->condEvent = CreateEvent(NULL, TRUE /* manual reset */,
FALSE /* non signaled */, NULL);
tsdPtr->nextPtr = NULL;
tsdPtr->prevPtr = NULL;
tsdPtr->flags = WIN_THREAD_RUNNING;
doExit = 1;
}
- TclpGlobalUnlock();
+ MASTER_UNLOCK;
if (doExit) {
/*
* Create a per-thread exit handler to clean up the condEvent. We
- * must be careful to do this outside the Global Lock because
+ * 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 Global Lock.
+ * and initializing that may drop back into the Master Lock.
*/
- Tcl_CreateThreadExitHandler(FinalizeConditionEvent, tsdPtr);
+ Tcl_CreateThreadExitHandler(FinalizeConditionEvent,
+ (ClientData) tsdPtr);
}
}
if (*condPtr == NULL) {
- TclpGlobalLock();
+ MASTER_LOCK;
/*
* Initialize the per-condition queue pointers and Mutex.
*/
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;
TclRememberCondition(condPtr);
}
- TclpGlobalUnlock();
+ MASTER_UNLOCK;
}
csPtr = *((CRITICAL_SECTION **)mutexPtr);
winCondPtr = *((WinCondition **)condPtr);
if (timePtr == NULL) {
wtime = INFINITE;
} else {
- wtime = (DWORD)timePtr->sec * 1000 + (DWORD)timePtr->usec / 1000;
+ wtime = timePtr->sec * 1000 + timePtr->usec / 1000;
}
/*
@@ -759,8 +769,7 @@ Tcl_ConditionWait(
while (!timeout && (tsdPtr->flags & WIN_THREAD_BLOCKED)) {
ResetEvent(tsdPtr->condEvent);
LeaveCriticalSection(&winCondPtr->condLock);
- if (WaitForSingleObjectEx(tsdPtr->condEvent, wtime,
- TRUE) == WAIT_TIMEOUT) {
+ if (WaitForSingleObject(tsdPtr->condEvent, wtime) == WAIT_TIMEOUT) {
timeout = 1;
}
EnterCriticalSection(&winCondPtr->condLock);
@@ -776,9 +785,9 @@ Tcl_ConditionWait(
timeout = 0;
} else {
/*
- * When dequeueing, we can leave the tsdPtr->nextPtr and
+ * When dequeuing, we can leave the tsdPtr->nextPtr and
* tsdPtr->prevPtr with dangling pointers because they are
- * reinitialized w/out reading them when the thread is enqueued
+ * reinitialilzed w/out reading them when the thread is enqueued
* later.
*/
@@ -879,7 +888,7 @@ Tcl_ConditionNotify(
static void
FinalizeConditionEvent(
- void *data)
+ ClientData data)
{
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) data;
@@ -895,7 +904,7 @@ FinalizeConditionEvent(
* This procedure is invoked to clean up a condition variable. This is
* only safe to call at the end of time.
*
- * This assumes the Global Lock is held.
+ * This assumes the Master Lock is held.
*
* Results:
* None.
@@ -921,7 +930,7 @@ TclpFinalizeCondition(
if (winCondPtr != NULL) {
DeleteCriticalSection(&winCondPtr->condLock);
- ckfree(winCondPtr);
+ ckfree((char *) winCondPtr);
*condPtr = NULL;
}
}
@@ -937,9 +946,9 @@ TclpFinalizeCondition(
Tcl_Mutex *
TclpNewAllocMutex(void)
{
- allocMutex *lockPtr;
+ struct allocMutex *lockPtr;
- lockPtr = (allocMutex *)malloc(sizeof(allocMutex));
+ lockPtr = malloc(sizeof(struct allocMutex));
if (lockPtr == NULL) {
Tcl_Panic("could not allocate lock");
}
@@ -961,24 +970,24 @@ TclpFreeAllocMutex(
free(lockPtr);
}
-void
-TclpInitAllocCache(void)
+void *
+TclpGetAllocCache(void)
{
- /*
- * We need to make sure that TclpFreeAllocCache is called on each
- * thread that calls this, but only on threads that call this.
- */
+ 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.
+ */
- tlsKey = TlsAlloc();
- if (tlsKey == TLS_OUT_OF_INDEXES) {
- Tcl_Panic("could not allocate thread local storage");
+ tlsKey = TlsAlloc();
+ once = 1;
+ 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");
@@ -1005,10 +1014,8 @@ TclpFreeAllocCache(
if (ptr != NULL) {
/*
- * 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.
+ * Called by us in TclpFinalizeThreadData when a thread exits and
+ * destroys the tsd key which stores allocator caches.
*/
TclFreeAllocCache(ptr);
@@ -1016,7 +1023,7 @@ TclpFreeAllocCache(
if (!success) {
Tcl_Panic("TlsSetValue failed from TclpFreeAllocCache");
}
- } else {
+ } else if (once) {
/*
* Called by us in TclFinalizeThreadAlloc() during the library
* finalization initiated from Tcl_Finalize()
@@ -1026,64 +1033,11 @@ TclpFreeAllocCache(
if (!success) {
Tcl_Panic("TlsFree failed from TclpFreeAllocCache");
}
- }
-}
-#endif /* USE_THREAD_ALLOC */
-
-
-void *
-TclpThreadCreateKey(void)
-{
- DWORD *key;
-
- key = (DWORD *)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");
+ once = 0; /* reset for next time. */
}
- return key;
}
-
-void
-TclpThreadDeleteKey(
- void *keyPtr)
-{
- DWORD *key = (DWORD *)keyPtr;
-
- if (!TlsFree(*key)) {
- Tcl_Panic("unable to delete key");
- }
-
- TclpSysFree(keyPtr);
-}
-
-void
-TclpThreadSetGlobalTSD(
- void *tsdKeyPtr,
- void *ptr)
-{
- DWORD *key = (DWORD *)tsdKeyPtr;
-
- if (!TlsSetValue(*key, ptr)) {
- Tcl_Panic("unable to set global TSD value");
- }
-}
-
-void *
-TclpThreadGetGlobalTSD(
- void *tsdKeyPtr)
-{
- DWORD *key = (DWORD *)tsdKeyPtr;
-
- return TlsGetValue(*key);
-}
-
+#endif /* USE_THREAD_ALLOC */
#endif /* TCL_THREADS */
/*