/* Copyright 2005 Chris Thomasson */


#include "../../../include/ac_eventcount_algo1.h"




#ifndef AC_I686_STACK_MPMC_H
#define AC_I686_STACK_MPMC_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_i686_prv_stack_mpmc_shared_
{
  ac_eventcount_algo1_shared_t wset;
  ac_i686_stack_mpmc_t queue;

} ac_i686_prv_stack_mpmc_shared_t;


typedef struct 
AC_DECLSPEC_PACKED_ALIGN_CACHE_LINE 
ac_i686_prv_stack_mpmc_shared_padded_
{
  ac_i686_prv_stack_mpmc_shared_t _this;

} ac_i686_prv_stack_mpmc_shared_padded_t;


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


typedef struct ac_i686_prv_stack_mpmc_
{
  ac_i686_prv_stack_mpmc_shared_t *shared;

  ac_eventcount_algo1_t wset;

  /* read-only */
  void *shared_mem;

} ac_i686_prv_stack_mpmc_t;




/* critical test compile time assertion */
AC_BUILD_DBG_ASSERT
( i686_prv_stack_mpmc,
  sizeof( ac_i686_prv_stack_mpmc_shared_padded_t ) == AC_CPU_CACHE_LINE );




AC_APIEXPORT int AC_APIDECL
ac_i686_prv_stack_mpmc_alloc
( ac_i686_prv_stack_mpmc_t*,
  ac_i686_prv_stack_mpmc_shared_t* );


AC_APIEXPORT int AC_APIDECL
ac_i686_prv_stack_mpmc_free
( ac_i686_prv_stack_mpmc_t* );


AC_APIEXPORT ac_i686_node_t* AC_APIDECL
ac_i686_prv_stack_mpmc_timedpop
( ac_i686_prv_stack_mpmc_t*, 
  ac_thread_t*,
  ac_lfgc_smr_hazard_t,
  ac_timeout_t );




AC_DECLSPEC_INLINE int AC_APIDECL
ac_i686_prv_stack_mpmc_push
( ac_i686_prv_stack_mpmc_t *_this,
  ac_thread_t *_tls,
  const void *state )
{
  int ret;
  ac_i686_node_t *node;

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

  node = ac_thread_cpu_node_cache_pop
          ( _tls,
            state );

  if (node) {

    ac_i686_stack_mpmc_push_cas
      ( &_this->shared->queue,
        node );

    ret = ac_eventcount_algo1_signal_naked
            ( &_this->wset );
    if ( ret ) { return ac_sys_error( ret ); }
 
    return 0;

  }

  return ac_sys_error(ENOMEM);
}


AC_DECLSPEC_INLINE int AC_APIDECL
ac_i686_stack_mpmc_timedpop
( ac_i686_prv_stack_mpmc_t *_this, 
  ac_thread_t *_tls,
  void **state,
  ac_timeout_t timeout )
{
  ac_i686_node_t *node;
  ac_lfgc_smr_hazard_t hazard;

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

  hazard = ac_lfgc_smr_get( _tls, 0 );

  node = np_ac_i686_lfgc_smr_stack_mpmc_pop_dwcas
          ( &_this->shared->queue,
            hazard );

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

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

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

  if ( ac_thread_cpu_node_cache_push_no_free
        ( _tls,
          node ) )
  {
    ac_lfgc_smr_collect( _tls, 0, node );
  }

  return 0;
}


#define ac_i686_prv_stack_mpmc_pop( ac_macro_this, ac_macro_thread, ac_macro_state ) \
  ( ac_i686_stack_mpmc_timedpop \
      ( (ac_macro_this), \
        (ac_macro_thread), \
        (ac_macro_state), \
        AC_INFINITE ) )




typedef ac_i686_prv_stack_mpmc_shared_t ac_stack_mpmc_shared_t;
typedef ac_i686_prv_stack_mpmc_shared_padded_t ac_stack_mpmc_shared_padded_t;
typedef ac_i686_prv_stack_mpmc_t ac_stack_mpmc_t;


#define ac_stack_mpmc_alloc ac_i686_prv_stack_mpmc_alloc
#define ac_stack_mpmc_free ac_i686_prv_stack_mpmc_free
#define ac_stack_mpmc_push ac_i686_prv_stack_mpmc_push
#define ac_stack_mpmc_timedpop ac_i686_stack_mpmc_timedpop
#define ac_stack_mpmc_pop ac_i686_prv_stack_mpmc_pop




#ifdef __cplusplus
}
#endif


#endif