Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a13bb5c7e5 | ||
|
|
77a0b2b716 | ||
|
|
83d4470ed9 | ||
|
|
fe5d5b9dec | ||
|
|
31855faf56 | ||
|
|
45b907287d | ||
|
|
71d42c7655 |
+2
-4
@@ -107,8 +107,6 @@ and join it.
|
||||
Restrictions: On some UNIX systems the pthread-library does not
|
||||
contain the functionality to specify the stack size of a thread. The
|
||||
specified value for the stack size is ignored on these systems.
|
||||
Windows currently does not support joinable threads. This
|
||||
flag value is therefore ignored on this platform.
|
||||
.PP
|
||||
Tcl provides the \fBTcl_ExitThread\fR and \fBTcl_FinalizeThread\fR functions
|
||||
for terminating threads and invoking optional per-thread exit
|
||||
@@ -158,9 +156,9 @@ A condition variable is used as a signaling mechanism:
|
||||
a thread can lock a mutex and then wait on a condition variable
|
||||
with \fBTcl_ConditionWait\fR. This atomically releases the mutex lock
|
||||
and blocks the waiting thread until another thread calls
|
||||
\fBTcl_ConditionNotify\fR. The caller of \fBTcl_ConditionNotify\fR should
|
||||
\fBTcl_ConditionNotify\fR. The caller of \fBTcl_ConditionNotify\fR may
|
||||
have the associated mutex held by previously calling \fBTcl_MutexLock\fR,
|
||||
but this is not enforced. Notifying the
|
||||
but this tends to produce more thread context switches. Notifying the
|
||||
condition variable unblocks all threads waiting on the condition variable,
|
||||
but they do not proceed until the mutex is released with \fBTcl_MutexUnlock\fR.
|
||||
The implementation of \fBTcl_ConditionWait\fR automatically locks
|
||||
|
||||
@@ -3235,7 +3235,6 @@ MODULE_SCOPE Tcl_Obj * TclGetHomeDirObj(Tcl_Interp *interp, const char *user);
|
||||
MODULE_SCOPE Tcl_Obj * TclResolveTildePath(Tcl_Interp *interp,
|
||||
Tcl_Obj *pathObj);
|
||||
MODULE_SCOPE Tcl_Obj * TclResolveTildePathList(Tcl_Obj *pathsObj);
|
||||
MODULE_SCOPE int TclJoinThread(Tcl_ThreadId id, int *result);
|
||||
MODULE_SCOPE void TclLimitRemoveAllHandlers(Tcl_Interp *interp);
|
||||
MODULE_SCOPE Tcl_Obj * TclLindexList(Tcl_Interp *interp,
|
||||
Tcl_Obj *listPtr, Tcl_Obj *argPtr);
|
||||
@@ -3363,7 +3362,6 @@ MODULE_SCOPE void TclThreadStorageKeySet(Tcl_ThreadDataKey *keyPtr,
|
||||
void *data);
|
||||
MODULE_SCOPE TCL_NORETURN void TclpThreadExit(int status);
|
||||
MODULE_SCOPE void TclRememberCondition(Tcl_Condition *mutex);
|
||||
MODULE_SCOPE void TclRememberJoinableThread(Tcl_ThreadId id);
|
||||
MODULE_SCOPE void TclRememberMutex(Tcl_Mutex *mutex);
|
||||
MODULE_SCOPE void TclRemoveScriptLimitCallbacks(Tcl_Interp *interp);
|
||||
MODULE_SCOPE int TclReToGlob(Tcl_Interp *interp, const char *reStr,
|
||||
@@ -3382,7 +3380,6 @@ MODULE_SCOPE void TclSetCmdNameObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
|
||||
MODULE_SCOPE void TclSetDuplicateObj(Tcl_Obj *dupPtr, Tcl_Obj *objPtr);
|
||||
MODULE_SCOPE void TclSetProcessGlobalValue(ProcessGlobalValue *pgvPtr,
|
||||
Tcl_Obj *newValue, Tcl_Encoding encoding);
|
||||
MODULE_SCOPE void TclSignalExitThread(Tcl_ThreadId id, int result);
|
||||
MODULE_SCOPE void TclSpellFix(Tcl_Interp *interp,
|
||||
Tcl_Obj *const *objv, Tcl_Size objc, Tcl_Size subIdx,
|
||||
Tcl_Obj *bad, Tcl_Obj *fix);
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* tclThreadJoin.c --
|
||||
*
|
||||
* This file implements a platform independent emulation layer for the
|
||||
* handling of joinable threads. The Windows platform uses this code to
|
||||
* provide the functionality of joining threads. This code is currently
|
||||
* not necessary on Unix.
|
||||
*
|
||||
* Copyright © 2000 Scriptics Corporation
|
||||
*
|
||||
* See the file "license.terms" for information on usage and redistribution of
|
||||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
*/
|
||||
|
||||
#include "tclInt.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/*
|
||||
* The information about each joinable thread is remembered in a structure as
|
||||
* defined below.
|
||||
*/
|
||||
|
||||
typedef struct JoinableThread {
|
||||
Tcl_ThreadId id; /* The id of the joinable thread. */
|
||||
int result; /* A place for the result after the demise of
|
||||
* the thread. */
|
||||
int done; /* Boolean flag. Initialized to 0 and set to 1
|
||||
* after the exit of the thread. This allows a
|
||||
* thread requesting a join to detect when
|
||||
* waiting is not necessary. */
|
||||
int waitedUpon; /* Boolean flag. Initialized to 0 and set to 1
|
||||
* by the thread waiting for this one via
|
||||
* Tcl_JoinThread. Used to lock any other
|
||||
* thread trying to wait on this one. */
|
||||
Tcl_Mutex threadMutex; /* The mutex used to serialize access to this
|
||||
* structure. */
|
||||
Tcl_Condition cond; /* This is the condition a thread has to wait
|
||||
* upon to get notified of the end of the
|
||||
* described thread. It is signaled indirectly
|
||||
* by Tcl_ExitThread. */
|
||||
struct JoinableThread *nextThreadPtr;
|
||||
/* Reference to the next thread in the list of
|
||||
* joinable threads. */
|
||||
} JoinableThread;
|
||||
|
||||
/*
|
||||
* The following variable is used to maintain the global list of all joinable
|
||||
* threads. Usage by a thread is allowed only if the thread acquired the
|
||||
* 'joinMutex'.
|
||||
*/
|
||||
|
||||
TCL_DECLARE_MUTEX(joinMutex)
|
||||
|
||||
static JoinableThread *firstThreadPtr;
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclJoinThread --
|
||||
*
|
||||
* This procedure waits for the exit of the thread with the specified id
|
||||
* and returns its result.
|
||||
*
|
||||
* Results:
|
||||
* A standard tcl result signaling the overall success/failure of the
|
||||
* operation and an integer result delivered by the thread which was
|
||||
* waited upon.
|
||||
*
|
||||
* Side effects:
|
||||
* Deallocates the memory allocated by TclRememberJoinableThread.
|
||||
* Removes the data associated to the thread waited upon from the list of
|
||||
* joinable threads.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
TclJoinThread(
|
||||
Tcl_ThreadId id, /* The id of the thread to wait upon. */
|
||||
int *result) /* Reference to a location for the result of
|
||||
* the thread we are waiting upon. */
|
||||
{
|
||||
JoinableThread *threadPtr;
|
||||
|
||||
/*
|
||||
* Steps done here:
|
||||
* i. Acquire the joinMutex and search for the thread.
|
||||
* ii. Error out if it could not be found.
|
||||
* iii. If found, switch from exclusive access to the list to exclusive
|
||||
* access to the thread structure.
|
||||
* iv. Error out if some other is already waiting.
|
||||
* v. Skip the waiting part of the thread is already done.
|
||||
* vi. Wait for the thread to exit, mark it as waited upon too.
|
||||
* vii. Get the result form the structure,
|
||||
* viii. switch to exclusive access of the list,
|
||||
* ix. remove the structure from the list,
|
||||
* x. then switch back to exclusive access to the structure
|
||||
* xi. and delete it.
|
||||
*/
|
||||
|
||||
Tcl_MutexLock(&joinMutex);
|
||||
|
||||
threadPtr = firstThreadPtr;
|
||||
while (threadPtr!=NULL && threadPtr->id!=id) {
|
||||
threadPtr = threadPtr->nextThreadPtr;
|
||||
}
|
||||
|
||||
if (threadPtr == NULL) {
|
||||
/*
|
||||
* Thread not found. Either not joinable, or already waited upon and
|
||||
* exited. Whatever, an error is in order.
|
||||
*/
|
||||
|
||||
Tcl_MutexUnlock(&joinMutex);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* [1] If we don't lock the structure before giving up exclusive access to
|
||||
* the list some other thread just completing its wait on the same thread
|
||||
* can delete the structure from under us, leaving us with a dangling
|
||||
* pointer.
|
||||
*/
|
||||
|
||||
Tcl_MutexLock(&threadPtr->threadMutex);
|
||||
Tcl_MutexUnlock(&joinMutex);
|
||||
|
||||
/*
|
||||
* [2] Now that we have the structure mutex any other thread that just
|
||||
* tries to delete structure will wait at location [3] until we are done
|
||||
* with the structure. And in that case we are done with it rather quickly
|
||||
* as 'waitedUpon' will be set and we will have to error out.
|
||||
*/
|
||||
|
||||
if (threadPtr->waitedUpon) {
|
||||
Tcl_MutexUnlock(&threadPtr->threadMutex);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are waiting now, let other threads recognize this.
|
||||
*/
|
||||
|
||||
threadPtr->waitedUpon = 1;
|
||||
|
||||
while (!threadPtr->done) {
|
||||
Tcl_ConditionWait(&threadPtr->cond, &threadPtr->threadMutex, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to release the structure before trying to access the list again
|
||||
* or we can run into deadlock with a thread at [1] (see above) because of
|
||||
* us holding the structure and the other holding the list. There is no
|
||||
* problem with dangling pointers here as 'waitedUpon == 1' is still valid
|
||||
* and any other thread will error out and not come to this place. IOW,
|
||||
* the fact that we are here also means that no other thread came here
|
||||
* before us and is able to delete the structure.
|
||||
*/
|
||||
|
||||
Tcl_MutexUnlock(&threadPtr->threadMutex);
|
||||
Tcl_MutexLock(&joinMutex);
|
||||
|
||||
/*
|
||||
* We have to search the list again as its structure may (may, almost
|
||||
* certainly) have changed while we were waiting. Especially now is the
|
||||
* time to compute the predecessor in the list. Any earlier result can be
|
||||
* dangling by now.
|
||||
*/
|
||||
|
||||
if (firstThreadPtr == threadPtr) {
|
||||
firstThreadPtr = threadPtr->nextThreadPtr;
|
||||
} else {
|
||||
JoinableThread *prevThreadPtr = firstThreadPtr;
|
||||
|
||||
while (prevThreadPtr->nextThreadPtr != threadPtr) {
|
||||
prevThreadPtr = prevThreadPtr->nextThreadPtr;
|
||||
}
|
||||
prevThreadPtr->nextThreadPtr = threadPtr->nextThreadPtr;
|
||||
}
|
||||
|
||||
Tcl_MutexUnlock(&joinMutex);
|
||||
|
||||
/*
|
||||
* [3] Now that the structure is not part of the list anymore no other
|
||||
* thread can acquire its mutex from now on. But it is possible that
|
||||
* another thread is still holding the mutex though, see location [2]. So
|
||||
* we have to acquire the mutex one more time to wait for that thread to
|
||||
* finish. We can (and have to) release the mutex immediately.
|
||||
*/
|
||||
|
||||
Tcl_MutexLock(&threadPtr->threadMutex);
|
||||
Tcl_MutexUnlock(&threadPtr->threadMutex);
|
||||
|
||||
/*
|
||||
* Copy the result to us, finalize the synchronisation objects, then free
|
||||
* the structure and return.
|
||||
*/
|
||||
|
||||
*result = threadPtr->result;
|
||||
|
||||
Tcl_ConditionFinalize(&threadPtr->cond);
|
||||
Tcl_MutexFinalize(&threadPtr->threadMutex);
|
||||
Tcl_Free(threadPtr);
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclRememberJoinableThread --
|
||||
*
|
||||
* This procedure remembers a thread as joinable. Only a call to
|
||||
* TclJoinThread will remove the structure created (and initialized) here.
|
||||
* IOW, not waiting upon a joinable thread will cause memory leaks.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* Allocates memory, adds it to the global list of all joinable threads.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
TclRememberJoinableThread(
|
||||
Tcl_ThreadId id) /* The thread to remember as joinable */
|
||||
{
|
||||
JoinableThread *threadPtr;
|
||||
|
||||
threadPtr = (JoinableThread *)Tcl_Alloc(sizeof(JoinableThread));
|
||||
threadPtr->id = id;
|
||||
threadPtr->done = 0;
|
||||
threadPtr->waitedUpon = 0;
|
||||
threadPtr->threadMutex = (Tcl_Mutex) NULL;
|
||||
threadPtr->cond = (Tcl_Condition) NULL;
|
||||
|
||||
Tcl_MutexLock(&joinMutex);
|
||||
|
||||
threadPtr->nextThreadPtr = firstThreadPtr;
|
||||
firstThreadPtr = threadPtr;
|
||||
|
||||
Tcl_MutexUnlock(&joinMutex);
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclSignalExitThread --
|
||||
*
|
||||
* This procedure signals that the specified thread is done with its
|
||||
* work. If the thread is joinable this signal is propagated to the
|
||||
* thread waiting upon it.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* Modifies the associated structure to hold the result.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
TclSignalExitThread(
|
||||
Tcl_ThreadId id, /* Id of the thread signaling its exit. */
|
||||
int result) /* The result from the thread. */
|
||||
{
|
||||
JoinableThread *threadPtr;
|
||||
|
||||
Tcl_MutexLock(&joinMutex);
|
||||
|
||||
threadPtr = firstThreadPtr;
|
||||
while ((threadPtr != NULL) && (threadPtr->id != id)) {
|
||||
threadPtr = threadPtr->nextThreadPtr;
|
||||
}
|
||||
|
||||
if (threadPtr == NULL) {
|
||||
/*
|
||||
* Thread not found. Not joinable. No problem, nothing to do.
|
||||
*/
|
||||
|
||||
Tcl_MutexUnlock(&joinMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch over the exclusive access from the list to the structure, then
|
||||
* store the result, set the flag and notify the waiting thread, provided
|
||||
* that it exists. The order of lock/unlock ensures that a thread entering
|
||||
* 'TclJoinThread' will not interfere with us.
|
||||
*/
|
||||
|
||||
Tcl_MutexLock(&threadPtr->threadMutex);
|
||||
Tcl_MutexUnlock(&joinMutex);
|
||||
|
||||
threadPtr->done = 1;
|
||||
threadPtr->result = result;
|
||||
|
||||
if (threadPtr->waitedUpon) {
|
||||
Tcl_ConditionNotify(&threadPtr->cond);
|
||||
}
|
||||
|
||||
Tcl_MutexUnlock(&threadPtr->threadMutex);
|
||||
}
|
||||
#else
|
||||
TCL_MAC_EMPTY_FILE(generic_tclThreadJoin_c)
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* c-basic-offset: 4
|
||||
* fill-column: 78
|
||||
* End:
|
||||
*/
|
||||
@@ -86,7 +86,6 @@
|
||||
F96D45C808F272BC004A47F5 /* tclTestProcBodyObj.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2908F272A7004A47F5 /* tclTestProcBodyObj.c */; };
|
||||
F96D45C908F272BC004A47F5 /* tclThread.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2A08F272A7004A47F5 /* tclThread.c */; };
|
||||
F96D45CA08F272BC004A47F5 /* tclThreadAlloc.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2B08F272A7004A47F5 /* tclThreadAlloc.c */; };
|
||||
F96D45CB08F272BC004A47F5 /* tclThreadJoin.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2C08F272A7004A47F5 /* tclThreadJoin.c */; };
|
||||
F96D45CC08F272BC004A47F5 /* tclThreadStorage.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2D08F272A7004A47F5 /* tclThreadStorage.c */; };
|
||||
F96D45CD08F272BC004A47F5 /* tclThreadTest.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2E08F272A7004A47F5 /* tclThreadTest.c */; };
|
||||
F96D45CE08F272BC004A47F5 /* tclTimer.c in Sources */ = {isa = PBXBuildFile; fileRef = F96D3F2F08F272A7004A47F5 /* tclTimer.c */; };
|
||||
@@ -522,7 +521,6 @@
|
||||
F96D3F2908F272A7004A47F5 /* tclTestProcBodyObj.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclTestProcBodyObj.c; sourceTree = "<group>"; };
|
||||
F96D3F2A08F272A7004A47F5 /* tclThread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclThread.c; sourceTree = "<group>"; };
|
||||
F96D3F2B08F272A7004A47F5 /* tclThreadAlloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclThreadAlloc.c; sourceTree = "<group>"; };
|
||||
F96D3F2C08F272A7004A47F5 /* tclThreadJoin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclThreadJoin.c; sourceTree = "<group>"; };
|
||||
F96D3F2D08F272A7004A47F5 /* tclThreadStorage.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclThreadStorage.c; sourceTree = "<group>"; };
|
||||
F96D3F2E08F272A7004A47F5 /* tclThreadTest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclThreadTest.c; sourceTree = "<group>"; };
|
||||
F96D3F2F08F272A7004A47F5 /* tclTimer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tclTimer.c; sourceTree = "<group>"; };
|
||||
@@ -1297,7 +1295,6 @@
|
||||
F96D3F2908F272A7004A47F5 /* tclTestProcBodyObj.c */,
|
||||
F96D3F2A08F272A7004A47F5 /* tclThread.c */,
|
||||
F96D3F2B08F272A7004A47F5 /* tclThreadAlloc.c */,
|
||||
F96D3F2C08F272A7004A47F5 /* tclThreadJoin.c */,
|
||||
F96D3F2D08F272A7004A47F5 /* tclThreadStorage.c */,
|
||||
F96D3F2E08F272A7004A47F5 /* tclThreadTest.c */,
|
||||
F96D3F2F08F272A7004A47F5 /* tclTimer.c */,
|
||||
@@ -2000,7 +1997,6 @@
|
||||
F96D45C808F272BC004A47F5 /* tclTestProcBodyObj.c in Sources */,
|
||||
F96D45C908F272BC004A47F5 /* tclThread.c in Sources */,
|
||||
F96D45CA08F272BC004A47F5 /* tclThreadAlloc.c in Sources */,
|
||||
F96D45CB08F272BC004A47F5 /* tclThreadJoin.c in Sources */,
|
||||
F96D45CC08F272BC004A47F5 /* tclThreadStorage.c in Sources */,
|
||||
F96D45CD08F272BC004A47F5 /* tclThreadTest.c in Sources */,
|
||||
F96D45CE08F272BC004A47F5 /* tclTimer.c in Sources */,
|
||||
|
||||
+1
-5
@@ -314,7 +314,7 @@ GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \
|
||||
tclPreserve.o tclProc.o tclProcess.o tclRegexp.o \
|
||||
tclResolve.o tclResult.o tclScan.o tclStringObj.o \
|
||||
tclStrToD.o tclThread.o \
|
||||
tclThreadAlloc.o tclThreadJoin.o tclThreadStorage.o tclStubInit.o \
|
||||
tclThreadAlloc.o tclThreadStorage.o tclStubInit.o \
|
||||
tclTimer.o tclTrace.o tclUtf.o tclUtil.o tclVar.o tclZlib.o \
|
||||
tclTomMathInterface.o tclZipfs.o
|
||||
|
||||
@@ -472,7 +472,6 @@ GENERIC_SRCS = \
|
||||
$(GENERIC_DIR)/tclTestProcBodyObj.c \
|
||||
$(GENERIC_DIR)/tclThread.c \
|
||||
$(GENERIC_DIR)/tclThreadAlloc.c \
|
||||
$(GENERIC_DIR)/tclThreadJoin.c \
|
||||
$(GENERIC_DIR)/tclThreadStorage.c \
|
||||
$(GENERIC_DIR)/tclTimer.c \
|
||||
$(GENERIC_DIR)/tclTrace.c \
|
||||
@@ -1574,9 +1573,6 @@ tclThread.o: $(GENERIC_DIR)/tclThread.c
|
||||
tclThreadAlloc.o: $(GENERIC_DIR)/tclThreadAlloc.c
|
||||
$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclThreadAlloc.c
|
||||
|
||||
tclThreadJoin.o: $(GENERIC_DIR)/tclThreadJoin.c
|
||||
$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclThreadJoin.c
|
||||
|
||||
tclThreadStorage.o: $(GENERIC_DIR)/tclThreadStorage.c
|
||||
$(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclThreadStorage.c
|
||||
|
||||
|
||||
@@ -354,7 +354,6 @@ GENERIC_OBJS = \
|
||||
tclStubInit.$(OBJEXT) \
|
||||
tclThread.$(OBJEXT) \
|
||||
tclThreadAlloc.$(OBJEXT) \
|
||||
tclThreadJoin.$(OBJEXT) \
|
||||
tclThreadStorage.$(OBJEXT) \
|
||||
tclTimer.$(OBJEXT) \
|
||||
tclTomMathInterface.$(OBJEXT) \
|
||||
|
||||
@@ -310,7 +310,6 @@ COREOBJS = \
|
||||
$(TMP_DIR)\tclStubInit.obj \
|
||||
$(TMP_DIR)\tclThread.obj \
|
||||
$(TMP_DIR)\tclThreadAlloc.obj \
|
||||
$(TMP_DIR)\tclThreadJoin.obj \
|
||||
$(TMP_DIR)\tclThreadStorage.obj \
|
||||
$(TMP_DIR)\tclTimer.obj \
|
||||
$(TMP_DIR)\tclTomMathInterface.obj \
|
||||
|
||||
@@ -1320,10 +1320,6 @@ SOURCE=..\generic\tclThread.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\generic\tclThreadJoin.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\generic\tclThreadTest.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
+244
-246
@@ -21,109 +21,51 @@
|
||||
_CRTIMP unsigned int __cdecl _controlfp (unsigned int unNew, unsigned int unMask);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the global lock used to serialize access to other serialization
|
||||
* data structures.
|
||||
*/
|
||||
static struct tclWinGlobals {
|
||||
INIT_ONCE init_once; /* must be first member,
|
||||
because of static initialziation */
|
||||
|
||||
static CRITICAL_SECTION globalLock;
|
||||
static int initialized = 0;
|
||||
/*
|
||||
* This is the global lock used to serialize access to other serialization
|
||||
* data structures.
|
||||
*/
|
||||
CRITICAL_SECTION globalLock;
|
||||
|
||||
/*
|
||||
* This is the global lock used to serialize initialization and finalization
|
||||
* of Tcl as a whole.
|
||||
*/
|
||||
/*
|
||||
* This is the global lock used to serialize initialization and finalization
|
||||
* of Tcl as a whole.
|
||||
*/
|
||||
|
||||
static CRITICAL_SECTION initLock;
|
||||
CRITICAL_SECTION initLock;
|
||||
|
||||
/*
|
||||
* allocLock is used by Tcl's version of malloc for synchronization. For
|
||||
* obvious reasons, cannot use any dyamically allocated storage.
|
||||
*/
|
||||
/* holds the TLS key for the Tcl Thread_ID */
|
||||
DWORD tls_thread_id;
|
||||
|
||||
/*
|
||||
* allocLock is used by Tcl's version of malloc for synchronization. For
|
||||
* obvious reasons, cannot use any dyamically allocated storage.
|
||||
*/
|
||||
|
||||
#if TCL_THREADS
|
||||
|
||||
static struct Tcl_Mutex_ {
|
||||
CRITICAL_SECTION crit;
|
||||
} allocLock;
|
||||
static Tcl_Mutex allocLockPtr = &allocLock;
|
||||
static int allocOnce = 0;
|
||||
struct Tcl_Mutex_ {
|
||||
CRITICAL_SECTION crit;
|
||||
} allocLock;
|
||||
Tcl_Mutex allocLockPtr;
|
||||
|
||||
#endif /* TCL_THREADS */
|
||||
|
||||
/*
|
||||
* 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 on when it is doing a ConditionWait;
|
||||
* it uses the same event for all condition variables because it only waits on
|
||||
* one at a time. Each condition variable has a queue of waiting threads, and
|
||||
* a mutex used to serialize access to this queue.
|
||||
*
|
||||
* Special thanks to David Nichols and Jim Davidson for advice on the
|
||||
* Condition Variable implementation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The per-thread event and queue pointers.
|
||||
*/
|
||||
} TclWinGlobals = {
|
||||
INIT_ONCE_STATIC_INIT
|
||||
};
|
||||
|
||||
#if TCL_THREADS
|
||||
|
||||
typedef struct ThreadSpecificData {
|
||||
HANDLE condEvent; /* Per-thread condition event */
|
||||
struct ThreadSpecificData *nextPtr; /* Queue pointers */
|
||||
struct ThreadSpecificData *prevPtr;
|
||||
int flags; /* See flags below */
|
||||
} ThreadSpecificData;
|
||||
static Tcl_ThreadDataKey dataKey;
|
||||
|
||||
#endif /* TCL_THREADS */
|
||||
|
||||
/*
|
||||
* State bits for the thread.
|
||||
* WIN_THREAD_UNINIT Uninitialized. Must be zero because of the way
|
||||
* ThreadSpecificData is created.
|
||||
* WIN_THREAD_RUNNING Running, not waiting.
|
||||
* WIN_THREAD_BLOCKED Waiting, or trying to wait.
|
||||
*/
|
||||
|
||||
#define WIN_THREAD_UNINIT 0x0
|
||||
#define WIN_THREAD_RUNNING 0x1
|
||||
#define WIN_THREAD_BLOCKED 0x2
|
||||
|
||||
/*
|
||||
* The per condition queue pointers and the Mutex used to serialize access to
|
||||
* the queue.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
CRITICAL_SECTION condLock; /* Lock to serialize queuing on the
|
||||
* condition. */
|
||||
struct ThreadSpecificData *firstPtr; /* Queue pointers */
|
||||
struct ThreadSpecificData *lastPtr;
|
||||
typedef struct Tcl_Condition_ {
|
||||
CONDITION_VARIABLE cv;
|
||||
} WinCondition;
|
||||
|
||||
/*
|
||||
* Additions by AOL for specialized thread memory allocator.
|
||||
*/
|
||||
#endif /* TCL_THREADS */
|
||||
|
||||
#ifdef USE_THREAD_ALLOC
|
||||
static DWORD tlsKey;
|
||||
|
||||
typedef struct {
|
||||
Tcl_Mutex tlock;
|
||||
CRITICAL_SECTION wlock;
|
||||
} allocMutex;
|
||||
#endif /* USE_THREAD_ALLOC */
|
||||
|
||||
/*
|
||||
* The per thread data passed from TclpThreadCreate
|
||||
@@ -135,8 +77,50 @@ typedef struct {
|
||||
LPVOID lpParameter; /* Original startup data */
|
||||
unsigned int fpControl; /* Floating point control word from the
|
||||
* main thread */
|
||||
HANDLE tHandle; /* used to wait on thread completion */
|
||||
# if defined(_MSC_VER) || defined(__MSVCRT__)
|
||||
unsigned
|
||||
# else
|
||||
DWORD
|
||||
# endif
|
||||
thread_id; /* initialized but unused,
|
||||
possibly useful for debugging */
|
||||
} WinThread;
|
||||
|
||||
/* assuming alignment constraints result in lsb being constant 0 */
|
||||
# define TCL_WIN_THREAD_JOIN_FLAG ((size_t) 1 << 0)
|
||||
|
||||
# define TCL_WIN_THREAD_ID(PTR, JOINABLE)\
|
||||
((Tcl_ThreadId) ((sizeof ((PTR) - (WinThread *) 0),\
|
||||
(size_t) (PTR)) | ((JOINABLE)? TCL_WIN_THREAD_JOIN_FLAG: 0)))
|
||||
|
||||
# define TCL_WIN_THREAD_PTR(ID)\
|
||||
((WinThread *) ((sizeof ((ID) == (Tcl_ThreadId) 0),\
|
||||
(size_t) (ID) & ~TCL_WIN_THREAD_JOIN_FLAG)))
|
||||
|
||||
# define TCL_WIN_THREAD_JOINABLE(ID)\
|
||||
(((sizeof ((ID) == (Tcl_ThreadId) 0),\
|
||||
(size_t) (ID) & TCL_WIN_THREAD_JOIN_FLAG)))
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclWinFailure --
|
||||
*
|
||||
* Call this when an unexpected WIN32 error occurs
|
||||
*
|
||||
* Does not return
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void TCL_NORETURN WINAPI
|
||||
TclWinFailure (
|
||||
char const *const routine)
|
||||
{
|
||||
Tcl_Panic ("%s failed, last error = %#x\n", routine, GetLastError ());
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
@@ -161,9 +145,8 @@ TclWinThreadStart(
|
||||
LPVOID lpParameter) /* The WinThread structure pointer passed
|
||||
* from TclpThreadCreate */
|
||||
{
|
||||
WinThread *winThreadPtr = (WinThread *) lpParameter;
|
||||
LPTHREAD_START_ROUTINE lpOrigStartAddress;
|
||||
LPVOID lpOrigParameter;
|
||||
WinThread *const winThreadPtr = (WinThread *) lpParameter;
|
||||
static DWORD result;
|
||||
|
||||
if (!winThreadPtr) {
|
||||
return TCL_ERROR;
|
||||
@@ -175,11 +158,12 @@ TclWinThreadStart(
|
||||
#endif
|
||||
);
|
||||
|
||||
lpOrigStartAddress = winThreadPtr->lpStartAddress;
|
||||
lpOrigParameter = winThreadPtr->lpParameter;
|
||||
|
||||
Tcl_Free(winThreadPtr);
|
||||
return lpOrigStartAddress(lpOrigParameter);
|
||||
if (!TlsSetValue (TclWinGlobals.tls_thread_id, winThreadPtr))
|
||||
TclWinFailure ("TlsSetValue");
|
||||
result = winThreadPtr->lpStartAddress (winThreadPtr->lpParameter);
|
||||
if (!winThreadPtr->tHandle)
|
||||
TclpSysFree (winThreadPtr);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -211,41 +195,50 @@ TclpThreadCreate(
|
||||
WinThread *winThreadPtr; /* Per-thread startup info */
|
||||
HANDLE tHandle;
|
||||
|
||||
winThreadPtr = (WinThread *)Tcl_Alloc(sizeof(WinThread));
|
||||
winThreadPtr = (WinThread *) TclpSysAlloc (sizeof *winThreadPtr);
|
||||
if (!winThreadPtr)
|
||||
TclWinFailure ("memory allocation");
|
||||
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__)
|
||||
tHandle = (HANDLE) _beginthreadex(NULL, (unsigned) stackSize,
|
||||
(Tcl_ThreadCreateProc*) TclWinThreadStart, winThreadPtr,
|
||||
0, (unsigned *)idPtr);
|
||||
0 /* flags */, &winThreadPtr->thread_id);
|
||||
#else
|
||||
tHandle = CreateThread(NULL, (DWORD) stackSize,
|
||||
TclWinThreadStart, winThreadPtr, 0, (LPDWORD)idPtr);
|
||||
TclWinThreadStart, winThreadPtr, 0 /* flags */,
|
||||
&winThreadPtr->thread_id);
|
||||
#endif
|
||||
|
||||
if (tHandle == NULL) {
|
||||
LeaveCriticalSection(&joinLock);
|
||||
if (!tHandle) {
|
||||
TclpSysFree (winThreadPtr);
|
||||
# if 1
|
||||
TclWinFailure ("CreateThread");
|
||||
# else
|
||||
return TCL_ERROR;
|
||||
# endif
|
||||
} else {
|
||||
if (flags & TCL_THREAD_JOINABLE) {
|
||||
TclRememberJoinableThread(*idPtr);
|
||||
if (!(flags & TCL_THREAD_JOINABLE)) {
|
||||
/*
|
||||
* The only purpose of this is to decrement the reference count
|
||||
* so the OS resources will be reacquired when the thread closes.
|
||||
*/
|
||||
if (!CloseHandle (tHandle))
|
||||
TclWinFailure ("ClosseHandle");
|
||||
/* note: the WIN32 OpenHandle() can be used to open a HANDLE
|
||||
from the given threadID. However, if the thread
|
||||
terminates with a HANDLE count of zero, the kernel may clean up
|
||||
the thread, in particular its return status, immediately.
|
||||
Hence, joining the thread requires keeping a HANDLE open
|
||||
until the thread has been successfully joined. */
|
||||
|
||||
tHandle = 0;
|
||||
}
|
||||
winThreadPtr->tHandle = tHandle;
|
||||
|
||||
/*
|
||||
* The only purpose of this is to decrement the reference count so the
|
||||
* OS resources will be reacquired when the thread closes.
|
||||
*/
|
||||
|
||||
CloseHandle(tHandle);
|
||||
LeaveCriticalSection(&joinLock);
|
||||
*idPtr = TCL_WIN_THREAD_ID (winThreadPtr, 0 != tHandle);
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
@@ -273,7 +266,26 @@ Tcl_JoinThread(
|
||||
int *result) /* Reference to the storage the result of the
|
||||
* thread we wait upon will be written into. */
|
||||
{
|
||||
return TclJoinThread(threadId, result);
|
||||
if (!TCL_WIN_THREAD_JOINABLE (threadId))
|
||||
return TCL_ERROR;
|
||||
|
||||
WinThread *winThreadPtr = TCL_WIN_THREAD_PTR (threadId);
|
||||
HANDLE const tHandle = winThreadPtr->tHandle;
|
||||
switch (WaitForSingleObject (tHandle, INFINITE)) {
|
||||
DWORD exit_code;
|
||||
default:
|
||||
return TCL_ERROR;
|
||||
case WAIT_FAILED:
|
||||
TclWinFailure ("WaitForSingleObject");
|
||||
case WAIT_OBJECT_0:
|
||||
if (!GetExitCodeThread (tHandle, &exit_code))
|
||||
TclWinFailure ("GetExitCodeThread");
|
||||
*result = exit_code;
|
||||
if (!CloseHandle (tHandle))
|
||||
TclWinFailure ("CloseHandle");
|
||||
TclpSysFree (winThreadPtr);
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -296,10 +308,12 @@ void
|
||||
TclpThreadExit(
|
||||
int status)
|
||||
{
|
||||
EnterCriticalSection(&joinLock);
|
||||
TclSignalExitThread(Tcl_GetCurrentThread(), status);
|
||||
LeaveCriticalSection(&joinLock);
|
||||
|
||||
WinThread *winThreadPtr;
|
||||
if (winThreadPtr = (WinThread *) TlsGetValue (
|
||||
TclWinGlobals.tls_thread_id)) {
|
||||
if (!winThreadPtr->tHandle)
|
||||
TclpSysFree (winThreadPtr);
|
||||
}
|
||||
#if defined(_MSC_VER) || defined(__MSVCRT__)
|
||||
_endthreadex((unsigned) status);
|
||||
#else
|
||||
@@ -326,8 +340,64 @@ TclpThreadExit(
|
||||
Tcl_ThreadId
|
||||
Tcl_GetCurrentThread(void)
|
||||
{
|
||||
return (Tcl_ThreadId)INT2PTR(GetCurrentThreadId());
|
||||
WinThread *winThreadPtr;
|
||||
if (!(winThreadPtr = (WinThread *) TlsGetValue (
|
||||
TclWinGlobals.tls_thread_id))) {
|
||||
if (ERROR_SUCCESS != GetLastError ())
|
||||
TclWinFailure ("TlsGetValue in Tcl_GetCurrentThread");
|
||||
/* this is an initial thread */
|
||||
/* can't use TclAlloc, as the USE_THREAD_ALLOC per-thread allocator
|
||||
calls Tcl_GetCurrentThread() */
|
||||
if (!(winThreadPtr = (WinThread *) TclpSysAlloc (sizeof *winThreadPtr)))
|
||||
TclWinFailure ("memory allocation");
|
||||
winThreadPtr->tHandle = 0;
|
||||
winThreadPtr->thread_id = GetCurrentThreadId ();
|
||||
if (!TlsSetValue (TclWinGlobals.tls_thread_id, winThreadPtr))
|
||||
TclWinFailure ("TlsSetValue");
|
||||
}
|
||||
return TCL_WIN_THREAD_ID (winThreadPtr, 0 != winThreadPtr->tHandle);
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* TclWinInitOnce
|
||||
*
|
||||
* Perform module initialization exactly once
|
||||
* storage keys.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
static BOOL
|
||||
TclWinInitOnce (
|
||||
INIT_ONCE *init_once,
|
||||
void *parameter,
|
||||
void *context)
|
||||
{
|
||||
struct tclWinGlobals *const twg = parameter;
|
||||
|
||||
InitializeCriticalSection(&twg->initLock);
|
||||
InitializeCriticalSection(&twg->globalLock);
|
||||
twg->tls_thread_id = TlsAlloc ();
|
||||
|
||||
#if TCL_THREADS
|
||||
InitializeCriticalSection(&twg->allocLock.crit);
|
||||
twg->allocLockPtr = &twg->allocLock;
|
||||
#endif /* TCL_THREADS */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
# define TCL_WIN_INITIALIZE_ONCE()\
|
||||
InitOnceExecuteOnce (&TclWinGlobals.init_once,\
|
||||
TclWinInitOnce,\
|
||||
&TclWinGlobals,\
|
||||
0)
|
||||
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
@@ -351,20 +421,9 @@ Tcl_GetCurrentThread(void)
|
||||
void
|
||||
TclpInitLock(void)
|
||||
{
|
||||
if (!initialized) {
|
||||
/*
|
||||
* There is a fundamental race here that is solved by creating the
|
||||
* first Tcl interpreter in a single threaded environment. Once the
|
||||
* interpreter has been created, it is safe to create more threads
|
||||
* that create interpreters in parallel.
|
||||
*/
|
||||
|
||||
initialized = 1;
|
||||
InitializeCriticalSection(&joinLock);
|
||||
InitializeCriticalSection(&initLock);
|
||||
InitializeCriticalSection(&globalLock);
|
||||
}
|
||||
EnterCriticalSection(&initLock);
|
||||
if (!TCL_WIN_INITIALIZE_ONCE ())
|
||||
TclWinFailure ("InitOnceExecuteOnce");
|
||||
EnterCriticalSection(&TclWinGlobals.initLock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -387,7 +446,7 @@ TclpInitLock(void)
|
||||
void
|
||||
TclpInitUnlock(void)
|
||||
{
|
||||
LeaveCriticalSection(&initLock);
|
||||
LeaveCriticalSection(&TclWinGlobals.initLock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -413,20 +472,9 @@ TclpInitUnlock(void)
|
||||
void
|
||||
TclpGlobalLock(void)
|
||||
{
|
||||
if (!initialized) {
|
||||
/*
|
||||
* There is a fundamental race here that is solved by creating the
|
||||
* first Tcl interpreter in a single threaded environment. Once the
|
||||
* interpreter has been created, it is safe to create more threads
|
||||
* that create interpreters in parallel.
|
||||
*/
|
||||
|
||||
initialized = 1;
|
||||
InitializeCriticalSection(&joinLock);
|
||||
InitializeCriticalSection(&initLock);
|
||||
InitializeCriticalSection(&globalLock);
|
||||
}
|
||||
EnterCriticalSection(&globalLock);
|
||||
if (!TCL_WIN_INITIALIZE_ONCE ())
|
||||
TclWinFailure ("InitOnceExecuteOnce");
|
||||
EnterCriticalSection(&TclWinGlobals.globalLock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -449,7 +497,7 @@ TclpGlobalLock(void)
|
||||
void
|
||||
TclpGlobalUnlock(void)
|
||||
{
|
||||
LeaveCriticalSection(&globalLock);
|
||||
LeaveCriticalSection(&TclWinGlobals.globalLock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -475,11 +523,9 @@ Tcl_Mutex *
|
||||
Tcl_GetAllocMutex(void)
|
||||
{
|
||||
#if TCL_THREADS
|
||||
if (!allocOnce) {
|
||||
InitializeCriticalSection(&allocLock.crit);
|
||||
allocOnce = 1;
|
||||
}
|
||||
return &allocLockPtr;
|
||||
if (!TCL_WIN_INITIALIZE_ONCE ())
|
||||
TclWinFailure ("InitOnceExecuteOnce");
|
||||
return &TclWinGlobals.allocLockPtr;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
@@ -506,36 +552,35 @@ Tcl_GetAllocMutex(void)
|
||||
void
|
||||
TclFinalizeLock(void)
|
||||
{
|
||||
struct tclWinGlobals *const twg = &TclWinGlobals;
|
||||
TclpGlobalLock();
|
||||
DeleteCriticalSection(&joinLock);
|
||||
|
||||
if (!TlsFree (twg->tls_thread_id))
|
||||
TclWinFailure ("TlsFree");
|
||||
|
||||
/*
|
||||
* Destroy the critical section that we are holding!
|
||||
*/
|
||||
|
||||
DeleteCriticalSection(&globalLock);
|
||||
initialized = 0;
|
||||
DeleteCriticalSection(&twg->globalLock);
|
||||
|
||||
#if TCL_THREADS
|
||||
if (allocOnce) {
|
||||
DeleteCriticalSection(&allocLock.crit);
|
||||
allocOnce = 0;
|
||||
}
|
||||
DeleteCriticalSection(&twg->allocLock.crit);
|
||||
#endif
|
||||
|
||||
LeaveCriticalSection(&initLock);
|
||||
LeaveCriticalSection(&twg->initLock);
|
||||
|
||||
/*
|
||||
* Destroy the critical section that we were holding.
|
||||
*/
|
||||
|
||||
DeleteCriticalSection(&initLock);
|
||||
DeleteCriticalSection(&twg->initLock);
|
||||
|
||||
InitOnceInitialize (&twg->init_once); /* assuming we want to
|
||||
re-initialize at some point -- dubious */
|
||||
}
|
||||
|
||||
#if TCL_THREADS
|
||||
|
||||
/* locally used prototype */
|
||||
static void FinalizeConditionEvent(void *data);
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
@@ -558,25 +603,25 @@ void
|
||||
Tcl_MutexLock(
|
||||
Tcl_Mutex *mutexPtr) /* The lock */
|
||||
{
|
||||
CRITICAL_SECTION *csPtr;
|
||||
struct Tcl_Mutex_ *mp;
|
||||
|
||||
if (*mutexPtr == NULL) {
|
||||
TclpGlobalLock();
|
||||
|
||||
/*
|
||||
* Double inside global lock check to avoid a race.
|
||||
* BROKEN: does work in relaxed memory models
|
||||
*/
|
||||
|
||||
if (*mutexPtr == NULL) {
|
||||
csPtr = (CRITICAL_SECTION *)Tcl_Alloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSection(csPtr);
|
||||
*mutexPtr = (Tcl_Mutex)csPtr;
|
||||
mp = Tcl_Alloc (sizeof *mp);
|
||||
InitializeCriticalSection (&mp->crit);
|
||||
*mutexPtr = mp;
|
||||
TclRememberMutex(mutexPtr);
|
||||
}
|
||||
TclpGlobalUnlock();
|
||||
}
|
||||
csPtr = *((CRITICAL_SECTION **)mutexPtr);
|
||||
EnterCriticalSection(csPtr);
|
||||
EnterCriticalSection (&(*mutexPtr)->crit);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -599,9 +644,7 @@ void
|
||||
Tcl_MutexUnlock(
|
||||
Tcl_Mutex *mutexPtr) /* The lock */
|
||||
{
|
||||
CRITICAL_SECTION *csPtr = *((CRITICAL_SECTION **)mutexPtr);
|
||||
|
||||
LeaveCriticalSection(csPtr);
|
||||
LeaveCriticalSection (&(*mutexPtr)->crit);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -625,11 +668,9 @@ void
|
||||
TclpFinalizeMutex(
|
||||
Tcl_Mutex *mutexPtr)
|
||||
{
|
||||
CRITICAL_SECTION *csPtr = *(CRITICAL_SECTION **)mutexPtr;
|
||||
|
||||
if (csPtr != NULL) {
|
||||
DeleteCriticalSection(csPtr);
|
||||
Tcl_Free(csPtr);
|
||||
if (*mutexPtr) {
|
||||
DeleteCriticalSection(&(*mutexPtr)->crit);
|
||||
Tcl_Free(*mutexPtr);
|
||||
*mutexPtr = NULL;
|
||||
}
|
||||
}
|
||||
@@ -808,8 +849,8 @@ Tcl_ConditionWait(
|
||||
*
|
||||
* This procedure is invoked to signal a condition variable.
|
||||
*
|
||||
* The mutex must be held during this call to avoid races, but this
|
||||
* interface does not enforce that.
|
||||
* The mutex does not need to be held during this call to avoid races
|
||||
* and should not be, to minimize the number of context switches
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
@@ -824,68 +865,19 @@ void
|
||||
Tcl_ConditionNotify(
|
||||
Tcl_Condition *condPtr)
|
||||
{
|
||||
WinCondition *winCondPtr;
|
||||
ThreadSpecificData *tsdPtr;
|
||||
/* double checked locking, i.e., BROKEN */
|
||||
if (*condPtr == NULL) {
|
||||
TclpGlobalLock();
|
||||
|
||||
if (*condPtr != NULL) {
|
||||
winCondPtr = *((WinCondition **)condPtr);
|
||||
|
||||
if (winCondPtr == NULL) {
|
||||
return;
|
||||
if (*condPtr == NULL) {
|
||||
WinCondition *winCondPtr;
|
||||
winCondPtr = (WinCondition *)Tcl_Alloc(sizeof *winCondPtr);
|
||||
InitializeConditionVariable (&winCondPtr->cv);
|
||||
*condPtr = winCondPtr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop through all the threads waiting on the condition and notify
|
||||
* them (i.e., broadcast semantics). The queue manipulation is guarded
|
||||
* by the per-condition coordinating mutex.
|
||||
*/
|
||||
|
||||
EnterCriticalSection(&winCondPtr->condLock);
|
||||
while (winCondPtr->firstPtr != NULL) {
|
||||
tsdPtr = winCondPtr->firstPtr;
|
||||
winCondPtr->firstPtr = tsdPtr->nextPtr;
|
||||
if (winCondPtr->lastPtr == tsdPtr) {
|
||||
winCondPtr->lastPtr = NULL;
|
||||
}
|
||||
tsdPtr->flags = WIN_THREAD_RUNNING;
|
||||
tsdPtr->nextPtr = NULL;
|
||||
tsdPtr->prevPtr = NULL; /* Not strictly necessary, see A: */
|
||||
SetEvent(tsdPtr->condEvent);
|
||||
}
|
||||
LeaveCriticalSection(&winCondPtr->condLock);
|
||||
} else {
|
||||
/*
|
||||
* No-one has used the condition variable, so there are no waiters.
|
||||
*/
|
||||
TclpGlobalUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------
|
||||
*
|
||||
* FinalizeConditionEvent --
|
||||
*
|
||||
* This procedure is invoked to clean up the per-thread event used to
|
||||
* implement condition waiting. This is only safe to call at the end of
|
||||
* time.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* The per-thread event is closed.
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void
|
||||
FinalizeConditionEvent(
|
||||
void *data)
|
||||
{
|
||||
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) data;
|
||||
|
||||
tsdPtr->flags = WIN_THREAD_UNINIT;
|
||||
CloseHandle(tsdPtr->condEvent);
|
||||
WakeAllConditionVariable (&(*condPtr)->cv);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -911,7 +903,7 @@ void
|
||||
TclpFinalizeCondition(
|
||||
Tcl_Condition *condPtr)
|
||||
{
|
||||
WinCondition *winCondPtr = *(WinCondition **)condPtr;
|
||||
WinCondition *winCondPtr = *condPtr;
|
||||
|
||||
/*
|
||||
* Note - this is called long after the thread-local storage is reclaimed.
|
||||
@@ -921,7 +913,6 @@ TclpFinalizeCondition(
|
||||
*/
|
||||
|
||||
if (winCondPtr != NULL) {
|
||||
DeleteCriticalSection(&winCondPtr->condLock);
|
||||
Tcl_Free(winCondPtr);
|
||||
*condPtr = NULL;
|
||||
}
|
||||
@@ -935,6 +926,13 @@ TclpFinalizeCondition(
|
||||
*/
|
||||
#ifdef USE_THREAD_ALLOC
|
||||
|
||||
static DWORD tlsKey;
|
||||
|
||||
typedef struct {
|
||||
Tcl_Mutex tlock;
|
||||
CRITICAL_SECTION wlock;
|
||||
} allocMutex;
|
||||
|
||||
Tcl_Mutex *
|
||||
TclpNewAllocMutex(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user