summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixThrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixThrd.c')
-rw-r--r--unix/tclUnixThrd.c111
1 files changed, 105 insertions, 6 deletions
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
index d30791d..18b419f 100644
--- a/unix/tclUnixThrd.c
+++ b/unix/tclUnixThrd.c
@@ -21,6 +21,19 @@ typedef struct ThreadSpecificData {
static Tcl_ThreadDataKey dataKey;
/*
+ * This is the number of milliseconds to wait between internal retries in
+ * the Tcl_MutexLock function. This value must be greater than zero and
+ * should be a suitable value for the given platform.
+ *
+ * TODO: This may need to be dynamically determined, based on the relative
+ * performance of the running process.
+ */
+
+#ifndef TCL_MUTEX_LOCK_SLEEP_TIME
+# define TCL_MUTEX_LOCK_SLEEP_TIME (25)
+#endif
+
+/*
* masterLock is used to serialize creation of mutexes, condition variables,
* and thread local storage. This is the only place that can count on the
* ability to statically initialize the mutex.
@@ -44,6 +57,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.
*/
@@ -219,13 +239,13 @@ TclpThreadGetStackSize(void)
#if defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) && defined(TclpPthreadGetAttrs)
pthread_attr_t threadAttr; /* This will hold the thread attributes for
* the current thread. */
-#ifdef __GLIBC__
+#ifdef __GLIBC__
/*
* Fix for [Bug 1815573]
*
* DESCRIPTION:
* On linux TclpPthreadGetAttrs (which is pthread_attr_get_np) may return
- * bogus values on the initial thread.
+ * bogus values on the initial thread.
*
* ASSUMPTIONS:
* There seems to be no api to determine if we are on the initial
@@ -263,7 +283,7 @@ TclpThreadGetStackSize(void)
}
}
-
+
if (pthread_attr_getstacksize(&threadAttr, &stackSize) != 0) {
pthread_attr_destroy(&threadAttr);
return (size_t)-1;
@@ -274,7 +294,7 @@ TclpThreadGetStackSize(void)
/*
* On Darwin, the API below does not return the correct stack size for the
* main thread (which is not a real pthread), so fallback to getrlimit().
- */
+ */
if (!pthread_main_np())
#endif
stackSize = pthread_get_stacksize_np(pthread_self());
@@ -457,6 +477,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
@@ -512,6 +584,9 @@ Tcl_MutexLock(
Tcl_Mutex *mutexPtr) /* Really (pthread_mutex_t **) */
{
pthread_mutex_t *pmutexPtr;
+
+retry:
+
if (*mutexPtr == NULL) {
MASTER_LOCK;
if (*mutexPtr == NULL) {
@@ -526,8 +601,32 @@ Tcl_MutexLock(
}
MASTER_UNLOCK;
}
- pmutexPtr = *((pthread_mutex_t **)mutexPtr);
- pthread_mutex_lock(pmutexPtr);
+ while (1) {
+ TclpMutexLock();
+ pmutexPtr = *((pthread_mutex_t **)mutexPtr);
+ if (pmutexPtr == NULL) {
+ TclpMutexUnlock();
+ goto retry;
+ }
+ if (pthread_mutex_trylock(pmutexPtr) == 0) {
+ TclpMutexUnlock();
+ return;
+ }
+ TclpMutexUnlock();
+ /*
+ * BUGBUG: All core and Thread package tests pass when usleep()
+ * is used; however, the Thread package tests hang at
+ * various places when Tcl_Sleep() is used, typically
+ * while running test "thread-17.8", "thread-17.9", or
+ * "thread-17.11a". Really, what we want here is just
+ * to yield to other threads for a while.
+ */
+#ifdef HAVE_USLEEP
+ usleep(TCL_MUTEX_LOCK_SLEEP_TIME * 1000);
+#else
+ Tcl_Sleep(TCL_MUTEX_LOCK_SLEEP_TIME);
+#endif
+ }
}
/*