// 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>

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

Standard_Mutex::Standard_Mutex () 
{
#ifdef WNT
  InitializeCriticalSection( &myMutex );
#else
  pthread_mutex_init( &myMutex, 0 );
#endif
}

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

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

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

void Standard_Mutex::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)
  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_Mutex::TryLock
//=============================================

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

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

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