// File:	Standard_Mutex.cxx
// Created:	Thu Apr 13 08:21:40 2006
// Author:	Andrey BETENEV
// Copyright:	Open CASCADE S.A.S. 2006

// On Windows, function TryEnterCriticalSection has appeared in Windows NT
// and is surrounded by #ifdef in MS VC++ 7.1 headers.
// Thus to use it we need to define appropriate macro saying that we wil
// run on Windows NT 4.0 at least
#if defined(WNT) && ! defined(_WIN32_WINNT)
#define _WIN32_WINNT 0x0400
#endif

#include <Standard_Mutex.hxx>
#include <Standard_OStream.hxx>

#ifdef WNT
#include <windows.h>
#else
#include <pthread.h>
#include <sys/errno.h>
#include <unistd.h>
#include <time.h>
#endif

// OS-dependent implementation of mutex
class Standard_MutexPrivate {
public:
    Standard_MutexPrivate()
    {
#ifdef WNT
        InitializeCriticalSection( &myMutex );
#else
        pthread_mutex_init( &myMutex, 0 );
#endif
    }

    ~Standard_MutexPrivate()
    {
#ifdef WNT
        DeleteCriticalSection( &myMutex );
#else
        pthread_mutex_destroy( &myMutex );
#endif
    }

    void Lock();
    Standard_Boolean TryLock();
    void Unlock()
    {
#ifdef WNT
        LeaveCriticalSection( &myMutex );
#else
        pthread_mutex_unlock( &myMutex );
#endif
    }

private:
#ifdef WNT
  CRITICAL_SECTION myMutex;
#else
  pthread_mutex_t myMutex;
#endif  
};

void Standard_MutexPrivate::Lock()
{
#ifdef WNT
  // On Windows, concurrent access to the same critical section leads to very low 
  // performance (at least on single-processor systems, even with hyperthreading).
  // To avoid this, we use TryEnter and explicitly suspend execution of the waiting
  // process for minimal possible time (1 ms) in cycle.
  // This ensures that when our thread is locked,  processor will immediately give 
  // control to other thread(s)
  EnterCriticalSection( &myMutex );
  //while ( ! TryEnterCriticalSection( &myMutex ) ) 
  //  Sleep ( 1 );
#else
#ifdef LIN
  // On Linux, concurrent access to the same mutex leads to very low performance on
  // single-processor systems; this most likely indicates that the mutex implementation 
  // makes the thread requesting the mutex to use CPU time due to busy-waiting.
  // To avoid this, we use trylock and explicitly suspend execution of the waiting
  // process for minimal possible time (1 ms) in cycle.
  // This ensures that when our thread is locked, processor will immediately give 
  // control to other thread(s)
  while ( pthread_mutex_trylock( &myMutex ) == EBUSY ) {
    const struct timespec ts = { 0L, 1000000 }; // 1 ms
    struct timespec tres;
    nanosleep(&ts,&tres);
  }
#else
  // On SUN, this works well. Other platforms are not tested so far.
  pthread_mutex_lock( &myMutex );
#endif
#endif
}

Standard_Boolean Standard_MutexPrivate::TryLock()
{
#ifdef WNT
  return ( TryEnterCriticalSection( &myMutex ) != 0 );
#else
  return ( pthread_mutex_trylock( &myMutex ) != EBUSY );
#endif
}


//=============================================
// Standard_Mutex::Standard_Mutex
//=============================================

Standard_Mutex::Standard_Mutex () 
{
  myMutex = new Standard_MutexPrivate;
}

//=============================================
// Standard_Mutex::~Standard_Mutex
//=============================================

Standard_Mutex::~Standard_Mutex () 
{
  if (myMutex)
    delete myMutex;
}

//=============================================
// Standard_Mutex::Lock
//=============================================

void Standard_Mutex::Lock ()
{
  myMutex->Lock();
}

//=============================================
// Standard_Mutex::TryLock
//=============================================

Standard_Boolean Standard_Mutex::TryLock ()
{
  return myMutex->TryLock();
}

//=============================================
// Standard_Mutex::Unlock
//=============================================

void Standard_Mutex::Unlock ()
{
  myMutex->Unlock();
}

//=============================================
// Standard_Mutex::DestroyCallback
//=============================================

void Standard_Mutex::DestroyCallback ()
{
  UnregisterCallback();
  Unlock();
}
