diff --git a/dlls/kernel/pthread.c b/dlls/kernel/pthread.c
index 1560f4bd7d4d5ab4b4b9df47cd21fd964996770d..ea6759c574be1e44aac6d117d787010e860087c6 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 <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 */
 };
diff --git a/include/wine/pthread.h b/include/wine/pthread.h
index 1f7ad1fdcd7eb5e4b464e6552a4294e990421736..bd78385158b20ffbbbb6e071dc33ad1fe09ca698 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 66fa86957ce0c13e67d14a2782051f9291dcf4b7..e5e0a003980d74dcb53ba9a5bcd9c5aaddac5e4f 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);