// Copyright (C) 2001
// William E. Kempf
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation.  William E. Kempf makes no representations
// about the suitability of this software for any purpose.
// It is provided "as is" without express or implied warranty.

#ifndef BOOST_XLOCK_WEK070601_HPP
#define BOOST_XLOCK_WEK070601_HPP

#include <boost/utility.hpp>
#include <boost/thread/exceptions.hpp>

namespace boost {

class condition;
struct xtime;

    namespace detail { namespace thread {

    template <typename Mutex>
    class lock_ops : private noncopyable
    {
    private:
        lock_ops() { }

    public:
        typedef typename Mutex::cv_state lock_state;

        static void lock(Mutex& m)
        {
            m.do_lock();
        }
        static bool trylock(Mutex& m)
        {
            return m.do_trylock();
        }
        static bool timedlock(Mutex& m, const xtime& xt)
        {
            return m.do_timedlock(xt);
        }
        static void unlock(Mutex& m)
        {
            m.do_unlock();
        }
        static void lock(Mutex& m, lock_state& state)
        {
            m.do_lock(state);
        }
        static void unlock(Mutex& m, lock_state& state)
        {
            m.do_unlock(state);
        }
    };

    template <typename Mutex>
    class scoped_lock : private noncopyable
    {
    public:
        typedef Mutex mutex_type;

        explicit scoped_lock(Mutex& mx, bool initially_locked=true)
            : m_mutex(mx), m_locked(false)
        {
            if (initially_locked) lock();
        }
        ~scoped_lock()
        {
            if (m_locked) unlock();
        }

        void lock()
        {
            if (m_locked) throw lock_error();
            lock_ops<Mutex>::lock(m_mutex);
            m_locked = true;
        }
        void unlock()
        {
            if (!m_locked) throw lock_error();
            lock_ops<Mutex>::unlock(m_mutex);
            m_locked = false;
        }

        bool locked() const { return m_locked; }
        operator const void*() const { return m_locked ? this : 0; }

    private:
        friend class boost::condition;

        Mutex& m_mutex;
        bool m_locked;
    };

    template <typename TryMutex>
    class scoped_try_lock : private noncopyable
    {
    public:
        typedef TryMutex mutex_type;

        explicit scoped_try_lock(TryMutex& mx)
            : m_mutex(mx), m_locked(false)
        {
            try_lock();
        }
        scoped_try_lock(TryMutex& mx, bool initially_locked)
            : m_mutex(mx), m_locked(false)
        {
            if (initially_locked) lock();
        }
        ~scoped_try_lock()
        {
            if (m_locked) unlock();
        }

        void lock()
        {
            if (m_locked) throw lock_error();
            lock_ops<TryMutex>::lock(m_mutex);
            m_locked = true;
        }
        bool try_lock()
        {
            if (m_locked) throw lock_error();
            return (m_locked = lock_ops<TryMutex>::trylock(m_mutex));
        }
        void unlock()
        {
            if (!m_locked) throw lock_error();
            lock_ops<TryMutex>::unlock(m_mutex);
            m_locked = false;
        }

        bool locked() const { return m_locked; }
        operator const void*() const { return m_locked ? this : 0; }

    private:
        friend class boost::condition;

        TryMutex& m_mutex;
        bool m_locked;
    };

    template <typename TimedMutex>
    class scoped_timed_lock : private noncopyable
    {
    public:
        typedef TimedMutex mutex_type;

        scoped_timed_lock(TimedMutex& mx, const xtime& xt)
            : m_mutex(mx), m_locked(false)
        {
            timed_lock(xt);
        }
        scoped_timed_lock(TimedMutex& mx, bool initially_locked)
            : m_mutex(mx), m_locked(false)
        {
            if (initially_locked) lock();
        }
        ~scoped_timed_lock()
        {
            if (m_locked) unlock();
        }

        void lock()
        {
            if (m_locked) throw lock_error();
            lock_ops<TimedMutex>::lock(m_mutex);
            m_locked = true;
        }
        bool timed_lock(const xtime& xt)
        {
            if (m_locked) throw lock_error();
            return (m_locked = lock_ops<TimedMutex>::timedlock(m_mutex, xt));
        }
        void unlock()
        {
            if (!m_locked) throw lock_error();
            lock_ops<TimedMutex>::unlock(m_mutex);
            m_locked = false;
        }

        bool locked() const { return m_locked; }
        operator const void*() const { return m_locked ? this : 0; }

    private:
        friend class boost::condition;

        TimedMutex& m_mutex;
        bool m_locked;
    };

    } // namespace thread
    } // namespace detail
} // namespace boost

// Change Log:
//    8 Feb 01  WEKEMPF Initial version.
//   22 May 01  WEKEMPF Modified to use xtime for time outs.
//   30 Jul 01  WEKEMPF Moved lock types into boost::detail::thread. Renamed some types.
//                      Added locked() methods.

#endif // BOOST_XLOCK_WEK070601_HPP
