#include <pthread.h>

void pthread_attr_init( pthread_attr_t *  pthread_attr )
{
  // nothing to do...
}

void pthread_attr_setdetachstate( pthread_attr_t *  pthread_attr,
                                  int               detach_state )
{
  // nothing to do...
}

int pthread_create( pthread_t *             pthread,
                    pthread_attr_t *        pthread_attr,
                    LPTHREAD_START_ROUTINE  thread_entry_point,
                    void *                  thread_entry_point_arg )
{
  HANDLE      result;
  DWORD       thread_id;

  result= CreateThread( NULL, 0, thread_entry_point, thread_entry_point_arg, 0, &thread_id );

  if (result == NULL)
    return 69;

  *pthread= result;

  return 0;
}

int pthread_key_create( pthread_key_t *  key,
                        void *           initial_value )
{
  const DWORD      result= TlsAlloc();

  if (result == TLS_OUT_OF_INDEXES)
    return 69;

  *key= result;

  return (TlsSetValue( *key, initial_value ) ? 0 : 69);
}

void * pthread_getspecific( pthread_key_t  key )
{
  return TlsGetValue( key );
}

int pthread_setspecific( pthread_key_t  key,
                         void *         value )
{
  return TlsSetValue( key, value );
}

int pthread_mutex_init( pthread_mutex_t *  mutex,
                        int                arg )
{
  return ((*mutex= CreateMutex( NULL, FALSE, NULL )) == NULL);
}

void pthread_mutex_lock( pthread_mutex_t *  mutex )
{
  WaitForSingleObject( *mutex, INFINITE );
}

void pthread_mutex_unlock( pthread_mutex_t *  mutex )
{
  ReleaseMutex( *mutex );
}

int pthread_cond_init( pthread_cond_t *            cv,
                       const pthread_condattr_t *  condattr )
{
  cv->waiters_count_ = 0;
  cv->was_broadcast_ = 0;
  cv->sema_= CreateSemaphore (NULL,       // no security
                                0,          // initially 0
                                0x7fffffff, // max count
                                NULL);      // unnamed 
  InitializeCriticalSection (&cv->waiters_count_lock_);
  cv->waiters_done_ = CreateEvent (NULL,  // no security
                                   FALSE, // auto-reset
                                   FALSE, // non-signaled initially
                                   NULL); // unnamed

  return 0;
}

int pthread_cond_wait( pthread_cond_t *   cv, 
                       pthread_mutex_t *  external_mutex )
{
  int         last_waiter;

  // Avoid race conditions.
  EnterCriticalSection( &cv->waiters_count_lock_ );
  cv->waiters_count_++;
  LeaveCriticalSection( &cv->waiters_count_lock_ );

  // This call atomically releases the mutex and waits on the
  // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
  // are called by another thread.
  SignalObjectAndWait( *external_mutex, cv->sema_, INFINITE, FALSE );

  // Reacquire lock to avoid race conditions.
  EnterCriticalSection( &cv->waiters_count_lock_ );

  // We're no longer waiting...
  cv->waiters_count_--;

  // Check to see if we're the last waiter after <pthread_cond_broadcast>.
  last_waiter= (cv->was_broadcast_ && (cv->waiters_count_ == 0));

  LeaveCriticalSection( &cv->waiters_count_lock_ );

  // If we're the last waiter thread during this particular broadcast
  // then let all the other threads proceed.
  if (last_waiter)
    // This call atomically signals the <waiters_done_> event and waits until
    // it can acquire the <external_mutex>.  This is required to ensure fairness. 
    SignalObjectAndWait( cv->waiters_done_, *external_mutex, INFINITE, FALSE );
  else
    // Always regain the external mutex since that's the guarantee we
    // give to our callers. 
    WaitForSingleObject( *external_mutex, INFINITE );

  return 0;
}

int pthread_cond_signal( pthread_cond_t *  cv )
{
  int         have_waiters;

  EnterCriticalSection( &cv->waiters_count_lock_ );
  have_waiters= (cv->waiters_count_ > 0);
  LeaveCriticalSection( &cv->waiters_count_lock_ );

  // If there aren't any waiters, then this is a no-op.  
  if (have_waiters)
    ReleaseSemaphore( cv->sema_, 1, 0 );

  return 0;
}

int pthread_cond_broadcast( pthread_cond_t *  cv )
{
  int         have_waiters= 0;

  // This is needed to ensure that <waiters_count_> and <was_broadcast_> are
  // consistent relative to each other.
  EnterCriticalSection( &cv->waiters_count_lock_ );

  if (cv->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.
    cv->was_broadcast_ = 1;
    have_waiters = 1;
  }

  if (have_waiters) {
    // Wake up all the waiters atomically.
    ReleaseSemaphore( cv->sema_, cv->waiters_count_, 0 );

    LeaveCriticalSection( &cv->waiters_count_lock_ );

    // Wait for all the awakened threads to acquire the counting
    // semaphore. 
    WaitForSingleObject( cv->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.
    cv->was_broadcast_= 0;
  }
  else
    LeaveCriticalSection( &cv->waiters_count_lock_ );

  return 0;
}
