summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixThrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixThrd.c')
-rw-r--r--unix/tclUnixThrd.c241
1 files changed, 202 insertions, 39 deletions
diff --git a/unix/tclUnixThrd.c b/unix/tclUnixThrd.c
index d6c420a..4a5836b 100644
--- a/unix/tclUnixThrd.c
+++ b/unix/tclUnixThrd.c
@@ -14,6 +14,159 @@
#include "tclInt.h"
#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 */
+
+/*
+ * 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 */
/*
* masterLock is used to serialize creation of mutexes, condition variables,
@@ -35,8 +188,15 @@ static pthread_mutex_t initLock = PTHREAD_MUTEX_INITIALIZER;
* obvious reasons, cannot use any dyamically 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;
+
+static void
+allocLockInit(void)
+{
+ PMutexInit(&allocLock);
+}
+static PMutex *allocLockPtr = &allocLock;
#endif /* TCL_THREADS */
@@ -101,17 +261,17 @@ TclpThreadCreate(
}
#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
- if (! (flags & TCL_THREAD_JOINABLE)) {
- pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+ if (!(flags & TCL_THREAD_JOINABLE)) {
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
}
if (pthread_create(&theThread, &attr,
- (void * (*)(void *))proc, (void *)clientData) &&
+ (void * (*)(void *)) proc, (void *) clientData) &&
pthread_create(&theThread, NULL,
- (void * (*)(void *))proc, (void *)clientData)) {
+ (void * (*)(void *)) proc, (void *) clientData)) {
result = TCL_ERROR;
} else {
- *idPtr = (Tcl_ThreadId)theThread;
+ *idPtr = (Tcl_ThreadId) theThread;
result = TCL_OK;
}
pthread_attr_destroy(&attr);
@@ -323,7 +483,6 @@ TclpMasterLock(void)
pthread_mutex_lock(&masterLock);
#endif
}
-
/*
*----------------------------------------------------------------------
@@ -373,7 +532,9 @@ Tcl_Mutex *
Tcl_GetAllocMutex(void)
{
#if TCL_THREADS
- pthread_mutex_t **allocLockPtrPtr = &allocLockPtr;
+ PMutex **allocLockPtrPtr = &allocLockPtr;
+
+ pthread_once(&allocLockInitOnce, allocLockInit);
return (Tcl_Mutex *) allocLockPtrPtr;
#else
return NULL;
@@ -405,9 +566,9 @@ 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) {
pthread_mutex_lock(&masterLock);
@@ -416,15 +577,15 @@ Tcl_MutexLock(
* Double inside master lock check to avoid a race condition.
*/
- pmutexPtr = ckalloc(sizeof(pthread_mutex_t));
- pthread_mutex_init(pmutexPtr, NULL);
- *mutexPtr = (Tcl_Mutex)pmutexPtr;
+ pmutexPtr = ckalloc(sizeof(PMutex));
+ PMutexInit(pmutexPtr);
+ *mutexPtr = (Tcl_Mutex) pmutexPtr;
TclRememberMutex(mutexPtr);
}
pthread_mutex_unlock(&masterLock);
}
- pmutexPtr = *((pthread_mutex_t **)mutexPtr);
- pthread_mutex_lock(pmutexPtr);
+ pmutexPtr = *((PMutex **) mutexPtr);
+ PMutexLock(pmutexPtr);
}
/*
@@ -446,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);
}
/*
@@ -476,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;
}
@@ -510,11 +671,11 @@ 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) {
@@ -533,10 +694,10 @@ Tcl_ConditionWait(
}
pthread_mutex_unlock(&masterLock);
}
- pmutexPtr = *((pthread_mutex_t **)mutexPtr);
- pcondPtr = *((pthread_cond_t **)condPtr);
+ pmutexPtr = *((PMutex **) mutexPtr);
+ pcondPtr = *((pthread_cond_t **) condPtr);
if (timePtr == NULL) {
- pthread_cond_wait(pcondPtr, pmutexPtr);
+ PCondWait(pcondPtr, pmutexPtr);
} else {
Tcl_Time now;
@@ -549,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);
}
}
@@ -576,12 +737,13 @@ void
Tcl_ConditionNotify(
Tcl_Condition *condPtr)
{
- pthread_cond_t *pcondPtr = *((pthread_cond_t **)condPtr);
+ pthread_cond_t *pcondPtr = *((pthread_cond_t **) condPtr);
+
if (pcondPtr != NULL) {
pthread_cond_broadcast(pcondPtr);
} else {
/*
- * Noone has used the condition variable, so there are no waiters.
+ * No-one has used the condition variable, so there are no waiters.
*/
}
}
@@ -609,7 +771,7 @@ void
TclpFinalizeCondition(
Tcl_Condition *condPtr)
{
- pthread_cond_t *pcondPtr = *(pthread_cond_t **)condPtr;
+ pthread_cond_t *pcondPtr = *(pthread_cond_t **) condPtr;
if (pcondPtr != NULL) {
pthread_cond_destroy(pcondPtr);
@@ -627,22 +789,22 @@ static pthread_key_t key;
typedef struct {
Tcl_Mutex tlock;
- pthread_mutex_t plock;
-} allocMutex;
+ PMutex plock;
+} AllocMutex;
Tcl_Mutex *
TclpNewAllocMutex(void)
{
- allocMutex *lockPtr;
- register pthread_mutex_t *plockPtr;
+ AllocMutex *lockPtr;
+ register PMutex *plockPtr;
- lockPtr = malloc(sizeof(allocMutex));
+ lockPtr = malloc(sizeof(AllocMutex));
if (lockPtr == NULL) {
Tcl_Panic("could not allocate lock");
}
plockPtr = &lockPtr->plock;
lockPtr->tlock = (Tcl_Mutex) plockPtr;
- pthread_mutex_init(&lockPtr->plock, NULL);
+ PMutexInit(&lockPtr->plock);
return &lockPtr->tlock;
}
@@ -650,11 +812,12 @@ void
TclpFreeAllocMutex(
Tcl_Mutex *mutex) /* The alloc mutex to free. */
{
- allocMutex* lockPtr = (allocMutex*) mutex;
+ AllocMutex *lockPtr = (AllocMutex *) mutex;
+
if (!lockPtr) {
return;
}
- pthread_mutex_destroy(&lockPtr->plock);
+ PMutexDestroy(&lockPtr->plock);
free(lockPtr);
}
@@ -706,7 +869,7 @@ TclpThreadCreateKey(void)
{
pthread_key_t *ptkeyPtr;
- ptkeyPtr = TclpSysAlloc(sizeof *ptkeyPtr, 0);
+ ptkeyPtr = TclpSysAlloc(sizeof(pthread_key_t), 0);
if (NULL == ptkeyPtr) {
Tcl_Panic("unable to allocate thread key!");
}