Authors: Juraj Hercek <juraj@syncad.com>, Eric Frias <efrias@syncad.com>

Implemented pthread conditions.
This commit is contained in:
Alexandre Julliard 2003-09-23 22:59:44 +00:00
parent 9d481e32a0
commit 3bff2b3c9c
3 changed files with 250 additions and 14 deletions

View File

@ -304,6 +304,230 @@ static int wine_pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
return 0;
}
/***** CONDITIONS *****/
/* The condition code is basically cut-and-pasted from Douglas
* Schmidt's paper:
* "Strategies for Implementing POSIX Condition Variables on Win32",
* at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html and
* http://www.cs.wustl.edu/~schmidt/win32-cv-2.html.
* This paper formed the basis for the condition variable
* impementation used in the ACE library.
*/
/* Possible problems with ACE:
* - unimplemented pthread_mutexattr_init
*/
typedef struct {
/* Number of waiting threads. */
int waiters_count;
/* Serialize access to <waiters_count>. */
CRITICAL_SECTION waiters_count_lock;
/*
* Semaphore used to queue up threads waiting for the condition to
* become signaled.
*/
HANDLE sema;
/*
* An auto-reset event used by the broadcast/signal thread to wait
* for all the waiting thread(s) to wake up and be released from the
* semaphore.
*/
HANDLE waiters_done;
/*
* Keeps track of whether we were broadcasting or signaling. This
* allows us to optimize the code if we're just signaling.
*/
size_t was_broadcast;
} wine_cond_detail;
/* see wine_mutex above for comments */
typedef struct {
wine_cond_detail *cond;
} *wine_cond;
static void wine_cond_real_init(pthread_cond_t *cond)
{
wine_cond_detail *detail = HeapAlloc(GetProcessHeap(), 0, sizeof(wine_cond_detail));
detail->waiters_count = 0;
detail->was_broadcast = 0;
detail->sema = CreateSemaphoreW( NULL, 0, 0x7fffffff, NULL );
detail->waiters_done = CreateEventW( NULL, FALSE, FALSE, NULL );
RtlInitializeCriticalSection (&detail->waiters_count_lock);
if (InterlockedCompareExchangePointer((void**)&(((wine_cond)cond)->cond), detail, NULL) != NULL)
{
/* too late, some other thread already did it */
P_OUTPUT("FIXME:pthread_cond_init:expect troubles...\n");
CloseHandle(detail->sema);
RtlDeleteCriticalSection(&detail->waiters_count_lock);
CloseHandle(detail->waiters_done);
HeapFree(GetProcessHeap(), 0, detail);
}
}
int wine_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
{
/* The same as for wine_pthread_mutex_init, we postpone initialization
until condition is really used.*/
((wine_cond)cond)->cond = NULL;
return 0;
}
int wine_pthread_cond_destroy(pthread_cond_t *cond)
{
wine_cond_detail *detail = ((wine_cond)cond)->cond;
if (!detail) return 0;
CloseHandle(detail->sema);
RtlDeleteCriticalSection(&detail->waiters_count_lock);
CloseHandle(detail->waiters_done);
HeapFree(GetProcessHeap(), 0, detail);
((wine_cond)cond)->cond = NULL;
return 0;
}
int wine_pthread_cond_signal(pthread_cond_t *cond)
{
int have_waiters;
wine_cond_detail *detail;
if ( !((wine_cond)cond)->cond ) wine_cond_real_init(cond);
detail = ((wine_cond)cond)->cond;
RtlEnterCriticalSection (&detail->waiters_count_lock);
have_waiters = detail->waiters_count > 0;
RtlLeaveCriticalSection (&detail->waiters_count_lock);
/* If there aren't any waiters, then this is a no-op. */
if (have_waiters)
ReleaseSemaphore(detail->sema, 1, NULL);
return 0;
}
int wine_pthread_cond_broadcast(pthread_cond_t *cond)
{
int have_waiters = 0;
wine_cond_detail *detail;
if ( !((wine_cond)cond)->cond ) wine_cond_real_init(cond);
detail = ((wine_cond)cond)->cond;
/*
* This is needed to ensure that <waiters_count> and <was_broadcast> are
* consistent relative to each other.
*/
RtlEnterCriticalSection (&detail->waiters_count_lock);
if (detail->waiters_count > 0) {
/*
* We are broadcasting, even if there is just one waiter...
* Record that we are broadcasting, which helps optimize
* <pthread_cond_wait> for the non-broadcast case.
*/
detail->was_broadcast = 1;
have_waiters = 1;
}
if (have_waiters) {
/* Wake up all the waiters atomically. */
ReleaseSemaphore(detail->sema, detail->waiters_count, NULL);
RtlLeaveCriticalSection (&detail->waiters_count_lock);
/* Wait for all the awakened threads to acquire the counting semaphore. */
WaitForSingleObject (detail->waiters_done, INFINITE);
/*
* This assignment is okay, even without the <waiters_count_lock> held
* because no other waiter threads can wake up to access it.
*/
detail->was_broadcast = 0;
}
else
RtlLeaveCriticalSection (&detail->waiters_count_lock);
return 0;
}
int wine_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
wine_cond_detail *detail;
int last_waiter;
if ( !((wine_cond)cond)->cond ) wine_cond_real_init(cond);
detail = ((wine_cond)cond)->cond;
/* Avoid race conditions. */
RtlEnterCriticalSection (&detail->waiters_count_lock);
detail->waiters_count++;
RtlLeaveCriticalSection (&detail->waiters_count_lock);
RtlLeaveCriticalSection ( ((wine_mutex)mutex)->critsect );
WaitForSingleObject(detail->sema, INFINITE);
/* Reacquire lock to avoid race conditions. */
RtlEnterCriticalSection (&detail->waiters_count_lock);
/* We're no longer waiting... */
detail->waiters_count--;
/* Check to see if we're the last waiter after <pthread_cond_broadcast>. */
last_waiter = detail->was_broadcast && detail->waiters_count == 0;
RtlLeaveCriticalSection (&detail->waiters_count_lock);
/*
* If we're the last waiter thread during this particular broadcast
* then let all the other threads proceed.
*/
if (last_waiter) SetEvent(detail->waiters_done);
RtlEnterCriticalSection (((wine_mutex)mutex)->critsect);
return 0;
}
int wine_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{
DWORD ms = abstime->tv_sec * 1000 + abstime->tv_nsec / 1000000;
int last_waiter;
wine_cond_detail *detail;
if ( !((wine_cond)cond)->cond ) wine_cond_real_init(cond);
detail = ((wine_cond)cond)->cond;
/* Avoid race conditions. */
RtlEnterCriticalSection (&detail->waiters_count_lock);
detail->waiters_count++;
RtlLeaveCriticalSection (&detail->waiters_count_lock);
RtlLeaveCriticalSection (((wine_mutex)mutex)->critsect);
WaitForSingleObject (detail->sema, ms);
/* Reacquire lock to avoid race conditions. */
RtlEnterCriticalSection (&detail->waiters_count_lock);
/* We're no longer waiting... */
detail->waiters_count--;
/* Check to see if we're the last waiter after <pthread_cond_broadcast>. */
last_waiter = detail->was_broadcast && detail->waiters_count == 0;
RtlLeaveCriticalSection (&detail->waiters_count_lock);
/*
* If we're the last waiter thread during this particular broadcast
* then let all the other threads proceed.
*/
if (last_waiter) SetEvent (detail->waiters_done);
RtlEnterCriticalSection (((wine_mutex)mutex)->critsect);
return 0;
}
/***** MISC *****/
static pthread_t wine_pthread_self(void)
@ -353,5 +577,11 @@ static const struct wine_pthread_functions functions =
wine_pthread_rwlock_tryrdlock, /* ptr_pthread_rwlock_tryrdlock */
wine_pthread_rwlock_wrlock, /* ptr_pthread_rwlock_wrlock */
wine_pthread_rwlock_trywrlock, /* ptr_pthread_rwlock_trywrlock */
wine_pthread_rwlock_unlock /* ptr_pthread_rwlock_unlock */
wine_pthread_rwlock_unlock, /* ptr_pthread_rwlock_unlock */
wine_pthread_cond_init, /* ptr_pthread_cond_init */
wine_pthread_cond_destroy, /* ptr_pthread_cond_destroy */
wine_pthread_cond_signal, /* ptr_pthread_cond_signal */
wine_pthread_cond_broadcast, /* ptr_pthread_cond_broadcast */
wine_pthread_cond_wait, /* ptr_pthread_cond_wait */
wine_pthread_cond_timedwait /* ptr_pthread_cond_timedwait */
};

View File

@ -56,6 +56,13 @@ struct wine_pthread_functions
int (*ptr_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock);
int (*ptr_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock);
int (*ptr_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock);
int (*ptr_pthread_cond_init)(pthread_cond_t *cond, const pthread_condattr_t *cond_attr);
int (*ptr_pthread_cond_destroy)(pthread_cond_t *cond);
int (*ptr_pthread_cond_signal)(pthread_cond_t *cond);
int (*ptr_pthread_cond_broadcast)(pthread_cond_t *cond);
int (*ptr_pthread_cond_wait)(pthread_cond_t *cond, pthread_mutex_t *mutex);
int (*ptr_pthread_cond_timedwait)(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime);
};
extern void wine_pthread_init_process( const struct wine_pthread_functions *functions );

View File

@ -563,47 +563,46 @@ void __pthread_cleanup_upto(jmp_buf target, char *frame)
}
/***** CONDITIONS *****/
/* not implemented right now */
int __pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
{
P_OUTPUT("FIXME:pthread_cond_init\n");
return 0;
if (!funcs.ptr_pthread_cond_init) return 0;
return funcs.ptr_pthread_cond_init(cond, cond_attr);
}
strong_alias(__pthread_cond_init, pthread_cond_init);
int __pthread_cond_destroy(pthread_cond_t *cond)
{
P_OUTPUT("FIXME:pthread_cond_destroy\n");
return 0;
if (!funcs.ptr_pthread_cond_destroy) return 0;
return funcs.ptr_pthread_cond_destroy(cond);
}
strong_alias(__pthread_cond_destroy, pthread_cond_destroy);
int __pthread_cond_signal(pthread_cond_t *cond)
{
P_OUTPUT("FIXME:pthread_cond_signal\n");
return 0;
if (!funcs.ptr_pthread_cond_signal) return 0;
return funcs.ptr_pthread_cond_signal(cond);
}
strong_alias(__pthread_cond_signal, pthread_cond_signal);
int __pthread_cond_broadcast(pthread_cond_t *cond)
{
P_OUTPUT("FIXME:pthread_cond_broadcast\n");
return 0;
if (!funcs.ptr_pthread_cond_broadcast) return 0;
return funcs.ptr_pthread_cond_broadcast(cond);
}
strong_alias(__pthread_cond_broadcast, pthread_cond_broadcast);
int __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
P_OUTPUT("FIXME:pthread_cond_wait\n");
return 0;
if (!funcs.ptr_pthread_cond_wait) return 0;
return funcs.ptr_pthread_cond_wait(cond, mutex);
}
strong_alias(__pthread_cond_wait, pthread_cond_wait);
int __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
{
P_OUTPUT("FIXME:pthread_cond_timedwait\n");
return 0;
if (!funcs.ptr_pthread_cond_timedwait) return 0;
return funcs.ptr_pthread_cond_timedwait(cond, mutex, abstime);
}
strong_alias(__pthread_cond_timedwait, pthread_cond_timedwait);