summaryrefslogtreecommitdiff
path: root/condvar.c
diff options
context:
space:
mode:
Diffstat (limited to 'condvar.c')
-rw-r--r--condvar.c231
1 files changed, 66 insertions, 165 deletions
diff --git a/condvar.c b/condvar.c
index 4b41a23..2413b02 100644
--- a/condvar.c
+++ b/condvar.c
@@ -65,15 +65,6 @@ _cond_check_need_init(pthread_cond_t *cond)
{
result = pthread_cond_init(cond, NULL);
}
- else if (*cond == NULL)
- {
- /*
- * The cv has been destroyed while we were waiting to
- * initialise it, so the operation that caused the
- * auto-initialisation should fail.
- */
- result = EINVAL;
- }
LeaveCriticalSection(&_pthread_cond_test_init_lock);
@@ -464,126 +455,27 @@ pthread_cond_destroy (pthread_cond_t * cond)
return EINVAL;
}
- if (*cond != (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT)
- {
- cv = *cond;
-
- if (pthread_mutex_lock(&(cv->waitersLock)) != 0)
- {
- return EINVAL;
- }
+ cv = *cond;
+ if (cv != (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT)
+ {
if (cv->waiters > 0)
{
- (void) pthread_mutex_unlock(&(cv->waitersLock));
return EBUSY;
}
(void) sem_destroy (&(cv->sema));
- (void) CloseHandle (cv->waitersDone);
- (void) pthread_mutex_unlock(&(cv->waitersLock));
(void) pthread_mutex_destroy (&(cv->waitersLock));
+ (void) CloseHandle (cv->waitersDone);
free(cv);
- *cond = NULL;
}
- else
- {
- /*
- * See notes in _cond_check_need_init() above also.
- */
- EnterCriticalSection(&_pthread_cond_test_init_lock);
- /*
- * Check again.
- */
- if (*cond == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT)
- {
- /*
- * This is all we need to do to destroy a statically
- * initialised cond that has not yet been used (initialised).
- * If we get to here, another thread
- * waiting to initialise this cond will get an EINVAL.
- */
- *cond = NULL;
- }
- else
- {
- /*
- * The cv has been initialised while we were waiting
- * so assume it's in use.
- */
- result = EBUSY;
- }
-
- LeaveCriticalSection(&_pthread_cond_test_init_lock);
- }
+ *cond = NULL;
return (result);
}
-/*
- * Arguments for cond_wait_cleanup, since we can only pass a
- * single void * to it.
- */
-typedef struct {
- pthread_mutex_t * mutexPtr;
- pthread_cond_t cv;
- int * resultPtr;
-} cond_wait_cleanup_args_t;
-
-static void
-cond_wait_cleanup(void * args)
-{
- cond_wait_cleanup_args_t * cleanup_args = (cond_wait_cleanup_args_t *) args;
- pthread_mutex_t * mutexPtr = cleanup_args->mutexPtr;
- pthread_cond_t cv = cleanup_args->cv;
- int * resultPtr = cleanup_args->resultPtr;
- int lock_result;
- int lastWaiter;
-
- if ((lock_result = pthread_mutex_lock (&(cv->waitersLock))) == 0)
- {
- /*
- * The waiter is responsible for decrementing
- * its count, protected by an internal mutex.
- */
-
- cv->waiters--;
-
- lastWaiter = cv->wasBroadcast && (cv->waiters == 0);
-
- if (lastWaiter)
- {
- cv->wasBroadcast = FALSE;
- }
-
- lock_result = pthread_mutex_unlock (&(cv->waitersLock));
- }
-
- if ((*resultPtr == 0 || *resultPtr == ETIMEDOUT) && lock_result == 0)
- {
- if (lastWaiter)
- {
- /*
- * If we are the last waiter on this broadcast
- * let the thread doing the broadcast proceed
- */
- if (!SetEvent (cv->waitersDone))
- {
- *resultPtr = EINVAL;
- }
- }
- }
-
- /*
- * We must always regain the external mutex, even when
- * errors occur, because that's the guarantee that we give
- * to our callers
- */
- (void) pthread_mutex_lock (mutexPtr);
-}
-
static int
cond_timedwait (pthread_cond_t * cond,
pthread_mutex_t * mutex,
@@ -593,7 +485,6 @@ cond_timedwait (pthread_cond_t * cond,
int internal_result = 0;
int lastWaiter = FALSE;
pthread_cond_t cv;
- cond_wait_cleanup_args_t cleanup_args;
if (cond == NULL || *cond == NULL)
{
@@ -619,22 +510,10 @@ cond_timedwait (pthread_cond_t * cond,
cv = *cond;
/*
- * It's not OK to increment cond->waiters while the caller locked 'mutex',
- * there may be other threads just waking up (with 'mutex' unlocked)
- * and cv->... data is not protected.
+ * OK to increment cond->waiters because the caller locked 'mutex'
*/
- if (pthread_mutex_lock(&(cv->waitersLock)) != 0)
- {
- return EINVAL;
- }
-
cv->waiters++;
- if (pthread_mutex_unlock(&(cv->waitersLock)) != 0)
- {
- return EINVAL;
- }
-
/*
* We keep the lock held just long enough to increment the count of
* waiters by one (above).
@@ -642,41 +521,67 @@ cond_timedwait (pthread_cond_t * cond,
* call to sem_wait since that will deadlock other calls
* to pthread_cond_signal
*/
- cleanup_args.mutexPtr = mutex;
- cleanup_args.cv = cv;
- cleanup_args.resultPtr = &result;
-
- pthread_cleanup_push (cond_wait_cleanup, (void *) &cleanup_args);
-
if ((result = pthread_mutex_unlock (mutex)) == 0)
{
/*
* Wait to be awakened by
* pthread_cond_signal, or
* pthread_cond_broadcast
- * timeout
*
* Note:
- * _pthread_sem_timedwait is a cancelation point,
+ * _pthread_sem_timedwait is a cancellation point,
* hence providing the
- * mechanism for making pthread_cond_wait a cancelation
+ * mechanism for making pthread_cond_wait a cancellation
* point. We use the cleanup mechanism to ensure we
- * re-lock the mutex and decrement the waiters count
- * if we are canceled.
+ * re-lock the mutex if we are cancelled.
*/
+ pthread_cleanup_push (pthread_mutex_lock, mutex);
+
if (_pthread_sem_timedwait (&(cv->sema), abstime) == -1)
{
result = errno;
}
+
+ pthread_cleanup_pop (0);
}
- pthread_cleanup_pop (1);
+ if ((internal_result = pthread_mutex_lock (&(cv->waitersLock))) == 0)
+ {
+ /*
+ * By making the waiter responsible for decrementing
+ * its count we don't have to worry about having an internal
+ * mutex.
+ */
+ cv->waiters--;
+
+ lastWaiter = cv->wasBroadcast && (cv->waiters == 0);
+
+ internal_result = pthread_mutex_unlock (&(cv->waitersLock));
+ }
+
+ if (result == 0 && internal_result == 0)
+ {
+ if (lastWaiter)
+ {
+ /*
+ * If we are the last waiter on this broadcast
+ * let the thread doing the broadcast proceed
+ */
+ if (!SetEvent (cv->waitersDone))
+ {
+ result = EINVAL;
+ }
+ }
+ }
/*
- * "result" can be modified by the cleanup handler.
- * Specifically, if we are the last waiting thread and failed
- * to notify the broadcast thread to proceed.
+ * We must always regain the external mutex, even when
+ * errors occur because that's the guarantee that we give
+ * to our callers
*/
+ (void) pthread_mutex_lock (mutex);
+
+
return (result);
} /* cond_timedwait */
@@ -855,7 +760,6 @@ pthread_cond_signal (pthread_cond_t * cond)
/*
* No-op if the CV is static and hasn't been initialised yet.
- * Assuming that race conditions are harmless.
*/
if (cv == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT)
{
@@ -864,7 +768,6 @@ pthread_cond_signal (pthread_cond_t * cond)
/*
* If there aren't any waiters, then this is a no-op.
- * Assuming that race conditions are harmless.
*/
if (cv->waiters > 0)
{
@@ -914,7 +817,6 @@ pthread_cond_broadcast (pthread_cond_t * cond)
*/
{
int result = 0;
- int wereWaiters = FALSE;
pthread_cond_t cv;
if (cond == NULL || *cond == NULL)
@@ -926,41 +828,40 @@ pthread_cond_broadcast (pthread_cond_t * cond)
/*
* No-op if the CV is static and hasn't been initialised yet.
- * Assuming that any race condition is harmless.
*/
if (cv == (pthread_cond_t) _PTHREAD_OBJECT_AUTO_INIT)
{
return 0;
}
- if (pthread_mutex_lock(&(cv->waitersLock)) == EINVAL)
- {
- return EINVAL;
- }
-
cv->wasBroadcast = TRUE;
- wereWaiters = (cv->waiters > 0);
- if (wereWaiters)
- {
- /*
- * Wake up all waiters
- */
- result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL )
- ? 0
- : EINVAL );
- }
+ /*
+ * Wake up all waiters
+ */
+
+#ifdef NEED_SEM
+
+ result = (_pthread_increase_semaphore( &cv->sema, cv->waiters )
+ ? 0
+ : EINVAL);
+
+#else /* NEED_SEM */
+
+ result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL )
+ ? 0
+ : EINVAL);
- (void) pthread_mutex_unlock(&(cv->waitersLock));
+#endif /* NEED_SEM */
- if (wereWaiters && result == 0)
+ if (cv->waiters > 0 && result == 0)
{
/*
* Wait for all the awakened threads to acquire their part of
* the counting semaphore
*/
- if (WaitForSingleObject (cv->waitersDone, INFINITE)
- == WAIT_OBJECT_0)
+ if (WaitForSingleObject (cv->waitersDone, INFINITE) ==
+ WAIT_OBJECT_0)
{
result = 0;
}