Skip to content

Commit

Permalink
Merge pull request #1214 from rongjiecomputer/master
Browse files Browse the repository at this point in the history
Implement pthread_cond_t with Win32 CONDITION_VARIABLE
  • Loading branch information
dscho authored Aug 15, 2017
2 parents 70c1ff8 + e426067 commit eead5ec
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 159 deletions.
138 changes: 0 additions & 138 deletions compat/win32/pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,141 +56,3 @@ pthread_t pthread_self(void)
t.tid = GetCurrentThreadId();
return t;
}

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);
CloseHandle(cond->continue_broadcast);
DeleteCriticalSection(&cond->waiters_lock);
return 0;
}

int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
{
int last_waiter;

EnterCriticalSection(&cond->waiters_lock);
cond->waiters++;
LeaveCriticalSection(&cond->waiters_lock);

/*
* Unlock external mutex and wait for signal.
* NOTE: we've held mutex locked long enough to increment
* waiters count above, so there's no problem with
* leaving mutex unlocked before we wait on semaphore.
*/
LeaveCriticalSection(mutex);

/* let's wait - ignore return value */
WaitForSingleObject(cond->sema, INFINITE);

/*
* 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)
{
int have_waiters;

EnterCriticalSection(&cond->waiters_lock);
have_waiters = cond->waiters > 0;
LeaveCriticalSection(&cond->waiters_lock);

/*
* Signal only when there are waiters
*/
if (have_waiters)
return ReleaseSemaphore(cond->sema, 1, NULL) ?
0 : err_win_to_posix(GetLastError());
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;
}
39 changes: 18 additions & 21 deletions compat/win32/pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,24 @@ typedef int pthread_mutexattr_t;
#define pthread_mutexattr_settype(a, t) 0
#define PTHREAD_MUTEX_RECURSIVE 0

/*
* Implement simple condition variable for Windows threads, based on ACE
* implementation.
*
* See original implementation: http://bit.ly/1vkDjo
* ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
* See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
*/
typedef struct {
LONG waiters;
int was_broadcast;
CRITICAL_SECTION waiters_lock;
HANDLE sema;
HANDLE continue_broadcast;
} pthread_cond_t;

extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
extern int pthread_cond_destroy(pthread_cond_t *cond);
extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
extern int pthread_cond_signal(pthread_cond_t *cond);
extern int pthread_cond_broadcast(pthread_cond_t *cond);
#define pthread_cond_t CONDITION_VARIABLE

WINBASEAPI VOID WINAPI
InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
WINBASEAPI VOID WINAPI
WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
WINBASEAPI VOID WINAPI
WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable);
WINBASEAPI WINBOOL WINAPI
SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable,
PCRITICAL_SECTION CriticalSection,
DWORD dwMilliseconds);

#define pthread_cond_init(a,b) InitializeConditionVariable((a))
#define pthread_cond_destroy(a) do {} while (0)
#define pthread_cond_wait(a,b) return_0(SleepConditionVariableCS((a), (b), INFINITE))
#define pthread_cond_signal WakeConditionVariable
#define pthread_cond_broadcast WakeAllConditionVariable

/*
* Simple thread creation implementation using pthread API
Expand Down

0 comments on commit eead5ec

Please sign in to comment.