summaryrefslogtreecommitdiff
path: root/tcl/win/tclWinThrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'tcl/win/tclWinThrd.c')
-rw-r--r--tcl/win/tclWinThrd.c166
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 */