int pthread_cond_init(pthread_cond_t *cond, const void *unused)
{
cond->waiters = 0;
+ cond->was_broadcast = 0;
+ InitializeCriticalSection(&cond->waiters_lock);
cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
if (!cond->sema)
die("CreateSemaphore() failed");
+
+ cond->continue_broadcast = CreateEvent(NULL, /* security */
+ FALSE, /* auto-reset */
+ FALSE, /* not signaled */
+ NULL); /* name */
+ if (!cond->continue_broadcast)
+ die("CreateEvent() failed");
+
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
CloseHandle(cond->sema);
- cond->sema = NULL;
-
+ CloseHandle(cond->continue_broadcast);
+ DeleteCriticalSection(&cond->waiters_lock);
return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
{
- InterlockedIncrement(&cond->waiters);
+ int last_waiter;
+
+ EnterCriticalSection(&cond->waiters_lock);
+ cond->waiters++;
+ LeaveCriticalSection(&cond->waiters_lock);
/*
* Unlock external mutex and wait for signal.
/* let's wait - ignore return value */
WaitForSingleObject(cond->sema, INFINITE);
- /* we're done waiting, so make sure we decrease waiters count */
- InterlockedDecrement(&cond->waiters);
-
+ /*
+ * Decrease waiters count. If we are the last waiter, then we must
+ * notify the broadcasting thread that it can continue.
+ * But if we continued due to cond_signal, we do not have to do that
+ * because the signaling thread knows that only one waiter continued.
+ */
+ EnterCriticalSection(&cond->waiters_lock);
+ cond->waiters--;
+ last_waiter = cond->was_broadcast && cond->waiters == 0;
+ LeaveCriticalSection(&cond->waiters_lock);
+
+ if (last_waiter) {
+ /*
+ * cond_broadcast was issued while mutex was held. This means
+ * that all other waiters have continued, but are contending
+ * for the mutex at the end of this function because the
+ * broadcasting thread did not leave cond_broadcast, yet.
+ * (This is so that it can be sure that each waiter has
+ * consumed exactly one slice of the semaphor.)
+ * The last waiter must tell the broadcasting thread that it
+ * can go on.
+ */
+ SetEvent(cond->continue_broadcast);
+ /*
+ * Now we go on to contend with all other waiters for
+ * the mutex. Auf in den Kampf!
+ */
+ }
/* lock external mutex again */
EnterCriticalSection(mutex);
return 0;
}
+/*
+ * IMPORTANT: This implementation requires that pthread_cond_signal
+ * is called while the mutex is held that is used in the corresponding
+ * pthread_cond_wait calls!
+ */
int pthread_cond_signal(pthread_cond_t *cond)
{
- /*
- * Access to waiters count is atomic; see "Interlocked Variable Access"
- * http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
- */
- int have_waiters = cond->waiters > 0;
+ int have_waiters;
+
+ EnterCriticalSection(&cond->waiters_lock);
+ have_waiters = cond->waiters > 0;
+ LeaveCriticalSection(&cond->waiters_lock);
/*
* Signal only when there are waiters
else
return 0;
}
+
+/*
+ * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
+ * is called while the mutex is held that is used in the corresponding
+ * pthread_cond_wait calls!
+ */
+int pthread_cond_broadcast(pthread_cond_t *cond)
+{
+ EnterCriticalSection(&cond->waiters_lock);
+
+ if ((cond->was_broadcast = cond->waiters > 0)) {
+ /* wake up all waiters */
+ ReleaseSemaphore(cond->sema, cond->waiters, NULL);
+ LeaveCriticalSection(&cond->waiters_lock);
+ /*
+ * At this point all waiters continue. Each one takes its
+ * slice of the semaphor. Now it's our turn to wait: Since
+ * the external mutex is held, no thread can leave cond_wait,
+ * yet. For this reason, we can be sure that no thread gets
+ * a chance to eat *more* than one slice. OTOH, it means
+ * that the last waiter must send us a wake-up.
+ */
+ WaitForSingleObject(cond->continue_broadcast, INFINITE);
+ /*
+ * Since the external mutex is held, no thread can enter
+ * cond_wait, and, hence, it is safe to reset this flag
+ * without cond->waiters_lock held.
+ */
+ cond->was_broadcast = 0;
+ } else {
+ LeaveCriticalSection(&cond->waiters_lock);
+ }
+ return 0;
+}