/*
    This file is a part of libcds - Concurrent Data Structures library

    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017

    Source code repo: http://github.com/khizmax/libcds/
    Download: http://sourceforge.net/projects/libcds/files/

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef CDSLIB_URCU_DETAILS_SH_DECL_H
#define CDSLIB_URCU_DETAILS_SH_DECL_H

#include <cds/urcu/details/base.h>

#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
#include <cds/details/static_functor.h>
#include <cds/details/lib.h>
#include <cds/user_setup/cache_line.h>

#include <signal.h>

//@cond
namespace cds { namespace urcu { namespace details {

    // We could derive thread_data from thread_list_record
    // but in this case m_nAccessControl would have offset != 0
    // that is not so efficiently
#   define CDS_SHURCU_DECLARE_THREAD_DATA(tag_) \
    template <> struct thread_data<tag_> { \
        atomics::atomic<uint32_t>        m_nAccessControl ; \
        atomics::atomic<bool>            m_bNeedMemBar    ; \
        thread_list_record< thread_data >   m_list ; \
        char pad_[cds::c_nCacheLineSize]; \
        thread_data(): m_nAccessControl(0), m_bNeedMemBar(false) {} \
        explicit thread_data( OS::ThreadId owner ): m_nAccessControl(0), m_bNeedMemBar(false), m_list(owner) {} \
        ~thread_data() {} \
    }

    CDS_SHURCU_DECLARE_THREAD_DATA( signal_buffered_tag );

#   undef CDS_SHURCU_DECLARE_THREAD_DATA

    template <typename RCUtag>
    struct sh_singleton_instance
    {
        static singleton_vtbl *     s_pRCU;
    };
#if CDS_COMPILER != CDS_COMPILER_MSVC
    template<> singleton_vtbl * sh_singleton_instance< signal_buffered_tag >::s_pRCU;
#endif

    template <typename SigRCUtag>
    class sh_thread_gc
    {
    public:
        typedef SigRCUtag                   rcu_tag;
        typedef typename rcu_tag::rcu_class rcu_class;
        typedef thread_data< rcu_tag >      thread_record;
        typedef cds::urcu::details::scoped_lock< sh_thread_gc > scoped_lock;

    protected:
        static thread_record * get_thread_record();

    public:
        sh_thread_gc();
        ~sh_thread_gc();

    public:
        static void access_lock();
        static void access_unlock();
        static bool is_locked();

        /// Retire pointer \p by the disposer \p Disposer
        template <typename Disposer, typename T>
        static void retire( T * p )
        {
            retire( p, cds::details::static_functor<Disposer, T>::call );
        }

        /// Retire pointer \p by the disposer \p pFunc
        template <typename T>
        static void retire( T * p, void (* pFunc)(T *))
        {
            retired_ptr rp( reinterpret_cast<void *>( p ), reinterpret_cast<free_retired_ptr_func>( pFunc ));
            retire( rp );
        }

        /// Retire pointer \p
        static void retire( retired_ptr& p )
        {
            assert( sh_singleton_instance< rcu_tag >::s_pRCU );
            sh_singleton_instance< rcu_tag >::s_pRCU->retire_ptr( p );
        }
    };

#   define CDS_SH_RCU_DECLARE_THREAD_GC( tag_ ) template <> class thread_gc<tag_>: public sh_thread_gc<tag_> {}

    CDS_SH_RCU_DECLARE_THREAD_GC( signal_buffered_tag  );

#   undef CDS_SH_RCU_DECLARE_THREAD_GC

    template <class RCUtag>
    class sh_singleton: public singleton_vtbl
    {
    public:
        typedef RCUtag  rcu_tag;
        typedef cds::urcu::details::thread_gc< rcu_tag >   thread_gc;

    protected:
        typedef typename thread_gc::thread_record   thread_record;
        typedef sh_singleton_instance< rcu_tag >    rcu_instance;

    protected:
        atomics::atomic<uint32_t>   m_nGlobalControl;
        thread_list< rcu_tag >      m_ThreadList;
        int const                   m_nSigNo;

    protected:
        sh_singleton( int nSignal )
            : m_nGlobalControl(1)
            , m_nSigNo( nSignal )
        {
            set_signal_handler();
        }

        ~sh_singleton()
        {
            clear_signal_handler();
        }

    public:
        static sh_singleton * instance()
        {
            return static_cast< sh_singleton *>( rcu_instance::s_pRCU );
        }

        static bool isUsed()
        {
            return rcu_instance::s_pRCU != nullptr;
        }

        int signal_no() const
        {
            return m_nSigNo;
        }

    public:
        virtual void retire_ptr( retired_ptr& p ) = 0;

    public: // thread_gc interface
        thread_record * attach_thread()
        {
            return m_ThreadList.alloc();
        }

        void detach_thread( thread_record * pRec )
        {
            m_ThreadList.retire( pRec );
        }

        uint32_t global_control_word( atomics::memory_order mo ) const
        {
            return m_nGlobalControl.load( mo );
        }

    protected:
        void set_signal_handler();
        void clear_signal_handler();
        static void signal_handler( int signo, siginfo_t * sigInfo, void * context );
        void raise_signal( cds::OS::ThreadId tid );

        template <class Backoff>
        void force_membar_all_threads( Backoff& bkOff );

        void switch_next_epoch()
        {
            m_nGlobalControl.fetch_xor( rcu_tag::c_nControlBit, atomics::memory_order_seq_cst );
        }
        bool check_grace_period( thread_record * pRec ) const;

        template <class Backoff>
        void wait_for_quiescent_state( Backoff& bkOff );
    };

#   define CDS_SIGRCU_DECLARE_SINGLETON( tag_ ) \
    template <> class singleton< tag_ > { \
    public: \
        typedef tag_  rcu_tag ; \
        typedef cds::urcu::details::thread_gc< rcu_tag >   thread_gc ; \
    protected: \
        typedef thread_gc::thread_record            thread_record ; \
        typedef sh_singleton_instance< rcu_tag >    rcu_instance  ; \
        typedef sh_singleton< rcu_tag >             rcu_singleton ; \
    public: \
        static bool isUsed() { return rcu_singleton::isUsed() ; } \
        static rcu_singleton * instance() { assert( rcu_instance::s_pRCU ); return static_cast<rcu_singleton *>( rcu_instance::s_pRCU ); } \
        static thread_record * attach_thread() { return instance()->attach_thread() ; } \
        static void detach_thread( thread_record * pRec ) { return instance()->detach_thread( pRec ) ; } \
        static uint32_t global_control_word( atomics::memory_order mo ) { return instance()->global_control_word( mo ) ; } \
    }

    CDS_SIGRCU_DECLARE_SINGLETON( signal_buffered_tag  );

#   undef CDS_SIGRCU_DECLARE_SINGLETON

}}} // namespace cds::urcu::details
//@endcond

#endif // #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
#endif // #ifndef CDSLIB_URCU_DETAILS_SH_DECL_H
