/* Copyright 2005 Chris Thomasson */


#include "../include/sys/ac_sys.h"
#include "../include/ac_queue_spsc.h"




int AC_APIDECL
ac_queue_spsc_alloc
( ac_queue_spsc_t *_this,
  ac_queue_spsc_shared_t *_shared )
{
  int ret;
  ac_cpu_node_t *dummy;

  if ( ! _shared )
  {
    ac_queue_spsc_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 );
  }

  ret = ac_eventcount_algo1_alloc
          ( &_this->impl_wset,
            &_shared->impl_wset );
  if ( ret )
  {
    ac_free( _this->shared_mem );
    return ac_sys_error( ret );
  }

  /* alloc the dummy nodes */
  dummy = ac_malloc( sizeof( *_shared->queue.front ) );

  if ( ! dummy )
  {
    ac_eventcount_algo1_free
      ( &_this->impl_wset );
    ac_free( _this->shared_mem );
    return ac_sys_error( ENOMEM );
  }

  ac_cpu_node_init( dummy, 0, 0 );

  ac_i686_queue_spsc_init
    ( &_shared->queue,
      dummy );

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


int AC_APIDECL
ac_queue_spsc_free
( ac_queue_spsc_t *_this )
{
  int ret;
  ac_thread_t *_tls = ac_thread_self();

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

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

  ac_thread_cpu_node_cache_push
    ( _tls,
      _this->shared->queue.front );

  ac_free( _this->shared_mem );

  return 0;
}


ac_cpu_node_t* AC_APIDECL
ac_prv_queue_spsc_timedpop
( ac_queue_spsc_t *_this, 
  ac_thread_t *_tls,
  ac_timeout_t timeout )
{
  ac_cpu_node_t *node;

  ac_cpu_pause_yield();

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

  while ( ! node )
  {
    ac_eventcount_cmp_t cmp;

    if ( ! timeout ) { return 0; }

    cmp = ac_eventcount_algo1_get( &_this->impl_wset );

    ac_cpu_pause_yield();

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

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

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

  return node;
}