From beea2ed90e5ef5ef3f760f1697e207d493813a0d Mon Sep 17 00:00:00 2001 From: Joe Mistachkin Date: Sun, 17 May 2015 22:13:39 +0000 Subject: This should completely fix the race conditions at the cost of more complexity. Also, on Unix, a more reliable means than Tcl_Sleep() of sleeping for a short time is needed. --- unix/tclUnixThrd.c | 41 ++++++++++++++++++++++++++++++++++------- win/tclWinThrd.c | 30 +++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c index 3a59624..11df5ee 100644 --- a/unix/tclUnixThrd.c +++ b/unix/tclUnixThrd.c @@ -22,6 +22,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 (0) +#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. @@ -496,14 +509,28 @@ retry: } MASTER_UNLOCK; } - TclpMutexLock(); - pmutexPtr = *((pthread_mutex_t **)mutexPtr); - if (pmutexPtr == NULL) { - TclpMutexUnlock(); - goto retry; + 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. + */ + Tcl_Sleep(TCL_MUTEX_LOCK_SLEEP_TIME); } - TclpMutexUnlock(); - pthread_mutex_lock(pmutexPtr); } /* diff --git a/win/tclWinThrd.c b/win/tclWinThrd.c index ef42a83..7fd5ff5 100644 --- a/win/tclWinThrd.c +++ b/win/tclWinThrd.c @@ -24,6 +24,16 @@ _CRTIMP unsigned int __cdecl _controlfp (unsigned int unNew, unsigned int unMask #endif /* + * This is the number of milliseconds to wait between internal retries in + * the Tcl_MutexLock function. This value must be greater than or equal + * to zero and should be a suitable value for the given platform. + */ + +#ifndef TCL_MUTEX_LOCK_SLEEP_TIME +# define TCL_MUTEX_LOCK_SLEEP_TIME (0) +#endif + +/* * This is the master lock used to serialize access to other serialization * data structures. */ @@ -641,14 +651,20 @@ retry: } MASTER_UNLOCK; } - TclpMutexLock(); - csPtr = *((CRITICAL_SECTION **)mutexPtr); - if (csPtr == NULL) { - TclpMutexUnlock(); - goto retry; + while (1) { + TclpMutexLock(); + csPtr = *((CRITICAL_SECTION **)mutexPtr); + if (csPtr == NULL) { + TclpMutexUnlock(); + goto retry; + } + if (TryEnterCriticalSection(csPtr)) { + TclpMutexUnlock(); + return; + } + TclpMutexUnlock(); + Tcl_Sleep(TCL_MUTEX_LOCK_SLEEP_TIME); } - TclpMutexUnlock(); - EnterCriticalSection(csPtr); } /* -- cgit v0.12