/*
 * Portions Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999
 * Boris Fomitchev
 *
 * This material is provided "as is", with absolutely no warranty expressed
 * or implied. Any use is at your own risk.
 *
 * Permission to use or copy this software for any purpose is hereby granted
 * without fee, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 *
 */
#ifndef _STLP_THREADS_C
#define _STLP_THREADS_C

#ifndef _STLP_INTERNAL_THREADS_H
#  include <stl/_threads.h>
#endif

#if defined (_STLP_EXPOSE_GLOBALS_IMPLEMENTATION)

#if defined (_STLP_SGI_THREADS)
#  include <time.h>
#elif defined (_STLP_UNIX)
#  ifndef _STLP_INTERNAL_CTIME
#    include <stl/_ctime.h>
#  endif
#  if defined (_STLP_USE_NAMESPACES) && !defined (_STLP_VENDOR_GLOBAL_CSTD)
using _STLP_VENDOR_CSTD::time_t;
#  endif
#  include <sys/time.h>
#endif

_STLP_BEGIN_NAMESPACE

#if (_STLP_STATIC_TEMPLATE_DATA > 0)

#  if defined (_STLP_USE_ATOMIC_SWAP_MUTEX)
#if !defined(__SYMBIAN32__WSD__)
template<int __32bits>
_STLP_STATIC_MUTEX
_Atomic_swap_struct<__32bits>::_S_swap_lock _STLP_MUTEX_INITIALIZER;
#endif
#    undef _STLP_USE_ATOMIC_SWAP_MUTEX
#  endif

#  if defined (_STLP_THREADS) && !defined (_STLP_USE_PTHREAD_SPINLOCK)
//Note: For SYMBIAN Emulator, these entries are to be considered WSD.  
//Still, EWSD solution can't be applied since it's templated.
template <int __inst>
unsigned _STLP_mutex_spin<__inst>::__max = _STLP_mutex_spin<__inst>::__low_max;

template <int __inst>
unsigned _STLP_mutex_spin<__inst>::__last = 0;

#  endif // _STLP_USE_PTHREAD_SPINLOCK

#else /* ( _STLP_STATIC_TEMPLATE_DATA > 0 ) */

#  if defined (_STLP_USE_ATOMIC_SWAP_MUTEX)
__DECLARE_INSTANCE(_STLP_STATIC_MUTEX, _Atomic_swap_struct<sizeof(__stl_atomic_t) == sizeof(void*)>::_S_swap_lock,
                   _STLP_MUTEX_INITIALIZER  );
#    undef _STLP_USE_ATOMIC_SWAP_MUTEX
#  endif /* _STLP_PTHREADS */

#  if defined (_STLP_THREADS) && !defined (_STLP_USE_PTHREAD_SPINLOCK)
__DECLARE_INSTANCE(unsigned, _STLP_mutex_spin<0>::__max,  =30);
__DECLARE_INSTANCE(unsigned, _STLP_mutex_spin<0>::__last, =0);
#  endif // _STLP_USE_PTHREAD_SPINLOCK

#endif /* ( _STLP_STATIC_TEMPLATE_DATA > 0 ) */

#if defined (_STLP_THREADS) && !defined (_STLP_USE_PTHREAD_SPINLOCK)

#  if defined (_STLP_SPARC_SOLARIS_THREADS)
// underground function in libc.so; we do not want dependance on librt
extern "C" int __nanosleep(const struct timespec*, struct timespec*);
#    define _STLP_NANOSLEEP __nanosleep
#  else
#    define _STLP_NANOSLEEP nanosleep
#  endif

template <int __inst>
void _STLP_CALL
_STLP_mutex_spin<__inst>::_S_nsec_sleep(int __log_nsec) {
#  if defined (_STLP_WIN32THREADS)
  if (__log_nsec <= 20) {
    // Note from boost (www.boost.org):
    // Changed to Sleep(1) from Sleep(0).
    // According to MSDN, Sleep(0) will never yield
    // to a lower-priority thread, whereas Sleep(1)
    // will. Performance seems not to be affected.
    Sleep(1);
  } else {
    Sleep(1 << (__log_nsec - 20));
  }
#  elif defined(_STLP_OS2THREADS)
  if (__log_nsec <= 20) {
    DosSleep(0);
  } else {
    DosSleep(1 << (__log_nsec - 20));
  }
#  elif defined (_STLP_UNIX)
  timespec __ts;
  /* Max sleep is 2**27nsec ~ 60msec      */
  __ts.tv_sec = 0;
  __ts.tv_nsec = 1 << __log_nsec;
  _STLP_NANOSLEEP(&__ts, 0);
#  endif
}

template <int __inst>
void  _STLP_CALL
_STLP_mutex_spin<__inst>::_M_do_lock(volatile __stl_atomic_t* __lock) {
#  if defined(_STLP_ATOMIC_EXCHANGE)
  if (_Atomic_swap(__lock, 1)) {
    unsigned __my_spin_max = _STLP_mutex_spin<0>::__max;
    unsigned __my_last_spins = _STLP_mutex_spin<0>::__last;
    volatile unsigned __junk = 17;   // Value doesn't matter.
    unsigned  __i;

    for (__i = 0; __i < __my_spin_max; ++__i) {
      if (__i < __my_last_spins/2 || *__lock) {
        __junk *= __junk; __junk *= __junk;
        __junk *= __junk; __junk *= __junk;
      } else {
        if (!_Atomic_swap(__lock, 1)) {
          // got it!
          // Spinning worked.  Thus we're probably not being scheduled
          // against the other process with which we were contending.
          // Thus it makes sense to spin longer the next time.
          _STLP_mutex_spin<0>::__last = __i;
          _STLP_mutex_spin<0>::__max = _STLP_mutex_spin<0>::__high_max;
          return;
        }
      }
    }

    // We are probably being scheduled against the other process.  Sleep.
    _STLP_mutex_spin<0>::__max = _STLP_mutex_spin<0>::__low_max;

    for (__i = 0 ;; ++__i) {
      int __log_nsec = __i + 6;

      if (__log_nsec > 27) __log_nsec = 27;
      if (!_Atomic_swap(__lock, 1)) {
        break;
      }
      _S_nsec_sleep(__log_nsec);
    }
  } /* first _Atomic_swap */
#  endif
}
#endif // _STLP_USE_PTHREAD_SPINLOCK

_STLP_END_NAMESPACE

#endif /* _STLP_EXPOSE_GLOBALS_IMPLEMENTATION */
#endif /*  _STLP_THREADS_C */

// Local Variables:
// mode:C++
// End:
