summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixThrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixThrd.c')
-rw-r--r--unix/tclUnixThrd.c271
1 files changed, 210 insertions, 61 deletions
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
index afb795d..92399f4 100644
--- a/unix/tclUnixThrd.c
+++ b/unix/tclUnixThrd.c
@@ -13,13 +13,160 @@
#include "tclInt.h"
-#ifdef TCL_THREADS
+#if TCL_THREADS
+
+/*
+ * TIP #509. Ensures that Tcl's mutexes are reentrant.
+ *
+ *----------------------------------------------------------------------
+ *
+ * PMutexInit --
+ *
+ * Sets up the memory pointed to by its argument so that it contains the
+ * implementation of a recursive lock. Caller supplies the space.
+ *
+ *----------------------------------------------------------------------
+ *
+ * PMutexDestroy --
+ *
+ * Tears down the implementation of a recursive lock (but does not
+ * deallocate the space holding the lock).
+ *
+ *----------------------------------------------------------------------
+ *
+ * PMutexLock --
+ *
+ * Locks a recursive lock. (Similar to pthread_mutex_lock)
+ *
+ *----------------------------------------------------------------------
+ *
+ * PMutexUnlock --
+ *
+ * Unlocks a recursive lock. (Similar to pthread_mutex_unlock)
+ *
+ *----------------------------------------------------------------------
+ *
+ * PCondWait --
+ *
+ * Waits on a condition variable linked a recursive lock. (Similar to
+ * pthread_cond_wait)
+ *
+ *----------------------------------------------------------------------
+ *
+ * PCondTimedWait --
+ *
+ * Waits for a limited amount of time on a condition variable linked to a
+ * recursive lock. (Similar to pthread_cond_timedwait)
+ *
+ *----------------------------------------------------------------------
+ */
+
+#ifndef HAVE_DECL_PTHREAD_MUTEX_RECURSIVE
+#define HAVE_DECL_PTHREAD_MUTEX_RECURSIVE 0
+#endif
+
+#if HAVE_DECL_PTHREAD_MUTEX_RECURSIVE
+/*
+ * Pthread has native reentrant (AKA recursive) mutexes. Use them for
+ * Tcl_Mutex.
+ */
+
+typedef pthread_mutex_t PMutex;
+
+static void
+PMutexInit(
+ PMutex *pmutexPtr)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(pmutexPtr, &attr);
+}
+
+#define PMutexDestroy pthread_mutex_destroy
+#define PMutexLock pthread_mutex_lock
+#define PMutexUnlock pthread_mutex_unlock
+#define PCondWait pthread_cond_wait
+#define PCondTimedWait pthread_cond_timedwait
+
+#else /* !HAVE_PTHREAD_MUTEX_RECURSIVE */
-typedef struct ThreadSpecificData {
+/*
+ * No native support for reentrant mutexes. Emulate them with regular mutexes
+ * and thread-local counters.
+ */
+
+typedef struct PMutex {
+ pthread_mutex_t mutex;
+ pthread_t thread;
+ int counter;
+} PMutex;
+
+static void
+PMutexInit(
+ PMutex *pmutexPtr)
+{
+ pthread_mutex_init(&pmutexPtr->mutex, NULL);
+ pmutexPtr->thread = 0;
+ pmutexPtr->counter = 0;
+}
+
+static void
+PMutexDestroy(
+ PMutex *pmutexPtr)
+{
+ pthread_mutex_destroy(&pmutexPtr->mutex);
+}
+
+static void
+PMutexLock(
+ PMutex *pmutexPtr)
+{
+ if (pmutexPtr->thread != pthread_self() || pmutexPtr->counter == 0) {
+ pthread_mutex_lock(&pmutexPtr->mutex);
+ pmutexPtr->thread = pthread_self();
+ pmutexPtr->counter = 0;
+ }
+ pmutexPtr->counter++;
+}
+
+static void
+PMutexUnlock(
+ PMutex *pmutexPtr)
+{
+ pmutexPtr->counter--;
+ if (pmutexPtr->counter == 0) {
+ pmutexPtr->thread = 0;
+ pthread_mutex_unlock(&pmutexPtr->mutex);
+ }
+}
+
+static void
+PCondWait(
+ pthread_cond_t *pcondPtr,
+ PMutex *pmutexPtr)
+{
+ pthread_cond_wait(pcondPtr, &pmutexPtr->mutex);
+}
+
+static void
+PCondTimedWait(
+ pthread_cond_t *pcondPtr,
+ PMutex *pmutexPtr,
+ struct timespec *ptime)
+{
+ pthread_cond_timedwait(pcondPtr, &pmutexPtr->mutex, ptime);
+}
+#endif /* HAVE_PTHREAD_MUTEX_RECURSIVE */
+
+#ifndef TCL_NO_DEPRECATED
+typedef struct {
char nabuf[16];
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
+#endif /* TCL_NO_DEPRECATED */
/*
* globalLock is used to serialize creation of mutexes, condition variables,
@@ -41,15 +188,15 @@ static pthread_mutex_t initLock = PTHREAD_MUTEX_INITIALIZER;
* obvious reasons, cannot use any dynamically allocated storage.
*/
-static pthread_mutex_t allocLock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t *allocLockPtr = &allocLock;
+static PMutex allocLock;
+static pthread_once_t allocLockInitOnce = PTHREAD_ONCE_INIT;
-/*
- * These are for the critical sections inside this file.
- */
-
-#define GLOBAL_LOCK pthread_mutex_lock(&globalLock)
-#define GLOBAL_UNLOCK pthread_mutex_unlock(&globalLock)
+static void
+allocLockInit(void)
+{
+ PMutexInit(&allocLock);
+}
+static PMutex *allocLockPtr = &allocLock;
#endif /* TCL_THREADS */
@@ -79,7 +226,7 @@ TclpThreadCreate(
int flags) /* Flags controlling behaviour of the new
* thread. */
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
pthread_attr_t attr;
pthread_t theThread;
int result;
@@ -157,7 +304,7 @@ Tcl_JoinThread(
* thread we wait upon will be written into.
* May be NULL. */
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
int result;
unsigned long retcode, *retcodePtr = &retcode;
@@ -171,7 +318,6 @@ Tcl_JoinThread(
#endif
}
-#ifdef TCL_THREADS
/*
*----------------------------------------------------------------------
*
@@ -192,9 +338,12 @@ void
TclpThreadExit(
int status)
{
+#if TCL_THREADS
pthread_exit(INT2PTR(status));
-}
+#else /* TCL_THREADS */
+ exit(status);
#endif /* TCL_THREADS */
+}
/*
*----------------------------------------------------------------------
@@ -215,7 +364,7 @@ TclpThreadExit(
Tcl_ThreadId
Tcl_GetCurrentThread(void)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
return (Tcl_ThreadId) pthread_self();
#else
return (Tcl_ThreadId) 0;
@@ -244,7 +393,7 @@ Tcl_GetCurrentThread(void)
void
TclpInitLock(void)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
pthread_mutex_lock(&initLock);
#endif
}
@@ -270,7 +419,7 @@ TclpInitLock(void)
void
TclFinalizeLock(void)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
/*
* You do not need to destroy mutexes that were created with the
* PTHREAD_MUTEX_INITIALIZER macro. These mutexes do not need any
@@ -301,7 +450,7 @@ TclFinalizeLock(void)
void
TclpInitUnlock(void)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
pthread_mutex_unlock(&initLock);
#endif
}
@@ -330,7 +479,7 @@ TclpInitUnlock(void)
void
TclpGlobalLock(void)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
pthread_mutex_lock(&globalLock);
#endif
}
@@ -355,7 +504,7 @@ TclpGlobalLock(void)
void
TclpGlobalUnlock(void)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
pthread_mutex_unlock(&globalLock);
#endif
}
@@ -382,15 +531,17 @@ TclpGlobalUnlock(void)
Tcl_Mutex *
Tcl_GetAllocMutex(void)
{
-#ifdef TCL_THREADS
- pthread_mutex_t **allocLockPtrPtr = &allocLockPtr;
+#if TCL_THREADS
+ PMutex **allocLockPtrPtr = &allocLockPtr;
+
+ pthread_once(&allocLockInitOnce, allocLockInit);
return (Tcl_Mutex *) allocLockPtrPtr;
#else
return NULL;
#endif
}
-#ifdef TCL_THREADS
+#if TCL_THREADS
/*
*----------------------------------------------------------------------
@@ -415,26 +566,26 @@ Tcl_GetAllocMutex(void)
void
Tcl_MutexLock(
- Tcl_Mutex *mutexPtr) /* Really (pthread_mutex_t **) */
+ Tcl_Mutex *mutexPtr) /* Really (PMutex **) */
{
- pthread_mutex_t *pmutexPtr;
+ PMutex *pmutexPtr;
if (*mutexPtr == NULL) {
- GLOBAL_LOCK;
+ pthread_mutex_lock(&globalLock);
if (*mutexPtr == NULL) {
/*
* Double inside global lock check to avoid a race condition.
*/
- pmutexPtr = (pthread_mutex_t *)ckalloc(sizeof(pthread_mutex_t));
- pthread_mutex_init(pmutexPtr, NULL);
- *mutexPtr = (Tcl_Mutex)pmutexPtr;
+ pmutexPtr = (PMutex *)ckalloc(sizeof(PMutex));
+ PMutexInit(pmutexPtr);
+ *mutexPtr = (Tcl_Mutex) pmutexPtr;
TclRememberMutex(mutexPtr);
}
- GLOBAL_UNLOCK;
+ pthread_mutex_unlock(&globalLock);
}
- pmutexPtr = *((pthread_mutex_t **)mutexPtr);
- pthread_mutex_lock(pmutexPtr);
+ pmutexPtr = *((PMutex **) mutexPtr);
+ PMutexLock(pmutexPtr);
}
/*
@@ -456,11 +607,11 @@ Tcl_MutexLock(
void
Tcl_MutexUnlock(
- Tcl_Mutex *mutexPtr) /* Really (pthread_mutex_t **) */
+ Tcl_Mutex *mutexPtr) /* Really (PMutex **) */
{
- pthread_mutex_t *pmutexPtr = *(pthread_mutex_t **) mutexPtr;
+ PMutex *pmutexPtr = *(PMutex **) mutexPtr;
- pthread_mutex_unlock(pmutexPtr);
+ PMutexUnlock(pmutexPtr);
}
/*
@@ -486,10 +637,10 @@ void
TclpFinalizeMutex(
Tcl_Mutex *mutexPtr)
{
- pthread_mutex_t *pmutexPtr = *(pthread_mutex_t **) mutexPtr;
+ PMutex *pmutexPtr = *(PMutex **) mutexPtr;
if (pmutexPtr != NULL) {
- pthread_mutex_destroy(pmutexPtr);
+ PMutexDestroy(pmutexPtr);
ckfree(pmutexPtr);
*mutexPtr = NULL;
}
@@ -520,15 +671,15 @@ TclpFinalizeMutex(
void
Tcl_ConditionWait(
Tcl_Condition *condPtr, /* Really (pthread_cond_t **) */
- Tcl_Mutex *mutexPtr, /* Really (pthread_mutex_t **) */
+ Tcl_Mutex *mutexPtr, /* Really (PMutex **) */
const Tcl_Time *timePtr) /* Timeout on waiting period */
{
pthread_cond_t *pcondPtr;
- pthread_mutex_t *pmutexPtr;
+ PMutex *pmutexPtr;
struct timespec ptime;
if (*condPtr == NULL) {
- GLOBAL_LOCK;
+ pthread_mutex_lock(&globalLock);
/*
* Double check inside mutex to avoid race, then initialize condition
@@ -541,12 +692,12 @@ Tcl_ConditionWait(
*condPtr = (Tcl_Condition) pcondPtr;
TclRememberCondition(condPtr);
}
- GLOBAL_UNLOCK;
+ pthread_mutex_unlock(&globalLock);
}
- pmutexPtr = *((pthread_mutex_t **)mutexPtr);
+ pmutexPtr = *((PMutex **)mutexPtr);
pcondPtr = *((pthread_cond_t **)condPtr);
if (timePtr == NULL) {
- pthread_cond_wait(pcondPtr, pmutexPtr);
+ PCondWait(pcondPtr, pmutexPtr);
} else {
Tcl_Time now;
@@ -559,7 +710,7 @@ Tcl_ConditionWait(
ptime.tv_sec = timePtr->sec + now.sec +
(timePtr->usec + now.usec) / 1000000;
ptime.tv_nsec = 1000 * ((timePtr->usec + now.usec) % 1000000);
- pthread_cond_timedwait(pcondPtr, pmutexPtr, &ptime);
+ PCondTimedWait(pcondPtr, pmutexPtr, &ptime);
}
}
@@ -651,6 +802,7 @@ TclpFinalizeCondition(
*----------------------------------------------------------------------
*/
+#ifndef TCL_NO_DEPRECATED
Tcl_DirEntry *
TclpReaddir(
TclDIR * dir)
@@ -663,7 +815,7 @@ char *
TclpInetNtoa(
struct in_addr addr)
{
-#ifdef TCL_THREADS
+#if TCL_THREADS
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
unsigned char *b = (unsigned char*) &addr.s_addr;
@@ -673,26 +825,26 @@ TclpInetNtoa(
return inet_ntoa(addr);
#endif
}
+#endif /* TCL_NO_DEPRECATED */
-#ifdef TCL_THREADS
+#if TCL_THREADS
/*
* Additions by AOL for specialized thread memory allocator.
*/
#ifdef USE_THREAD_ALLOC
-static volatile int initialized = 0;
static pthread_key_t key;
typedef struct {
Tcl_Mutex tlock;
- pthread_mutex_t plock;
+ PMutex plock;
} AllocMutex;
Tcl_Mutex *
TclpNewAllocMutex(void)
{
AllocMutex *lockPtr;
- pthread_mutex_t *plockPtr;
+ PMutex *plockPtr;
lockPtr = (AllocMutex *)malloc(sizeof(AllocMutex));
if (lockPtr == NULL) {
@@ -700,7 +852,7 @@ TclpNewAllocMutex(void)
}
plockPtr = &lockPtr->plock;
lockPtr->tlock = (Tcl_Mutex) plockPtr;
- pthread_mutex_init(&lockPtr->plock, NULL);
+ PMutexInit(&lockPtr->plock);
return &lockPtr->tlock;
}
@@ -713,11 +865,17 @@ TclpFreeAllocMutex(
if (!lockPtr) {
return;
}
- pthread_mutex_destroy(&lockPtr->plock);
+ PMutexDestroy(&lockPtr->plock);
free(lockPtr);
}
void
+TclpInitAllocCache(void)
+{
+ pthread_key_create(&key, NULL);
+}
+
+void
TclpFreeAllocCache(
void *ptr)
{
@@ -730,28 +888,19 @@ TclpFreeAllocCache(
TclFreeAllocCache(ptr);
pthread_setspecific(key, NULL);
- } else if (initialized) {
+ } else {
/*
* Called by TclFinalizeThreadAlloc() during the process
* finalization initiated from Tcl_Finalize()
*/
pthread_key_delete(key);
- initialized = 0;
}
}
void *
TclpGetAllocCache(void)
{
- if (!initialized) {
- pthread_mutex_lock(allocLockPtr);
- if (!initialized) {
- pthread_key_create(&key, NULL);
- initialized = 1;
- }
- pthread_mutex_unlock(allocLockPtr);
- }
return pthread_getspecific(key);
}