summaryrefslogtreecommitdiff
path: root/mutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'mutex.c')
-rw-r--r--mutex.c565
1 files changed, 428 insertions, 137 deletions
diff --git a/mutex.c b/mutex.c
index 3e4722e..fd09044 100644
--- a/mutex.c
+++ b/mutex.c
@@ -85,6 +85,222 @@ ptw32_mutex_check_need_init(pthread_mutex_t *mutex)
return(result);
}
+
+/*
+ * The following internal versions of *CriticalSection()
+ * include an implementation of TryEnterCriticalSection
+ * for platforms on which that function has not been
+ * provided by Microsoft (eg. W95/98). This allows us
+ * to use critical sections exclusively as the basis
+ * of our implementation of POSIX mutex locks.
+ *
+ * Where TryEnterCriticalSection() is provided by the
+ * platform, these routines act as wrappers with
+ * minimal additional overhead. Otherwise, these
+ * routines manage additional state in order to
+ * properly emulate TryEnterCriticalSection().
+ *
+ * In any case, using of critical sections exclusively
+ * should still be much faster than using Win32 mutex
+ * locks as our POSIX mutex locks.
+ */
+
+static void
+ptw32_InitializeCriticalSection (ptw32_cs_t * csect)
+ /*
+ * ------------------------------------------------------
+ *
+ * PARAMETERS
+ * csect
+ * pointer to an instance of ptw32_cs_t
+ *
+ * DESCRIPTION
+ * Internal implementation of InitializeCriticalSection.
+ *
+ * ------------------------------------------------------
+ */
+{
+ ptw32_cs_t cs = *csect;
+
+ cs->owner = NULL;
+ cs->lock_idx = -1;
+ cs->entered_count = 0;
+ InitializeCriticalSection(&cs->cs);
+ cs->valid = 1;
+}
+
+static void
+ptw32_DeleteCriticalSection (ptw32_cs_t * csect)
+ /*
+ * ------------------------------------------------------
+ *
+ * PARAMETERS
+ * csect
+ * pointer to an instance of ptw32_cs_t
+ *
+ * DESCRIPTION
+ * Internal implementation of DeleteCriticalSection.
+ *
+ * ------------------------------------------------------
+ */
+{
+ ptw32_cs_t cs = *csect;
+
+ cs->valid = 0;
+ DeleteCriticalSection(&cs->cs);
+}
+
+static void
+ptw32_EnterCriticalSection(ptw32_cs_t * csect)
+ /*
+ * ------------------------------------------------------
+ *
+ * PARAMETERS
+ * csect
+ * pointer to an instance of ptw32_cs_t
+ *
+ * DESCRIPTION
+ * Internal implementation of EnterCriticalSection.
+ *
+ * ------------------------------------------------------
+ */
+{
+ ptw32_cs_t cs = *csect;
+
+ if (!cs->valid)
+ {
+ return;
+ }
+
+ if (NULL != ptw32_try_enter_critical_section)
+ {
+ EnterCriticalSection(&cs->cs);
+ }
+ else
+ {
+ while (InterlockedIncrement(&cs->lock_idx) > 0)
+ {
+ InterlockedDecrement(&cs->lock_idx);
+ Sleep(0);
+ }
+ EnterCriticalSection(&cs->cs);
+ cs->entered_count++;
+ cs->owner = pthread_self();
+ InterlockedDecrement(&cs->lock_idx);
+ }
+}
+
+static void
+ptw32_LeaveCriticalSection (ptw32_cs_t * csect)
+ /*
+ * ------------------------------------------------------
+ *
+ * PARAMETERS
+ * csect
+ * pointer to an instance of ptw32_cs_t
+ *
+ * DESCRIPTION
+ * Internal implementation of LeaveCriticalSection.
+ *
+ * ------------------------------------------------------
+ */
+{
+ ptw32_cs_t cs = *csect;
+
+ if (!cs->valid)
+ {
+ return;
+ }
+
+ if (NULL != ptw32_try_enter_critical_section)
+ {
+ LeaveCriticalSection(&cs->cs);
+ }
+ else
+ {
+ while (InterlockedIncrement(&cs->lock_idx) > 0)
+ {
+ InterlockedDecrement(&cs->lock_idx);
+ Sleep(0);
+ }
+
+ LeaveCriticalSection(&cs->cs);
+
+ cs->entered_count--;
+
+ if (cs->entered_count == 0)
+ {
+ cs->owner = NULL;
+ }
+
+ InterlockedDecrement(&cs->lock_idx);
+ }
+}
+
+static BOOL
+ptw32_TryEnterCriticalSection (ptw32_cs_t * csect)
+ /*
+ * ------------------------------------------------------
+ *
+ * PARAMETERS
+ * csect
+ * pointer to an instance of ptw32_cs_t
+ *
+ * DESCRIPTION
+ * Internal implementation of TryEnterCriticalSection.
+ *
+ * RETURNS
+ * FALSE Current thread doesn't own the
+ * lock,
+ * TRUE Current thread owns the lock
+ * (if the current thread already
+ * held the lock then we recursively
+ * enter).
+ * ------------------------------------------------------
+ */
+{
+ ptw32_cs_t cs = *csect;
+ BOOL result = FALSE;
+
+ if (!cs->valid)
+ {
+ return (FALSE);
+ }
+
+ if (NULL != ptw32_try_enter_critical_section)
+ {
+ result = (*ptw32_try_enter_critical_section)(&cs->cs);
+ }
+ else
+ {
+ pthread_t self = pthread_self();
+
+ while (InterlockedIncrement(&cs->lock_idx) > 0)
+ {
+ InterlockedDecrement(&cs->lock_idx);
+ Sleep(0);
+ }
+
+ if (cs->owner == NULL || pthread_equal(cs->owner, self))
+ {
+ /* The semantics of TryEnterCriticalSection
+ * (according to the documentation at MS)
+ * are that the CS is entered recursively
+ * if the thread is the current owner.
+ */
+ EnterCriticalSection(&cs->cs);
+ cs->entered_count++;
+ cs->owner = self;
+ result = TRUE;
+ }
+
+ InterlockedDecrement(&cs->lock_idx);
+ }
+
+ return (result);
+}
+
+
int
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
@@ -104,82 +320,35 @@ pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
goto FAIL0;
}
- mx->mutex = 0;
+ ptw32_InitializeCriticalSection(&mx->cs);
mx->lockCount = 0;
mx->ownerThread = NULL;
- if (attr != NULL
- && *attr != NULL
- && (*attr)->pshared == PTHREAD_PROCESS_SHARED
- )
+ if (attr != NULL && *attr != NULL)
{
- /*
- * Creating mutex that can be shared between
- * processes.
- */
-#if _POSIX_THREAD_PROCESS_SHARED
- /*
- * Not implemented yet.
- */
+ mx->type = (*attr)->type;
-#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet.
+ if ((*attr)->pshared == PTHREAD_PROCESS_SHARED)
+ {
+ /*
+ * Creating mutex that can be shared between
+ * processes.
+ */
+#if _POSIX_THREAD_PROCESS_SHARED
- mx->mutex = CreateMutex(NULL, FALSE, "FIXME FIXME FIXME");
+ /*
+ * Not implemented yet.
+ */
- if (mx->mutex == 0)
- {
- result = EAGAIN;
- }
+#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet.
#else
- result = ENOSYS;
+ result = ENOSYS;
#endif /* _POSIX_THREAD_PROCESS_SHARED */
- }
- else
- {
- if (ptw32_try_enter_critical_section != NULL
- || (attr != NULL
- && *attr != NULL
- && (*attr)->forcecs == 1)
- )
- {
- /*
- * Create a critical section.
- */
- InitializeCriticalSection(&mx->cs);
-
- /*
- * Check that it works ok - since InitializeCriticalSection doesn't
- * return success or failure.
- */
- if ((*ptw32_try_enter_critical_section)(&mx->cs))
- {
- LeaveCriticalSection(&mx->cs);
- }
- else
- {
- DeleteCriticalSection(&mx->cs);
- result = EAGAIN;
- }
- }
- else
- {
- /*
- * Create a mutex that can only be used within the
- * current process
- */
- mx->mutex = CreateMutex (NULL,
- FALSE,
- NULL);
-
- if (mx->mutex == 0)
- {
- result = EAGAIN;
- }
- }
+ }
}
if (result != 0 && mx != NULL)
@@ -200,8 +369,7 @@ pthread_mutex_destroy(pthread_mutex_t *mutex)
int result = 0;
pthread_mutex_t mx;
- if (mutex == NULL
- || *mutex == NULL)
+ if (mutex == NULL || *mutex == NULL)
{
return EINVAL;
}
@@ -213,7 +381,9 @@ pthread_mutex_destroy(pthread_mutex_t *mutex)
{
mx = *mutex;
- if ((result = pthread_mutex_trylock(&mx)) == 0)
+ result = pthread_mutex_trylock(&mx);
+
+ if (result == 0 || result == EDEADLK)
{
/*
* FIXME!!!
@@ -224,20 +394,11 @@ pthread_mutex_destroy(pthread_mutex_t *mutex)
*/
*mutex = NULL;
- pthread_mutex_unlock(&mx);
-
- if (mx->mutex == 0)
- {
- DeleteCriticalSection(&mx->cs);
- }
- else
- {
- result = (CloseHandle (mx->mutex) ? 0 : EINVAL);
- }
+ result = pthread_mutex_unlock(&mx);
if (result == 0)
{
- mx->mutex = 0;
+ ptw32_DeleteCriticalSection(&mx->cs);
free(mx);
}
else
@@ -321,6 +482,9 @@ pthread_mutexattr_init (pthread_mutexattr_t * attr)
result = ENOMEM;
}
+ ma->pshared = PTHREAD_PROCESS_PRIVATE;
+ ma->type = PTHREAD_MUTEX_DEFAULT;
+
*attr = ma;
return (result);
@@ -519,10 +683,130 @@ pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,
int
+pthread_mutexattr_settype (pthread_mutexattr_t * attr,
+ int type)
+ /*
+ * ------------------------------------------------------
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_mutexattr_t
+ *
+ * type
+ * must be one of:
+ *
+ * PTHREAD_MUTEX_DEFAULT
+ *
+ * PTHREAD_MUTEX_NORMAL
+ *
+ * PTHREAD_MUTEX_ERRORCHECK
+ *
+ * PTHREAD_MUTEX_RECURSIVE
+ *
+ * DESCRIPTION
+ * The pthread_mutexattr_gettype() and
+ * pthread_mutexattr_settype() functions respectively get and
+ * set the mutex type attribute. This attribute is set in the
+ * type parameter to these functions. The default value of the
+ * type attribute is PTHREAD_MUTEX_DEFAULT.
+ *
+ * The type of mutex is contained in the type attribute of the
+ * mutex attributes. Valid mutex types include:
+ *
+ * PTHREAD_MUTEX_NORMAL
+ * This type of mutex does not detect deadlock. A
+ * thread attempting to relock this mutex without
+ * first unlocking it will deadlock. Attempting to
+ * unlock a mutex locked by a different thread
+ * results in undefined behavior. Attempting to
+ * unlock an unlocked mutex results in undefined
+ * behavior.
+ *
+ * PTHREAD_MUTEX_ERRORCHECK
+ * This type of mutex provides error checking. A
+ * thread attempting to relock this mutex without
+ * first unlocking it will return with an error. A
+ * thread attempting to unlock a mutex which another
+ * thread has locked will return with an error. A
+ * thread attempting to unlock an unlocked mutex will
+ * return with an error.
+ *
+ * PTHREAD_MUTEX_RECURSIVE
+ * A thread attempting to relock this mutex without
+ * first unlocking it will succeed in locking the
+ * mutex. The relocking deadlock which can occur with
+ * mutexes of type PTHREAD_MUTEX_NORMAL cannot occur
+ * with this type of mutex. Multiple locks of this
+ * mutex require the same number of unlocks to
+ * release the mutex before another thread can
+ * acquire the mutex. A thread attempting to unlock a
+ * mutex which another thread has locked will return
+ * with an error. A thread attempting to unlock an
+ * unlocked mutex will return with an error. This
+ * type of mutex is only supported for mutexes whose
+ * process shared attribute is
+ * PTHREAD_PROCESS_PRIVATE.
+ *
+ * RESULTS
+ * 0 successfully set attribute,
+ * EINVAL 'attr' or 'type' is invalid,
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result = 0;
+
+ if ((attr != NULL && *attr != NULL))
+ {
+ switch (type)
+ {
+ case PTHREAD_MUTEX_DEFAULT:
+ case PTHREAD_MUTEX_NORMAL:
+ case PTHREAD_MUTEX_ERRORCHECK:
+ case PTHREAD_MUTEX_RECURSIVE:
+ (*attr)->type = type;
+ break;
+ default:
+ result = EINVAL;
+ break;
+ }
+ }
+ else
+ {
+ result = EINVAL;
+ }
+
+ return (result);
+
+} /* pthread_mutexattr_settype */
+
+
+int
+pthread_mutexattr_gettype (pthread_mutexattr_t * attr,
+ int type)
+{
+ int result = 0;
+
+ if ((attr != NULL && *attr != NULL))
+ {
+ result = (*attr)->type;
+ }
+ else
+ {
+ result = EINVAL;
+ }
+
+ return (result);
+}
+
+
+int
pthread_mutex_lock(pthread_mutex_t *mutex)
{
int result = 0;
pthread_mutex_t mx;
+ pthread_t self;
+
if (mutex == NULL || *mutex == NULL)
{
@@ -541,25 +825,49 @@ pthread_mutex_lock(pthread_mutex_t *mutex)
}
mx = *mutex;
+ self = pthread_self();
- if (result == 0)
+ ptw32_EnterCriticalSection(&mx->cs);
+
+ switch (mx->type)
{
- if (mx->mutex == 0)
- {
- EnterCriticalSection(&mx->cs);
- }
- else
- {
- result = (WaitForSingleObject(mx->mutex, INFINITE)
- == WAIT_OBJECT_0)
- ? 0
- : EINVAL;
- }
+ case PTHREAD_MUTEX_NORMAL:
+ if (pthread_equal(mx->ownerThread, self))
+ {
+ ptw32_LeaveCriticalSection(&mx->cs);
+ /*
+ * Pretend to be deadlocked but release the
+ * mutex if we are canceled.
+ */
+ pthread_cleanup_push(pthread_mutex_unlock, (void *) mutex);
+ while (TRUE)
+ {
+ Sleep(0);
+ }
+ pthread_cleanup_pop(1);
+ }
+ break;
+ case PTHREAD_MUTEX_DEFAULT:
+ case PTHREAD_MUTEX_ERRORCHECK:
+ if (pthread_equal(mx->ownerThread, self))
+ {
+ ptw32_LeaveCriticalSection(&mx->cs);
+ result = EDEADLK;
+ }
+ break;
+ case PTHREAD_MUTEX_RECURSIVE:
+ /*
+ * Nothing more to do.
+ */
+ break;
+ default:
+ result = EINVAL;
+ break;
}
if (result == 0)
{
- mx->ownerThread = pthread_self();
+ mx->ownerThread = self;
mx->lockCount++;
}
@@ -586,39 +894,16 @@ pthread_mutex_unlock(pthread_mutex_t *mutex)
*/
if (mx != (pthread_mutex_t) PTW32_OBJECT_AUTO_INIT)
{
- pthread_t self = pthread_self();
-
- if (pthread_equal(mx->ownerThread, self))
+ if (pthread_equal(mx->ownerThread, pthread_self()))
{
- int oldCount = mx->lockCount;
- pthread_t oldOwner = mx->ownerThread;
-
- if (mx->lockCount > 0)
- {
- mx->lockCount--;
- }
+ mx->lockCount--;
if (mx->lockCount == 0)
{
mx->ownerThread = NULL;
}
- if (mx->mutex == 0)
- {
- LeaveCriticalSection(&mx->cs);
- }
- else
- {
- if (!ReleaseMutex(mx->mutex))
- {
- result = EINVAL;
- /*
- * Put things back the way they were.
- */
- mx->lockCount = oldCount;
- mx->ownerThread = oldOwner;
- }
- }
+ ptw32_LeaveCriticalSection(&mx->cs);
}
else
{
@@ -638,6 +923,7 @@ pthread_mutex_trylock(pthread_mutex_t *mutex)
{
int result = 0;
pthread_mutex_t mx;
+ pthread_t self;
if (mutex == NULL || *mutex == NULL)
{
@@ -656,36 +942,41 @@ pthread_mutex_trylock(pthread_mutex_t *mutex)
}
mx = *mutex;
+ self = pthread_self();
if (result == 0)
{
- if (mx->mutex == 0)
- {
- if ((*ptw32_try_enter_critical_section)(&mx->cs) != TRUE)
- {
- result = EBUSY;
- }
- }
+ /*
+ * TryEnterCriticalSection is a little different to
+ * the POSIX trylock semantics. Trylock returns
+ * EBUSY even if the calling thread already owns
+ * the mutex - it doesn't lock it recursively, even
+ * if the mutex type is PTHREAD_MUTEX_RECURSIVE.
+ */
+ if (ptw32_TryEnterCriticalSection(&mx->cs))
+ {
+ /*
+ * We now own the lock, but check that we don't
+ * already own the mutex.
+ */
+ if (pthread_equal(mx->ownerThread, self))
+ {
+ ptw32_LeaveCriticalSection(&mx->cs);
+ result = EBUSY;
+ }
+ }
else
- {
- DWORD status;
-
- status = WaitForSingleObject (mx->mutex, 0);
-
- if (status != WAIT_OBJECT_0)
- {
- result = ((status == WAIT_TIMEOUT)
- ? EBUSY
- : EINVAL);
- }
- }
+ {
+ result = EBUSY;
+ }
}
if (result == 0)
{
- mx->ownerThread = pthread_self();
+ mx->ownerThread = self;
mx->lockCount++;
}
- return(result);
+ return (result);
}
+