/* ---------------------------------------------------------------------------
 *
 * (c) The GHC Team, 2001
 *
 * Accessing OS threads functionality in a (mostly) OS-independent
 * manner. 
 *
 * 
 * --------------------------------------------------------------------------*/
#include "Rts.h"
#if defined(RTS_SUPPORTS_THREADS)
#include "OSThreads.h"
#include "RtsUtils.h"

#if defined(HAVE_PTHREAD_H) && !defined(WANT_NATIVE_WIN32_THREADS)
/*
 * This (allegedly) OS threads independent layer was initially
 * abstracted away from code that used Pthreads, so the functions
 * provided here are mostly just wrappers to the Pthreads API.
 *
 */

void
initCondition( Condition* pCond )
{
  pthread_cond_init(pCond, NULL);
  return;
}

void
closeCondition( Condition* pCond )
{
  pthread_cond_destroy(pCond);
  return;
}

rtsBool
broadcastCondition ( Condition* pCond )
{
  return (pthread_cond_broadcast(pCond) == 0);
}

rtsBool
signalCondition ( Condition* pCond )
{
  return (pthread_cond_signal(pCond) == 0);
}

rtsBool
waitCondition ( Condition* pCond, Mutex* pMut )
{
  return (pthread_cond_wait(pCond,pMut) == 0);
}

void
yieldThread()
{
  sched_yield();
  return;
}

void
shutdownThread()
{
  pthread_exit(NULL);
}

/* Don't need the argument nor the result, at least not yet. */
static void* startProcWrapper(void* pProc);
static void*
startProcWrapper(void* pProc)
{
  ((void (*)(void))pProc)();
  return NULL;
}


int
createOSThread ( OSThreadId* pId, void (*startProc)(void))
{
  int result = pthread_create(pId, NULL, startProcWrapper, (void*)startProc);
  if(!result)
    pthread_detach(*pId);
  return result;
}

OSThreadId
osThreadId()
{
  return pthread_self();
}

void
initMutex(Mutex* pMut)
{
  pthread_mutex_init(pMut,NULL);
  return;
}

static void *
forkOS_createThreadWrapper ( void * entry )
{
    rts_lock();
    rts_evalStableIO((HsStablePtr) entry, NULL);
    rts_unlock();
    return NULL;
}

int
forkOS_createThread ( HsStablePtr entry )
{
    pthread_t tid;
    int result = pthread_create(&tid, NULL,
				forkOS_createThreadWrapper, (void*)entry);
    if(!result)
        pthread_detach(tid);
    return result;
}

#elif defined(HAVE_WINDOWS_H)
/* For reasons not yet clear, the entire contents of process.h is protected 
 * by __STRICT_ANSI__ not being defined.
 */
#undef __STRICT_ANSI__
#include <process.h>

/* Win32 threads and synchronisation objects */

/* A Condition is represented by a Win32 Event object;
 * a Mutex by a Mutex kernel object.
 *
 * ToDo: go through the defn and usage of these to
 * make sure the semantics match up with that of 
 * the (assumed) pthreads behaviour. This is really
 * just a first pass at getting something compilable.
 */

void
initCondition( Condition* pCond )
{
  HANDLE h =  CreateEvent(NULL, 
			  FALSE,  /* auto reset */
			  FALSE,  /* initially not signalled */
			  NULL); /* unnamed => process-local. */
  
  if ( h == NULL ) {
    errorBelch("initCondition: unable to create");
  }
  *pCond = h;
  return;
}

void
closeCondition( Condition* pCond )
{
  if ( CloseHandle(*pCond) == 0 ) {
    errorBelch("closeCondition: failed to close");
  }
  return;
}

rtsBool
broadcastCondition ( Condition* pCond )
{
  PulseEvent(*pCond);
  return rtsTrue;
}

rtsBool
signalCondition ( Condition* pCond )
{
    if (SetEvent(*pCond) == 0) {
	barf("SetEvent: %d", GetLastError());
    }
    return rtsTrue;
}

rtsBool
waitCondition ( Condition* pCond, Mutex* pMut )
{
  RELEASE_LOCK(pMut);
  WaitForSingleObject(*pCond, INFINITE);
  /* Hmm..use WaitForMultipleObjects() ? */
  ACQUIRE_LOCK(pMut);
  return rtsTrue;
}

void
yieldThread()
{
  Sleep(0);
  return;
}

void
shutdownThread()
{
  _endthreadex(0);
}

static unsigned __stdcall startProcWrapper(void* pReal);
static unsigned __stdcall
startProcWrapper(void* pReal)
{
  ((void (*)(void))pReal)();
  return 0;
}

int
createOSThread ( OSThreadId* pId, void (*startProc)(void))
{
  
  return (_beginthreadex ( NULL,  /* default security attributes */
			   0,
			   startProcWrapper,
			   (void*)startProc,
			   0,
			   (unsigned*)pId) == 0);
}

OSThreadId
osThreadId()
{
  return GetCurrentThreadId();
}

void
initMutex (Mutex* pMut)
{
  HANDLE h = CreateMutex ( NULL,  /* default sec. attributes */
			   FALSE, /* not owned => initially signalled */
			   NULL
			   );
  *pMut = h;
  return;
}

static unsigned __stdcall
forkOS_createThreadWrapper ( void * entry )
{
    rts_lock();
    rts_evalStableIO((HsStablePtr) entry, NULL);
    rts_unlock();
    return 0;
}

int
forkOS_createThread ( HsStablePtr entry )
{
    unsigned long pId;
    return (_beginthreadex ( NULL,  /* default security attributes */
			   0,
			   forkOS_createThreadWrapper,
			   (void*)entry,
			   0,
			   (unsigned*)&pId) == 0);
}

#endif /* defined(HAVE_PTHREAD_H) */

#else /* !defined(RTS_SUPPORTS_THREADS) */

int
forkOS_createThread ( HsStablePtr entry STG_UNUSED )
{
    return -1;
}

#endif /* !defined(RTS_SUPPORTS_THREADS) */

