summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/Thread.320
-rw-r--r--generic/tcl.decls5
-rw-r--r--generic/tcl.h2
-rw-r--r--generic/tclDecls.h5
-rw-r--r--generic/tclInt.h2
-rw-r--r--generic/tclStubInit.c1
-rw-r--r--generic/tclThread.c38
-rw-r--r--unix/tclUnixNotfy.c2
-rw-r--r--unix/tclUnixThrd.c67
-rw-r--r--[-rwxr-xr-x]win/tclWinFile.c0
-rw-r--r--win/tclWinThrd.c63
11 files changed, 198 insertions, 7 deletions
diff --git a/doc/Thread.3 b/doc/Thread.3
index ac5f2ba..c84dcb4 100644
--- a/doc/Thread.3
+++ b/doc/Thread.3
@@ -9,7 +9,7 @@
.so man.macros
.BS
.SH NAME
-Tcl_ConditionNotify, Tcl_ConditionWait, Tcl_ConditionFinalize, Tcl_GetThreadData, Tcl_MutexLock, Tcl_MutexUnlock, Tcl_MutexFinalize, Tcl_CreateThread, Tcl_JoinThread \- Tcl thread support
+Tcl_ConditionNotify, Tcl_ConditionWait, Tcl_ConditionFinalize, Tcl_GetThreadData, Tcl_MutexLock, Tcl_MutexUnlock, Tcl_MutexFinalize, Tcl_MutexUnlockAndFinalize, Tcl_CreateThread, Tcl_JoinThread \- Tcl thread support
.SH SYNOPSIS
.nf
\fB#include <tcl.h>\fR
@@ -35,6 +35,9 @@ void
void
\fBTcl_MutexFinalize\fR(\fImutexPtr\fR)
.sp
+void
+\fBTcl_MutexUnlockAndFinalize\fR(\fImutexPtr\fR)
+.sp
int
\fBTcl_CreateThread\fR(\fIidPtr, proc, clientData, stackSize, flags\fR)
.sp
@@ -138,11 +141,16 @@ A mutex is a lock that is used to serialize all threads through a piece
of code by calling \fBTcl_MutexLock\fR and \fBTcl_MutexUnlock\fR.
If one thread holds a mutex, any other thread calling \fBTcl_MutexLock\fR will
block until \fBTcl_MutexUnlock\fR is called.
-A mutex can be destroyed after its use by calling \fBTcl_MutexFinalize\fR.
+A mutex can be destroyed after its use by calling \fBTcl_MutexFinalize\fR or
+\fBTcl_MutexUnlockAndFinalize\fR. The \fBTcl_MutexFinalize\fR function may
+only be used on unlocked mutexes; otherwise, its behavior is undefined. The
+\fBTcl_MutexUnlockAndFinalize\fR function may only be used on locked mutexes;
+otherwise, its behavior is undefined.
The result of locking a mutex twice from the same thread is undefined.
On some platforms it will result in a deadlock.
-The \fBTcl_MutexLock\fR, \fBTcl_MutexUnlock\fR and \fBTcl_MutexFinalize\fR
-procedures are defined as empty macros if not compiling with threads enabled.
+The \fBTcl_MutexLock\fR, \fBTcl_MutexUnlock\fR, \fBTcl_MutexFinalize\fR, and
+\fBTcl_MutexUnlockAndFinalize\fR procedures are defined as empty macros if
+not compiling with threads enabled.
For declaration of mutexes the \fBTCL_DECLARE_MUTEX\fR macro should be used.
This macro assures correct mutex handling even when the core is compiled
without threads enabled.
@@ -176,8 +184,8 @@ They are implemented as opaque pointers that should be NULL
upon first use.
The mutexes and condition variables are
either cleaned up by process exit handlers (if living that long) or
-explicitly by calls to \fBTcl_MutexFinalize\fR or
-\fBTcl_ConditionFinalize\fR.
+explicitly by calls to \fBTcl_MutexFinalize\fR (or
+\fBTcl_MutexUnlockAndFinalize\fR) and \fBTcl_ConditionFinalize\fR.
Thread local storage is reclaimed during \fBTcl_FinalizeThread\fR.
.SH "SCRIPT-LEVEL ACCESS TO THREADS"
.PP
diff --git a/generic/tcl.decls b/generic/tcl.decls
index 1829249..8be7d32 100644
--- a/generic/tcl.decls
+++ b/generic/tcl.decls
@@ -2326,6 +2326,11 @@ declare 630 {
# ----- BASELINE -- FOR -- 8.6.0 ----- #
+# TIP #XXX
+declare 631 {
+ void Tcl_MutexUnlockAndFinalize(Tcl_Mutex *mutex)
+}
+
##############################################################################
# Define the platform specific public Tcl interface. These functions are only
diff --git a/generic/tcl.h b/generic/tcl.h
index 297b42c..80f924a 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -2592,6 +2592,8 @@ EXTERN void Tcl_GetMemoryInfo(Tcl_DString *dsPtr);
#define Tcl_MutexLock(mutexPtr)
#undef Tcl_MutexUnlock
#define Tcl_MutexUnlock(mutexPtr)
+#undef Tcl_MutexUnlockAndFinalize
+#define Tcl_MutexUnlockAndFinalize(mutexPtr)
#undef Tcl_MutexFinalize
#define Tcl_MutexFinalize(mutexPtr)
#undef Tcl_ConditionNotify
diff --git a/generic/tclDecls.h b/generic/tclDecls.h
index 91c0add..f4ebc53 100644
--- a/generic/tclDecls.h
+++ b/generic/tclDecls.h
@@ -1815,6 +1815,8 @@ EXTERN int Tcl_FSUnloadFile(Tcl_Interp *interp,
EXTERN void Tcl_ZlibStreamSetCompressionDictionary(
Tcl_ZlibStream zhandle,
Tcl_Obj *compressionDictionaryObj);
+/* 631 */
+EXTERN void Tcl_MutexUnlockAndFinalize(Tcl_Mutex *mutex);
typedef struct {
const struct TclPlatStubs *tclPlatStubs;
@@ -2481,6 +2483,7 @@ typedef struct TclStubs {
void * (*tcl_FindSymbol) (Tcl_Interp *interp, Tcl_LoadHandle handle, const char *symbol); /* 628 */
int (*tcl_FSUnloadFile) (Tcl_Interp *interp, Tcl_LoadHandle handlePtr); /* 629 */
void (*tcl_ZlibStreamSetCompressionDictionary) (Tcl_ZlibStream zhandle, Tcl_Obj *compressionDictionaryObj); /* 630 */
+ void (*tcl_MutexUnlockAndFinalize) (Tcl_Mutex *mutex); /* 631 */
} TclStubs;
extern const TclStubs *tclStubsPtr;
@@ -3773,6 +3776,8 @@ extern const TclStubs *tclStubsPtr;
(tclStubsPtr->tcl_FSUnloadFile) /* 629 */
#define Tcl_ZlibStreamSetCompressionDictionary \
(tclStubsPtr->tcl_ZlibStreamSetCompressionDictionary) /* 630 */
+#define Tcl_MutexUnlockAndFinalize \
+ (tclStubsPtr->tcl_MutexUnlockAndFinalize) /* 631 */
#endif /* defined(USE_TCL_STUBS) */
diff --git a/generic/tclInt.h b/generic/tclInt.h
index c89f053..51d153b 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -3059,6 +3059,8 @@ MODULE_SCOPE void TclpInitUnlock(void);
MODULE_SCOPE Tcl_Obj * TclpObjListVolumes(void);
MODULE_SCOPE void TclpMasterLock(void);
MODULE_SCOPE void TclpMasterUnlock(void);
+MODULE_SCOPE void TclpMutexLock(void);
+MODULE_SCOPE void TclpMutexUnlock(void);
MODULE_SCOPE int TclpMatchFiles(Tcl_Interp *interp, char *separators,
Tcl_DString *dirPtr, char *pattern, char *tail);
MODULE_SCOPE int TclpObjNormalizePath(Tcl_Interp *interp,
diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c
index 7a84cba..f0d00ee 100644
--- a/generic/tclStubInit.c
+++ b/generic/tclStubInit.c
@@ -1412,6 +1412,7 @@ const TclStubs tclStubs = {
Tcl_FindSymbol, /* 628 */
Tcl_FSUnloadFile, /* 629 */
Tcl_ZlibStreamSetCompressionDictionary, /* 630 */
+ Tcl_MutexUnlockAndFinalize, /* 631 */
};
/* !END!: Do not edit above this line. */
diff --git a/generic/tclThread.c b/generic/tclThread.c
index 198fa6a..b46ddf5 100644
--- a/generic/tclThread.c
+++ b/generic/tclThread.c
@@ -50,6 +50,7 @@ static void RememberSyncObject(void *objPtr,
#undef Tcl_MutexLock
#undef Tcl_MutexUnlock
#undef Tcl_MutexFinalize
+#undef Tcl_MutexUnlockAndFinalize
#undef Tcl_ConditionNotify
#undef Tcl_ConditionWait
#undef Tcl_ConditionFinalize
@@ -284,6 +285,43 @@ Tcl_MutexFinalize(
/*
*----------------------------------------------------------------------
*
+ * Tcl_MutexUnlockAndFinalize --
+ *
+ * This procedure is invoked to unlock and then finalize a mutex.
+ * The mutex must have been locked by Tcl_MutexLock. It is also
+ * removed from the list of remembered objects. The mutex can no
+ * longer be used after calling this procedure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Remove the mutex from the list.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_MutexUnlockAndFinalize(
+ Tcl_Mutex *mutexPtr)
+{
+ Tcl_Mutex mutex;
+ TclpMasterLock();
+ TclpMutexLock();
+#ifdef TCL_THREADS
+ mutex = *mutexPtr;
+ *mutexPtr = NULL; /* Force it to be created again. */
+ Tcl_MutexUnlock(&mutex);
+ TclpFinalizeMutex(&mutex);
+#endif
+ ForgetSyncObject(mutexPtr, &mutexRecord);
+ TclpMutexUnlock();
+ TclpMasterUnlock();
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TclRememberCondition
*
* Keep a list of condition variables used during finalization.
diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c
index b2bea45..aa32915 100644
--- a/unix/tclUnixNotfy.c
+++ b/unix/tclUnixNotfy.c
@@ -1376,7 +1376,7 @@ AtForkParent(void)
static void
AtForkChild(void)
{
- Tcl_MutexFinalize(&notifierMutex);
+ Tcl_MutexUnlockAndFinalize(&notifierMutex);
Tcl_InitNotifier();
}
#endif /* HAVE_PTHREAD_ATFORK */
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
index d34bc88..42b6bda 100644
--- a/unix/tclUnixThrd.c
+++ b/unix/tclUnixThrd.c
@@ -45,6 +45,13 @@ static pthread_mutex_t allocLock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t *allocLockPtr = &allocLock;
/*
+ * The mutexLock serializes Tcl_MutexLock. This is necessary to prevent
+ * races when finalizing a mutex that some other thread may want to lock.
+ */
+
+static pthread_mutex_t mutexLock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
* These are for the critical sections inside this file.
*/
@@ -365,6 +372,58 @@ TclpMasterUnlock(void)
/*
*----------------------------------------------------------------------
*
+ * TclpMutexLock
+ *
+ * This procedure is used to grab a lock that serializes locking
+ * another mutex.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpMutexLock(void)
+{
+#ifdef TCL_THREADS
+ pthread_mutex_lock(&mutexLock);
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpMutexUnlock
+ *
+ * This procedure is used to release a lock that serializes locking
+ * another mutex.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpMutexUnlock(void)
+{
+#ifdef TCL_THREADS
+ pthread_mutex_unlock(&mutexLock);
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_GetAllocMutex
*
* This procedure returns a pointer to a statically initialized mutex for
@@ -421,6 +480,8 @@ Tcl_MutexLock(
{
pthread_mutex_t *pmutexPtr;
+retry:
+
if (*mutexPtr == NULL) {
MASTER_LOCK;
if (*mutexPtr == NULL) {
@@ -435,8 +496,14 @@ Tcl_MutexLock(
}
MASTER_UNLOCK;
}
+ TclpMutexLock();
pmutexPtr = *((pthread_mutex_t **)mutexPtr);
+ if (pmutexPtr == NULL) {
+ TclpMutexUnlock();
+ goto retry;
+ }
pthread_mutex_lock(pmutexPtr);
+ TclpMutexUnlock();
}
/*
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index a5b14b4..a5b14b4 100755..100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c
index 1c9d483..4748818 100644
--- a/win/tclWinThrd.c
+++ b/win/tclWinThrd.c
@@ -57,6 +57,13 @@ static int allocOnce = 0;
#endif /* TCL_THREADS */
/*
+ * The mutexLock serializes Tcl_MutexLock. This is necessary to prevent
+ * races when finalizing a mutex that some other thread may want to lock.
+ */
+
+static CRITICAL_SECTION mutexLock;
+
+/*
* 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
@@ -369,6 +376,7 @@ TclpInitLock(void)
*/
init = 1;
+ InitializeCriticalSection(&mutexLock);
InitializeCriticalSection(&joinLock);
InitializeCriticalSection(&initLock);
InitializeCriticalSection(&masterLock);
@@ -464,6 +472,52 @@ TclpMasterUnlock(void)
/*
*----------------------------------------------------------------------
*
+ * TclpMutexLock
+ *
+ * This procedure is used to grab a lock that serializes locking
+ * another mutex.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpMutexLock(void)
+{
+ EnterCriticalSection(&mutexLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpMutexUnlock
+ *
+ * This procedure is used to release a lock that serializes locking
+ * another mutex.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpMutexUnlock(void)
+{
+ LeaveCriticalSection(&mutexLock);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_GetAllocMutex
*
* This procedure returns a pointer to a statically initialized mutex for
@@ -516,6 +570,7 @@ void
TclFinalizeLock(void)
{
MASTER_LOCK;
+ DeleteCriticalSection(&mutexLock);
DeleteCriticalSection(&joinLock);
/*
@@ -569,6 +624,8 @@ Tcl_MutexLock(
{
CRITICAL_SECTION *csPtr;
+retry:
+
if (*mutexPtr == NULL) {
MASTER_LOCK;
@@ -584,8 +641,14 @@ Tcl_MutexLock(
}
MASTER_UNLOCK;
}
+ TclpMutexLock();
csPtr = *((CRITICAL_SECTION **)mutexPtr);
+ if (csPtr == NULL) {
+ TclpMutexUnlock();
+ goto retry;
+ }
EnterCriticalSection(csPtr);
+ TclpMutexUnlock();
}
/*