#ifndef BOOST_SMART_PTR_ATOMIC_SHARED_PTR_HPP_INCLUDED
#define BOOST_SMART_PTR_ATOMIC_SHARED_PTR_HPP_INCLUDED

//
//  atomic_shared_ptr.hpp
//
//  Copyright 2017 Peter Dimov
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
//  See http://www.boost.org/libs/smart_ptr/ for documentation.
//

#include <boost/smart_ptr/detail/requires_cxx11.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/detail/spinlock.hpp>
#include <cstring>

namespace boost
{

template<class T> class atomic_shared_ptr
{
private:

    boost::shared_ptr<T> p_;

    mutable boost::detail::spinlock l_;

    atomic_shared_ptr(const atomic_shared_ptr&);
    atomic_shared_ptr& operator=(const atomic_shared_ptr&);

private:

    bool compare_exchange( shared_ptr<T>& v, shared_ptr<T> w ) BOOST_SP_NOEXCEPT
    {
        l_.lock();

        if( p_._internal_equiv( v ) )
        {
            p_.swap( w );

            l_.unlock();
            return true;
        }
        else
        {
            shared_ptr<T> tmp( p_ );

            l_.unlock();

            tmp.swap( v );
            return false;
        }
    }

public:

#if !defined( BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX ) && !defined( BOOST_NO_CXX11_CONSTEXPR )

    constexpr atomic_shared_ptr() BOOST_SP_NOEXCEPT: l_ BOOST_DETAIL_SPINLOCK_INIT
    {
    }

    atomic_shared_ptr( shared_ptr<T> p ) BOOST_SP_NOEXCEPT
        : p_( std::move( p ) ), l_ BOOST_DETAIL_SPINLOCK_INIT
    {
    }

#else

    atomic_shared_ptr() BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock init = BOOST_DETAIL_SPINLOCK_INIT;
        std::memcpy( &l_, &init, sizeof( init ) );
    }

    atomic_shared_ptr( shared_ptr<T> p ) BOOST_SP_NOEXCEPT
#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )
        : p_( std::move( p ) )
#else
        : p_( p )
#endif
    {
        boost::detail::spinlock init = BOOST_DETAIL_SPINLOCK_INIT;
        std::memcpy( &l_, &init, sizeof( init ) );
    }

#endif

    atomic_shared_ptr& operator=( shared_ptr<T> r ) BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock::scoped_lock lock( l_ );
        p_.swap( r );

        return *this;
    }

    BOOST_CONSTEXPR bool is_lock_free() const BOOST_SP_NOEXCEPT
    {
        return false;
    }

    shared_ptr<T> load() const BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock::scoped_lock lock( l_ );
        return p_;
    }

    template<class M> shared_ptr<T> load( M ) const BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock::scoped_lock lock( l_ );
        return p_;
    }

    operator shared_ptr<T>() const BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock::scoped_lock lock( l_ );
        return p_;
    }

    void store( shared_ptr<T> r ) BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock::scoped_lock lock( l_ );
        p_.swap( r );
    }

    template<class M> void store( shared_ptr<T> r, M ) BOOST_SP_NOEXCEPT
    {
        boost::detail::spinlock::scoped_lock lock( l_ );
        p_.swap( r );
    }

    shared_ptr<T> exchange( shared_ptr<T> r ) BOOST_SP_NOEXCEPT
    {
        {
            boost::detail::spinlock::scoped_lock lock( l_ );
            p_.swap( r );
        }

#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )

        return std::move( r );

#else

        return r;

#endif
    }

    template<class M> shared_ptr<T> exchange( shared_ptr<T> r, M ) BOOST_SP_NOEXCEPT
    {
        {
            boost::detail::spinlock::scoped_lock lock( l_ );
            p_.swap( r );
        }

#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )

        return std::move( r );

#else

        return r;

#endif
    }

    template<class M> bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, M, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, w );
    }

    template<class M> bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, w );
    }

    bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, w );
    }

    template<class M> bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, M, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, w );
    }

    template<class M> bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, w );
    }

    bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, w );
    }

#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES )

    template<class M> bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, M, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, std::move( w ) );
    }

    template<class M> bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, std::move( w ) );
    }

    bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, std::move( w ) );
    }

    template<class M> bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, M, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, std::move( w ) );
    }

    template<class M> bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, M ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, std::move( w ) );
    }

    bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w ) BOOST_SP_NOEXCEPT
    {
        return compare_exchange( v, std::move( w ) );
    }

#endif
};

} // namespace boost

#endif  // #ifndef BOOST_SMART_PTR_ATOMIC_SHARED_PTR_HPP_INCLUDED
