// <mutex> -*- C++ -*-

// Copyright (C) 2003-2017 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

/** @file include/mutex
*  This is a Standard C++ Library header.
*/

#ifndef _GLIBCXX_MUTEX
#define _GLIBCXX_MUTEX 1

#pragma GCC system_header

#if __cplusplus < 201103L
# include <bits/c++0x_warning.h>
#else

#include <tuple>
#include <chrono>
#include <exception>
#include <type_traits>
#include <system_error>
#include <bits/std_mutex.h>
#if ! _GTHREAD_USE_MUTEX_TIMEDLOCK
# include <condition_variable>
# include <thread>
#endif
#ifndef _GLIBCXX_HAVE_TLS
# include <bits/std_function.h>
#endif

#ifdef _GLIBCXX_USE_C99_STDINT_TR1

namespace std _GLIBCXX_VISIBILITY(default)
{
    _GLIBCXX_BEGIN_NAMESPACE_VERSION

        /**
        * @ingroup mutexes
        * @{
        */

#ifdef _GLIBCXX_HAS_GTHREADS

        // Common base class for std::recursive_mutex and std::recursive_timed_mutex
        class __recursive_mutex_base
    {
    protected:
        typedef __gthread_recursive_mutex_t		__native_type;

        __recursive_mutex_base(const __recursive_mutex_base&) = delete;
        __recursive_mutex_base& operator=(const __recursive_mutex_base&) = delete;

#ifdef __GTHREAD_RECURSIVE_MUTEX_INIT
        __native_type  _M_mutex = __GTHREAD_RECURSIVE_MUTEX_INIT;

        __recursive_mutex_base() = default;
#else
        __native_type  _M_mutex;

        __recursive_mutex_base()
        {
            // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may)
            __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION(&_M_mutex);
        }

        ~__recursive_mutex_base()
        { __gthread_recursive_mutex_destroy(&_M_mutex); }
#endif
    };

    /// The standard recursive mutex type.
    class recursive_mutex : private __recursive_mutex_base
    {
    public:
        typedef __native_type* 			native_handle_type;

        recursive_mutex() = default;
        ~recursive_mutex() = default;

        recursive_mutex(const recursive_mutex&) = delete;
        recursive_mutex& operator=(const recursive_mutex&) = delete;

        void
            lock()
        {
            int __e = __gthread_recursive_mutex_lock(&_M_mutex);

            // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
            if (__e)
                __throw_system_error(__e);
        }

        bool
            try_lock() noexcept
        {
            // XXX EINVAL, EAGAIN, EBUSY
            return !__gthread_recursive_mutex_trylock(&_M_mutex);
        }

        void
            unlock()
        {
            // XXX EINVAL, EAGAIN, EBUSY
            __gthread_recursive_mutex_unlock(&_M_mutex);
        }

        native_handle_type
            native_handle() noexcept
        { return &_M_mutex; }
    };

#if _GTHREAD_USE_MUTEX_TIMEDLOCK
    template<typename _Derived>
    class __timed_mutex_impl
    {
    protected:
        typedef chrono::high_resolution_clock 	__clock_t;

        template<typename _Rep, typename _Period>
        bool
            _M_try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
        {
            using chrono::steady_clock;
            auto __rt = chrono::duration_cast<steady_clock::duration>(__rtime);
            if (ratio_greater<steady_clock::period, _Period>())
                ++__rt;
            return _M_try_lock_until(steady_clock::now() + __rt);
        }

        template<typename _Duration>
        bool
            _M_try_lock_until(const chrono::time_point<__clock_t,
                _Duration>& __atime)
        {
            auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
            auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);

            __gthread_time_t __ts = {
                static_cast<std::time_t>(__s.time_since_epoch().count()),
                static_cast<long>(__ns.count())
            };

            return static_cast<_Derived*>(this)->_M_timedlock(__ts);
        }

        template<typename _Clock, typename _Duration>
        bool
            _M_try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
        {
            auto __rtime = __atime - _Clock::now();
            return _M_try_lock_until(__clock_t::now() + __rtime);
        }
    };

    /// The standard timed mutex type.
    class timed_mutex
        : private __mutex_base, public __timed_mutex_impl<timed_mutex>
    {
    public:
        typedef __native_type* 		  	native_handle_type;

        timed_mutex() = default;
        ~timed_mutex() = default;

        timed_mutex(const timed_mutex&) = delete;
        timed_mutex& operator=(const timed_mutex&) = delete;

        void
            lock()
        {
            int __e = __gthread_mutex_lock(&_M_mutex);

            // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
            if (__e)
                __throw_system_error(__e);
        }

        bool
            try_lock() noexcept
        {
            // XXX EINVAL, EAGAIN, EBUSY
            return !__gthread_mutex_trylock(&_M_mutex);
        }

        template <class _Rep, class _Period>
        bool
            try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
        { return _M_try_lock_for(__rtime); }

        template <class _Clock, class _Duration>
        bool
            try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
        { return _M_try_lock_until(__atime); }

        void
            unlock()
        {
            // XXX EINVAL, EAGAIN, EBUSY
            __gthread_mutex_unlock(&_M_mutex);
        }

        native_handle_type
            native_handle() noexcept
        { return &_M_mutex; }

    private:
        friend class __timed_mutex_impl<timed_mutex>;

        bool
            _M_timedlock(const __gthread_time_t& __ts)
        { return !__gthread_mutex_timedlock(&_M_mutex, &__ts); }
    };

    /// recursive_timed_mutex
    class recursive_timed_mutex
        : private __recursive_mutex_base,
        public __timed_mutex_impl<recursive_timed_mutex>
    {
    public:
        typedef __native_type* 			native_handle_type;

        recursive_timed_mutex() = default;
        ~recursive_timed_mutex() = default;

        recursive_timed_mutex(const recursive_timed_mutex&) = delete;
        recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;

        void
            lock()
        {
            int __e = __gthread_recursive_mutex_lock(&_M_mutex);

            // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
            if (__e)
                __throw_system_error(__e);
        }

        bool
            try_lock() noexcept
        {
            // XXX EINVAL, EAGAIN, EBUSY
            return !__gthread_recursive_mutex_trylock(&_M_mutex);
        }

        template <class _Rep, class _Period>
        bool
            try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
        { return _M_try_lock_for(__rtime); }

        template <class _Clock, class _Duration>
        bool
            try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
        { return _M_try_lock_until(__atime); }

        void
            unlock()
        {
            // XXX EINVAL, EAGAIN, EBUSY
            __gthread_recursive_mutex_unlock(&_M_mutex);
        }

        native_handle_type
            native_handle() noexcept
        { return &_M_mutex; }

    private:
        friend class __timed_mutex_impl<recursive_timed_mutex>;

        bool
            _M_timedlock(const __gthread_time_t& __ts)
        { return !__gthread_recursive_mutex_timedlock(&_M_mutex, &__ts); }
    };

#else // !_GTHREAD_USE_MUTEX_TIMEDLOCK

    /// timed_mutex
    class timed_mutex
    {
        mutex		_M_mut;
        condition_variable	_M_cv;
        bool		_M_locked = false;

    public:

        timed_mutex() = default;
        ~timed_mutex() { __glibcxx_assert( !_M_locked ); }

        timed_mutex(const timed_mutex&) = delete;
        timed_mutex& operator=(const timed_mutex&) = delete;

        void
            lock()
        {
            unique_lock<mutex> __lk(_M_mut);
            _M_cv.wait(__lk, [&]{ return !_M_locked; });
            _M_locked = true;
        }

        bool
            try_lock()
        {
            lock_guard<mutex> __lk(_M_mut);
            if (_M_locked)
                return false;
            _M_locked = true;
            return true;
        }

        template<typename _Rep, typename _Period>
        bool
            try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
        {
            unique_lock<mutex> __lk(_M_mut);
            if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
                return false;
            _M_locked = true;
            return true;
        }

        template<typename _Clock, typename _Duration>
        bool
            try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
        {
            unique_lock<mutex> __lk(_M_mut);
            if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
                return false;
            _M_locked = true;
            return true;
        }

        void
            unlock()
        {
            lock_guard<mutex> __lk(_M_mut);
            __glibcxx_assert( _M_locked );
            _M_locked = false;
            _M_cv.notify_one();
        }
    };

    /// recursive_timed_mutex
    class recursive_timed_mutex
    {
        mutex		_M_mut;
        condition_variable	_M_cv;
        thread::id		_M_owner;
        unsigned		_M_count = 0;

        // Predicate type that tests whether the current thread can lock a mutex.
        struct _Can_lock
        {
            // Returns true if the mutex is unlocked or is locked by _M_caller.
            bool
                operator()() const noexcept
            { return _M_mx->_M_count == 0 || _M_mx->_M_owner == _M_caller; }

            const recursive_timed_mutex* _M_mx;
            thread::id _M_caller;
        };

    public:

        recursive_timed_mutex() = default;
        ~recursive_timed_mutex() { __glibcxx_assert( _M_count == 0 ); }

        recursive_timed_mutex(const recursive_timed_mutex&) = delete;
        recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;

        void
            lock()
        {
            auto __id = this_thread::get_id();
            _Can_lock __can_lock{this, __id};
            unique_lock<mutex> __lk(_M_mut);
            _M_cv.wait(__lk, __can_lock);
            if (_M_count == -1u)
                __throw_system_error(EAGAIN); // [thread.timedmutex.recursive]/3
            _M_owner = __id;
            ++_M_count;
        }

        bool
            try_lock()
        {
            auto __id = this_thread::get_id();
            _Can_lock __can_lock{this, __id};
            lock_guard<mutex> __lk(_M_mut);
            if (!__can_lock())
                return false;
            if (_M_count == -1u)
                return false;
            _M_owner = __id;
            ++_M_count;
            return true;
        }

        template<typename _Rep, typename _Period>
        bool
            try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
        {
            auto __id = this_thread::get_id();
            _Can_lock __can_lock{this, __id};
            unique_lock<mutex> __lk(_M_mut);
            if (!_M_cv.wait_for(__lk, __rtime, __can_lock))
                return false;
            if (_M_count == -1u)
                return false;
            _M_owner = __id;
            ++_M_count;
            return true;
        }

        template<typename _Clock, typename _Duration>
        bool
            try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
        {
            auto __id = this_thread::get_id();
            _Can_lock __can_lock{this, __id};
            unique_lock<mutex> __lk(_M_mut);
            if (!_M_cv.wait_until(__lk, __atime, __can_lock))
                return false;
            if (_M_count == -1u)
                return false;
            _M_owner = __id;
            ++_M_count;
            return true;
        }

        void
            unlock()
        {
            lock_guard<mutex> __lk(_M_mut);
            __glibcxx_assert( _M_owner == this_thread::get_id() );
            __glibcxx_assert( _M_count > 0 );
            if (--_M_count == 0)
            {
                _M_owner = {};
                _M_cv.notify_one();
            }
        }
    };

#endif
#endif // _GLIBCXX_HAS_GTHREADS

    template<typename _Lock>
    inline unique_lock<_Lock>
        __try_to_lock(_Lock& __l)
    { return unique_lock<_Lock>{__l, try_to_lock}; }

    template<int _Idx, bool _Continue = true>
    struct __try_lock_impl
    {
        template<typename... _Lock>
        static void
            __do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
        {
            __idx = _Idx;
            auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
            if (__lock.owns_lock())
            {
                const/*expr*/ bool __cont = _Idx + 2 < sizeof...(_Lock);
                using __try_locker = __try_lock_impl<_Idx + 1, __cont>;
                __try_locker::__do_try_lock(__locks, __idx);
                if (__idx == -1)
                    __lock.release();
            }
        }
    };

    template<int _Idx>
    struct __try_lock_impl<_Idx, false>
    {
        template<typename... _Lock>
        static void
            __do_try_lock(tuple<_Lock&...>& __locks, int& __idx)
        {
            __idx = _Idx;
            auto __lock = std::__try_to_lock(std::get<_Idx>(__locks));
            if (__lock.owns_lock())
            {
                __idx = -1;
                __lock.release();
            }
        }
    };

    /** @brief Generic try_lock.
    *  @param __l1 Meets Mutex requirements (try_lock() may throw).
    *  @param __l2 Meets Mutex requirements (try_lock() may throw).
    *  @param __l3 Meets Mutex requirements (try_lock() may throw).
    *  @return Returns -1 if all try_lock() calls return true. Otherwise returns
    *          a 0-based index corresponding to the argument that returned false.
    *  @post Either all arguments are locked, or none will be.
    *
    *  Sequentially calls try_lock() on each argument.
    */
    template<typename _Lock1, typename _Lock2, typename... _Lock3>
    int
        try_lock(_Lock1& __l1, _Lock2& __l2, _Lock3&... __l3)
    {
        int __idx;
        auto __locks = std::tie(__l1, __l2, __l3...);
        __try_lock_impl<0>::__do_try_lock(__locks, __idx);
        return __idx;
    }

    /** @brief Generic lock.
    *  @param __l1 Meets Mutex requirements (try_lock() may throw).
    *  @param __l2 Meets Mutex requirements (try_lock() may throw).
    *  @param __l3 Meets Mutex requirements (try_lock() may throw).
    *  @throw An exception thrown by an argument's lock() or try_lock() member.
    *  @post All arguments are locked.
    *
    *  All arguments are locked via a sequence of calls to lock(), try_lock()
    *  and unlock().  If the call exits via an exception any locks that were
    *  obtained will be released.
    */
    template<typename _L1, typename _L2, typename... _L3>
    void
        lock(_L1& __l1, _L2& __l2, _L3&... __l3)
    {
        while (true)
        {
            using __try_locker = __try_lock_impl<0, sizeof...(_L3) != 0>;
            unique_lock<_L1> __first(__l1);
            int __idx;
            auto __locks = std::tie(__l2, __l3...);
            __try_locker::__do_try_lock(__locks, __idx);
            if (__idx == -1)
            {
                __first.release();
                return;
            }
        }
    }

#if __cplusplus > 201402L
#define __cpp_lib_scoped_lock 201707
    /** @brief A scoped lock type for multiple lockable objects.
    *
    * A scoped_lock controls mutex ownership within a scope, releasing
    * ownership in the destructor.
    */
    template<typename... _MutexTypes>
    class scoped_lock
    {
    public:
        explicit scoped_lock(_MutexTypes&... __m) : _M_devices(std::tie(__m...))
        { std::lock(__m...); }

        explicit scoped_lock(adopt_lock_t, _MutexTypes&... __m) noexcept
            : _M_devices(std::tie(__m...))
        { } // calling thread owns mutex

        ~scoped_lock()
        {
            std::apply([](_MutexTypes&... __m) {
                char __i[] __attribute__((__unused__)) = { (__m.unlock(), 0)... };
                }, _M_devices);
        }

        scoped_lock(const scoped_lock&) = delete;
        scoped_lock& operator=(const scoped_lock&) = delete;

    private:
        tuple<_MutexTypes&...> _M_devices;
    };

    template<>
    class scoped_lock<>
    {
    public:
        explicit scoped_lock() = default;
        explicit scoped_lock(adopt_lock_t) noexcept { }
        ~scoped_lock() = default;

        scoped_lock(const scoped_lock&) = delete;
        scoped_lock& operator=(const scoped_lock&) = delete;
    };

    template<typename _Mutex>
    class scoped_lock<_Mutex>
    {
    public:
        using mutex_type = _Mutex;

        explicit scoped_lock(mutex_type& __m) : _M_device(__m)
        { _M_device.lock(); }

        explicit scoped_lock(adopt_lock_t, mutex_type& __m) noexcept
            : _M_device(__m)
        { } // calling thread owns mutex

        ~scoped_lock()
        { _M_device.unlock(); }

        scoped_lock(const scoped_lock&) = delete;
        scoped_lock& operator=(const scoped_lock&) = delete;

    private:
        mutex_type&  _M_device;
    };
#endif // C++17

#ifdef _GLIBCXX_HAS_GTHREADS
    /// once_flag
    struct once_flag
    {
    private:
        typedef __gthread_once_t __native_type;
        __native_type  _M_once = __GTHREAD_ONCE_INIT;

    public:
        /// Constructor
        constexpr once_flag() noexcept = default;

        /// Deleted copy constructor
        once_flag(const once_flag&) = delete;
        /// Deleted assignment operator
        once_flag& operator=(const once_flag&) = delete;

        template<typename _Callable, typename... _Args>
        friend void
            call_once(once_flag& __once, _Callable&& __f, _Args&&... __args);
    };

#ifdef _GLIBCXX_HAVE_TLS
    extern __thread void* __once_callable;
    extern __thread void (*__once_call)();
#else
    extern function<void()> __once_functor;

    extern void
        __set_once_functor_lock_ptr(unique_lock<mutex>*);

    extern mutex&
        __get_once_mutex();
#endif

    extern "C" void __once_proxy(void);

    /// call_once
    template<typename _Callable, typename... _Args>
    void
        call_once(once_flag& __once, _Callable&& __f, _Args&&... __args)
    {
        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 2442. call_once() shouldn't DECAY_COPY()
        auto __callable = [&] {
            std::__invoke(std::forward<_Callable>(__f),
                std::forward<_Args>(__args)...);
        };
#ifdef _GLIBCXX_HAVE_TLS
        __once_callable = std::__addressof(__callable);
        __once_call = []{ (*(decltype(__callable)*)__once_callable)(); };
#else
        unique_lock<mutex> __functor_lock(__get_once_mutex());
        __once_functor = __callable;
        __set_once_functor_lock_ptr(&__functor_lock);
#endif

        int __e = __gthread_once(&__once._M_once, &__once_proxy);

#ifndef _GLIBCXX_HAVE_TLS
        if (__functor_lock)
            __set_once_functor_lock_ptr(0);
#endif

        if (__e)
            __throw_system_error(__e);
    }
#endif // _GLIBCXX_HAS_GTHREADS

    // @} group mutexes
    _GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif // _GLIBCXX_USE_C99_STDINT_TR1

#endif // C++11

#endif // _GLIBCXX_MUTEX
