/* Copyright 2005 Chris Thomasson */


#include "appcore.h"




#ifndef AC_SMR_HPP
#define AC_SMR_HPP




template< typename T > class shared_smr_ptr;
template< typename T > class local_smr_ptr;




template< typename T >
class shared_smr_ptr
{
  friend class local_smr_ptr< T >;


  mutable ac_lfgc_refcount_t *m_ref;


  static void AC_CDECL smr_dtor( void *state )
  {
    delete (T*)state;
  }


  ac_lfgc_refcount_t* get_refcount() const { return m_ref; }


  ac_lfgc_refcount_t* mb_get_refcount() const 
  { return (ac_lfgc_refcount_t*)ac_mb_loadptr_depends( &m_ref ); }


public:

  shared_smr_ptr( T *state = 0, ac_thread_t *_tls = 0 )
    : m_ref( 0 )
  {
    if ( state )
    { int ret = ac_lfgc_refcount_alloc
                  ( _tls,
                    &m_ref,
                    smr_dtor,
                    state );
      if ( ret ) { throw ret; }
    }
  }


  shared_smr_ptr( const shared_smr_ptr &src ) 
    : m_ref( 0 ) { *this = src; }


  shared_smr_ptr( const local_smr_ptr< T > &src ) 
    : m_ref( 0 ) { *this = src; }


  ~shared_smr_ptr() { ac_lfgc_refcount_release( m_ref ); }


public:

  
  void xchg( local_smr_ptr< T > &_xchg )
  {
    ac_lfgc_refcount_t *temp = _xchg.mb_get_refcount();
    _xchg.m_ref = ac_lfgc_refcount_xchg( &m_ref, temp );
    ac_lfgc_refcount_release( temp );
  }


  bool cas( const local_smr_ptr< T > &cmp,
            const local_smr_ptr< T > &xchg )
  {
    return ( ! ac_lfgc_refcount_cas
                ( &m_ref,
                  cmp.get_refcount(),
                  xchg.get_refcount() ) ) 
           ? true : false;
  }


public:

  shared_smr_ptr& operator =( T *state )
  {
    if ( state )
    { local_smr_ptr< T > temp( state );
      xchg( temp );
    }

    else { ac_lfgc_refcount_null( &m_ref ); }

    return *this;
  }


  shared_smr_ptr& operator =( const shared_smr_ptr &src )
  {
    ac_lfgc_refcount_copy_shared
      ( 0,
        &m_ref,
        &src.m_ref );
    return *this;
  }


  shared_smr_ptr& operator =( const local_smr_ptr< T > &src )
  {
    ac_lfgc_refcount_copy_local
      ( &m_ref,
        src.get_refcount() );
    return *this;
  }


  bool operator !() { return ( ! m_ref ); }


};




template< typename T >
class local_smr_ptr
{
  friend class shared_smr_ptr< T >;


  mutable ac_lfgc_refcount_t *m_ref;


  ac_lfgc_refcount_t* get_refcount() const { return m_ref; }


  ac_lfgc_refcount_t* mb_get_refcount() const 
  { return (ac_lfgc_refcount_t*)ac_mb_loadptr_depends( &m_ref ); }


public:

  local_smr_ptr( T *state = 0, ac_thread_t *_tls = 0 )
    : m_ref( 0 )
  {
    if ( state )
    { int ret = ac_lfgc_refcount_alloc
                  ( _tls,
                    &m_ref,
                    shared_smr_ptr< T >::smr_dtor,
                    state );
      if ( ret ) { throw ret; }
    }
  }


  local_smr_ptr( const local_smr_ptr &src ) 
    : m_ref( 0 ) { *this = src; }


  local_smr_ptr( const shared_smr_ptr< T > &src ) 
    : m_ref( 0 ) { *this = src; }


  ~local_smr_ptr() { ac_lfgc_refcount_release( m_ref ); }


public:

  local_smr_ptr& operator =( T *state )
  {
    if ( state )
    { local_smr_ptr< T > temp( state );
      xchg( temp );
    }

    else { ac_lfgc_refcount_null( &m_ref ); }

    return *this;
  }


  local_smr_ptr& operator =( const local_smr_ptr &src )
  {
    ac_lfgc_refcount_copy_local
      ( &m_ref,
        src.get_refcount() );
    return *this;
  }


  local_smr_ptr& operator =( const shared_smr_ptr< T > &src )
  {
    ac_lfgc_refcount_copy_shared
      ( 0,
        &m_ref,
        &src.m_ref );
    return *this;
  }


  T* operator ->() { return (T*)ac_lfgc_refcount_get( m_ref ); }


  bool operator !() { return ( ! m_ref ); }

};




#endif