/* Copyright 2005 Chris Thomasson */


#include "ac_eventcount_algo1.h"




#ifndef AC_QUEUE_SPSC_H
#define AC_QUEUE_SPSC_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_queue_spsc_shared_
{
  ac_cpu_queue_spsc_t queue;
  ac_eventcount_algo1_shared_t impl_wset;

} ac_queue_spsc_shared_t;

typedef struct 
AC_DECLSPEC_PACKED_ALIGN_CACHE_LINE 
ac_queue_spsc_shared_padded_
{
  ac_queue_spsc_shared_t _this;

} ac_queue_spsc_shared_padded_t;

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


typedef struct ac_queue_spsc_
{
  ac_queue_spsc_shared_t *shared;

  ac_eventcount_algo1_t impl_wset;

  /* read-only */
  void *shared_mem;

} ac_queue_spsc_t;




/* critical test compile time assertion */
AC_BUILD_DBG_ASSERT
( queue_spsc,
  sizeof( ac_queue_spsc_shared_padded_t ) == AC_CPU_CACHE_LINE );




AC_APIEXPORT int AC_APIDECL
ac_queue_spsc_alloc
( ac_queue_spsc_t*,
  ac_queue_spsc_shared_t* );


AC_APIEXPORT int AC_APIDECL
ac_queue_spsc_free
( ac_queue_spsc_t* );


AC_APIEXPORT ac_cpu_node_t* AC_APIDECL
ac_prv_queue_spsc_timedpop
( ac_queue_spsc_t*, 
  ac_thread_t*,
  ac_timeout_t );


#define ac_queue_spsc_pop( ac_macro_this, ac_macro_thread, ac_macro_state ) \
  ( ac_queue_spsc_timedpop \
      ( (ac_macro_this), \
        (ac_macro_thread), \
        (ac_macro_state), \
        AC_INFINITE ) )


AC_DECLSPEC_INLINE ac_eventcount_algo1_t* AC_APIDECL
ac_queue_spsc_getec
( ac_queue_spsc_t *_this )
{
  return &_this->impl_wset;
}



AC_DECLSPEC_INLINE int AC_APIDECL
ac_queue_spsc_push_nosig
( ac_queue_spsc_t *_this,
  ac_thread_t *_tls,
  const void *state )
{
  ac_cpu_node_t *node;

  if ( ! _tls ) { _tls = ac_thread_self(); };

  node = ac_thread_cpu_node_cache_pop
          ( _tls,
            state );

  if (node) {

    /* atomic push */
    ac_cpu_queue_spsc_push
      ( &_this->shared->queue,
        node );

    return 0;
  }

  return ac_sys_error(ENOMEM);
}


AC_DECLSPEC_INLINE int AC_APIDECL
ac_queue_spsc_push
( ac_queue_spsc_t *_this,
  ac_thread_t *_tls,
  const void *state )
{
  int ret;
  ac_cpu_node_t *node;

  if ( ! _tls ) { _tls = ac_thread_self(); };

  node = ac_thread_cpu_node_cache_pop
          ( _tls,
            state );

  if (node) {

    /* atomic push */
    ac_cpu_queue_spsc_push
      ( &_this->shared->queue,
        node );

    ret = ac_eventcount_algo1_signal_fence
            ( &_this->impl_wset );
    if ( ret ) { return ac_sys_error( ret ); }
 
    return 0;
  }

  return ac_sys_error(ENOMEM); 
}


AC_DECLSPEC_INLINE int AC_APIDECL
ac_queue_spsc_timedpop
( ac_queue_spsc_t *_this, 
  ac_thread_t *_tls,
  void **state,
  ac_timeout_t timeout )
{
  ac_cpu_node_t *node;

  if ( ! _tls ) { _tls = ac_thread_self(); };

  node = ac_cpu_queue_spsc_pop( &_this->shared->queue );

  if ( ! node )
  {
    node = ac_prv_queue_spsc_timedpop
            ( _this,
              _tls,
              timeout );

    if ( ! node ) { return ac_sys_error( ETIMEDOUT ); }
  }

  *state = (void*)node->state;

  ac_thread_cpu_node_cache_push
    ( _tls,
      node );

  return 0;
}




#ifdef __cplusplus
}
#endif


#endif