/* Copyright 2005 Chris Thomasson */


#include "../../../include/sys/ac_sys.h"
#include "../../../include/cpu/i686/ac_i686_stack_mpmc.h"




int AC_APIDECL
ac_i686_prv_stack_mpmc_alloc
( ac_i686_prv_stack_mpmc_t *_this,
  ac_i686_prv_stack_mpmc_shared_t *_shared )
{
  int ret;

  if ( ! _shared )
  {
    ac_i686_prv_stack_mpmc_shared_padded_t *_padded;

    /* alloc and align memory */
    _padded = ac_malloc_aligned
              ( &_this->shared_mem,
                sizeof( *_padded ),
                AC_CPU_CACHE_LINE );
    if ( ! _padded ) { return ac_sys_error( ENOMEM ); }
     
    _shared = &_padded->_this;
  }

  else { _this->shared_mem = 0; }

  /* verify shared alignment */
  ret = AC_SYS_IS_MEM_ALIGNED
          ( _shared,
            AC_CPU_WORD_SIZE );
  if ( ret )
  {
    ac_free( _this->shared_mem );
    return ac_sys_error( ret );
  }

  /* init waitset */
  ret = ac_eventcount_algo1_alloc
          ( &_this->wset,
            &_shared->wset );
  if ( ret )
  {
    ac_free( _this->shared_mem );
    return ac_sys_error( ret );
  }

  /* init shared */
  _shared->queue.front = 0;

  /* init read-only */
  _this->shared = _shared;
  
  return 0;
}


int AC_APIDECL
ac_i686_prv_stack_mpmc_free
( ac_i686_prv_stack_mpmc_t *_this )
{
  int ret;

  /* see if there are any nodes */
  if ( _this->shared->queue.front )
  {
    return ac_sys_error( EBUSY );
  }

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

  ac_free( _this->shared_mem );

  return 0;
}


ac_i686_node_t* AC_APIDECL
ac_i686_prv_stack_mpmc_timedpop
( ac_i686_prv_stack_mpmc_t *_this, 
  ac_thread_t *_tls,
  ac_lfgc_smr_hazard_t hazard,
  ac_timeout_t timeout )
{
  ac_i686_node_t *node;

  ac_cpu_pause_yield();

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

  while ( ! node )
  {
    ac_eventcount_cmp_t cmp;

    if ( ! timeout ) { return 0; }

    cmp = ac_eventcount_algo1_get( &_this->wset );

    ac_cpu_pause_yield();

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

    if ( ! node )
    {
      int ret = ac_eventcount_algo1_timedwait
                  ( &_this->wset,
                    _tls, 
                    cmp,
                    timeout );
      if ( ret ) { ac_sys_error( ret ); return 0; }

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

  return node;
}