diff --git a/dlls/kernel/pthread.c b/dlls/kernel/pthread.c index 1560f4bd7d4..ea6759c574b 100644 --- a/dlls/kernel/pthread.c +++ b/dlls/kernel/pthread.c @@ -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 . */ + 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 and 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 + * 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 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 . */ + 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 . */ + 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 */ }; diff --git a/include/wine/pthread.h b/include/wine/pthread.h index 1f7ad1fdcd7..bd78385158b 100644 --- a/include/wine/pthread.h +++ b/include/wine/pthread.h @@ -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 ); diff --git a/scheduler/pthread.c b/scheduler/pthread.c index 66fa86957ce..e5e0a003980 100644 --- a/scheduler/pthread.c +++ b/scheduler/pthread.c @@ -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);