/* Copyright 2005 Chris Thomasson */


#ifndef AC_RWSPINLOCK_ALGO1_H
#define AC_RWSPINLOCK_ALGO1_H


#ifdef __cplusplus
extern "C"
{
#endif


#ifdef _MSC_VER 
/* 4324: structure was padded due to _declspec(align()) */
#pragma warning ( disable : 4324 )
#pragma pack(1) 
#endif

typedef struct
AC_DECLSPEC_PACKED
ac_rwspinlock_algo1_shared_
{
  ac_intword_t writes;
  ac_intword_t reads;

} ac_rwspinlock_algo1_shared_t;


typedef struct 
AC_DECLSPEC_PACKED_ALIGN_CACHE_LINE 
ac_rwspinlock_algo1_shared_padded_
{
  ac_rwspinlock_algo1_shared_t _this;

} ac_rwspinlock_algo1_shared_padded_t;

#ifdef _MSC_VER 
#pragma pack() 
/* 4324: structure was padded due to _declspec(align()) */
#pragma warning ( default : 4324 )
#endif


typedef struct ac_rwspinlock_algo1_
{
  ac_rwspinlock_algo1_shared_t *shared;

  /* read-only */
  void *shared_mem;

} ac_rwspinlock_algo1_t;




/* critical test compile time assertion */
AC_BUILD_DBG_ASSERT
( rwspinlock_algo1,
  sizeof( ac_rwspinlock_algo1_shared_padded_t ) == AC_CPU_CACHE_LINE );





AC_APIEXPORT int AC_APIDECL
ac_rwspinlock_algo1_alloc
( ac_rwspinlock_algo1_t*,
  ac_rwspinlock_algo1_shared_t* );


AC_APIEXPORT int AC_APIDECL
ac_rwspinlock_algo1_free
( ac_rwspinlock_algo1_t* );




AC_DECLSPEC_INLINE int AC_APIDECL
ac_rwspinlock_algo1_read_unlock
( ac_rwspinlock_algo1_t *_this,
  ac_thread_t *_tls )
{
  if ( ! _tls ) { _tls = ac_thread_self(); }

  if ( ! ac_sys_thread_recurse_dec
          ( _tls,
            _this ) )
  {
    return 0;
  }

  ac_atomic_dec_release( &_this->shared->reads );

  return 0;
}


AC_DECLSPEC_INLINE int AC_APIDECL
ac_rwspinlock_algo1_write_unlock
( ac_rwspinlock_algo1_t *_this,
  ac_thread_t *_tls )
{
  if ( ! _tls ) { _tls = ac_thread_self(); }

  if ( ! ac_sys_thread_recurse_dec
          ( _tls,
            _this ) )
  {
    return 0;
  }

  ac_mb_store_release( &_this->shared->writes, 0 );

  return 0;
}


AC_DECLSPEC_INLINE int AC_APIDECL
ac_rwspinlock_algo1_read_lock
( ac_rwspinlock_algo1_t *_this,
  ac_thread_t *_tls )
{
  if ( ! _tls ) { _tls = ac_thread_self(); }

  if ( ! ac_sys_thread_recurse_inc
          ( _tls,
            _this ) )
  {
    return 0;
  }

  for ( ;; )
  {
    while ( ac_mb_load_naked( &_this->shared->writes ) )
    {
      ac_cpu_pause_yield();
    }

    ac_atomic_inc_acquire( &_this->shared->reads );

    if ( ! ac_mb_load_naked( &_this->shared->writes ) )
    {
      break;
    }

    ac_atomic_dec_release( &_this->shared->reads );
    ac_cpu_pause_yield();
  }

  ac_sys_thread_recurse_locked
    ( _tls,
      _this );

  return 0;
}


AC_DECLSPEC_INLINE int AC_APIDECL
ac_rwspinlock_algo1_write_lock
( ac_rwspinlock_algo1_t *_this,
  ac_thread_t *_tls )
{
  if ( ! _tls ) { _tls = ac_thread_self(); }

  if ( ! ac_sys_thread_recurse_inc
          ( _tls,
            _this ) )
  {
    return 0;
  }

  while ( ac_atomic_xchg_acquire
            ( &_this->shared->writes, 1 ) )
  {
    ac_cpu_pause_yield();
  }

  while ( ac_mb_load_naked( &_this->shared->reads ) )
  {
    ac_cpu_pause_yield();
  }

  ac_sys_thread_recurse_locked
    ( _tls,
      _this );

  return 0;
}




#ifdef __cplusplus
}
#endif


#endif