diff options
Diffstat (limited to 'unix/tclUnixThrd.c')
-rw-r--r-- | unix/tclUnixThrd.c | 111 |
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 + } } /* |