////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software 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.
// The author or Addison-Wesley Longman make no representations about the
//     suitability of this software for any purpose. It is provided "as is"
//     without express or implied warranty.
////////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef LOKI_THREADS_INC_
#define LOKI_THREADS_INC_

// $Id: Threads.h 902 2008-11-10 05:47:06Z rich_sposato $


///  @defgroup  ThreadingGroup Threading
///  Policies to for the threading model:
///
///  - SingleThreaded
///  - ObjectLevelLockable
///  - ClassLevelLockable
///
///  All classes in Loki have configurable threading model.
///
///  The macro LOKI_DEFAULT_THREADING selects the default
///  threading model for certain components of Loki
///  (it affects only default template arguments)
///
///  \par Usage:
///
///  To use a specific threading model define
///
///  - nothing, single-theading is default
///  - LOKI_OBJECT_LEVEL_THREADING for object-level-threading
///  - LOKI_CLASS_LEVEL_THREADING for class-level-threading
///
///  \par Supported platfroms:
///
///  - Windows (windows.h)
///  - POSIX (pthread.h):
///    No recursive mutex support with pthread.
///    This means: calling Lock() on a Loki::Mutex twice from the
///    same thread before unlocking the mutex deadlocks the system.
///    To avoid this redesign your synchronization. See also:
///    http://sourceforge.net/tracker/index.php?func=detail&aid=1516182&group_id=29557&atid=396647


#include <cassert>

#if defined(LOKI_CLASS_LEVEL_THREADING) || defined(LOKI_OBJECT_LEVEL_THREADING)

    #define LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL ::Loki::ClassLevelLockable

    #if defined(LOKI_CLASS_LEVEL_THREADING) && !defined(LOKI_OBJECT_LEVEL_THREADING)
        #define LOKI_DEFAULT_THREADING ::Loki::ClassLevelLockable
    #else
        #define LOKI_DEFAULT_THREADING ::Loki::ObjectLevelLockable
    #endif

    #if defined(WIN32) || defined(WIN64)
        #include <windows.h>
        #define LOKI_WINDOWS_H
    #else
        #include <pthread.h>
        #define LOKI_PTHREAD_H
    #endif

#else

    #define LOKI_DEFAULT_THREADING ::Loki::SingleThreaded
    #define LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL ::Loki::SingleThreaded

#endif

#ifndef LOKI_DEFAULT_MUTEX
#define LOKI_DEFAULT_MUTEX ::Loki::Mutex
#endif

#ifdef LOKI_WINDOWS_H

#define LOKI_THREADS_MUTEX(x)           CRITICAL_SECTION (x);
#define LOKI_THREADS_MUTEX_INIT(x)      ::InitializeCriticalSection (x)
#define LOKI_THREADS_MUTEX_DELETE(x)    ::DeleteCriticalSection (x)
#define LOKI_THREADS_MUTEX_LOCK(x)      ::EnterCriticalSection (x)
#define LOKI_THREADS_MUTEX_UNLOCK(x)    ::LeaveCriticalSection (x)
#define LOKI_THREADS_LONG               LONG
#define LOKI_THREADS_MUTEX_CTOR(x)

#define LOKI_THREADS_ATOMIC_FUNCTIONS                                   \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val) \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval *= val;                                                \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val)  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval /= val;                                                \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicIncrement(volatile IntType& lval)          \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            ++lval;                                                     \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDecrement(volatile IntType& lval)          \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            --lval;                                                     \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static void AtomicAssign(volatile IntType& lval, const IntType val)   \
        { InterlockedExchange(&const_cast<IntType&>(lval), val); }      \
                                                                        \
        static void AtomicAssign(IntType& lval, volatile const IntType& val)  \
        { InterlockedExchange(&lval, val); }                            \
                                                                        \
        static IntType AtomicIncrement(volatile IntType& lval, const IntType compare, bool & matches )  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            ++lval;                                                     \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDecrement(volatile IntType& lval, const IntType compare, bool & matches )  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            --lval;                                                     \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicAdd(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )  \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval += val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicSubtract(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval -= val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                   \
            lval *= val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )   \
        {                                                               \
            ::EnterCriticalSection( &atomic_mutex_ );                    \
            lval /= val;                                                \
            matches = ( lval == compare );                              \
            ::LeaveCriticalSection( &atomic_mutex_ );                    \
            return lval;                                                \
        }

#elif defined(LOKI_PTHREAD_H)


#define LOKI_THREADS_MUTEX(x)           pthread_mutex_t (x);

#define LOKI_THREADS_MUTEX_INIT(x)      ::pthread_mutex_init(x, 0)

// define to 1 to enable recursive mutex support
#if 0
// experimental recursive mutex support
#define LOKI_THREADS_MUTEX_CTOR(x)      : x(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
#else
// no recursive mutex support
#define LOKI_THREADS_MUTEX_CTOR(x)
#endif

#define LOKI_THREADS_MUTEX_DELETE(x)    ::pthread_mutex_destroy (x)
#define LOKI_THREADS_MUTEX_LOCK(x)      ::pthread_mutex_lock (x)
#define LOKI_THREADS_MUTEX_UNLOCK(x)    ::pthread_mutex_unlock (x)
#define LOKI_THREADS_LONG               long

#define LOKI_THREADS_ATOMIC(x)                                           \
                pthread_mutex_lock(&atomic_mutex_);                      \
                x;                                                       \
                pthread_mutex_unlock(&atomic_mutex_)

#define LOKI_THREADS_ATOMIC_FUNCTIONS                                \
    private:                                                         \
        static pthread_mutex_t atomic_mutex_;                        \
    public:                                                          \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval *= val;                                                 \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval /= val;                                                 \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicIncrement(volatile IntType& lval)           \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            ++lval;                                                      \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicDecrement(volatile IntType& lval)           \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            --lval;                                                      \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static void AtomicAssign(volatile IntType& lval, const IntType val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval = val;                                                  \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static void AtomicAssign(IntType& lval, volatile const IntType& val) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            lval = val;                                                  \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicIncrement(volatile IntType& lval, const IntType compare, bool & matches ) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            ++lval;                                                      \
            matches = ( compare == lval );                               \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
                                                                         \
        static IntType AtomicDecrement(volatile IntType& lval, const IntType compare, bool & matches ) \
        {                                                                \
            ::pthread_mutex_lock( &atomic_mutex_ );                      \
            --lval;                                                      \
            matches = ( compare == lval );                               \
            ::pthread_mutex_unlock( &atomic_mutex_ );                    \
            return lval;                                                 \
        }                                                                \
        static IntType AtomicMultiply(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::pthread_mutex_lock( &atomic_mutex_ );                     \
            lval *= val;                                                \
            matches = ( lval == compare );                              \
            ::pthread_mutex_unlock( &atomic_mutex_ );                   \
            return lval;                                                \
        }                                                               \
                                                                        \
        static IntType AtomicDivide(volatile IntType& lval, const IntType val, const IntType compare, bool & matches ) \
        {                                                               \
            ::pthread_mutex_lock( &atomic_mutex_ );                     \
            lval /= val;                                                \
            matches = ( lval == compare );                              \
            ::pthread_mutex_unlock( &atomic_mutex_ );                   \
            return lval;                                                \
        }

#else // single threaded

#define LOKI_THREADS_MUTEX(x)
#define LOKI_THREADS_MUTEX_INIT(x)
#define LOKI_THREADS_MUTEX_DELETE(x)
#define LOKI_THREADS_MUTEX_LOCK(x)
#define LOKI_THREADS_MUTEX_UNLOCK(x)
#define LOKI_THREADS_LONG
#define LOKI_THREADS_MUTEX_CTOR(x)

#endif



namespace Loki
{

    ////////////////////////////////////////////////////////////////////////////////
    ///  \class Mutex
    //
    ///  \ingroup ThreadingGroup
    ///  A simple and portable Mutex.  A default policy class for locking objects.
    ////////////////////////////////////////////////////////////////////////////////

    class Mutex
    {
    public:
        Mutex() LOKI_THREADS_MUTEX_CTOR(mtx_)
        {
            LOKI_THREADS_MUTEX_INIT(&mtx_);
        }
        ~Mutex()
        {
            LOKI_THREADS_MUTEX_DELETE(&mtx_);
        }
        void Lock()
        {
            LOKI_THREADS_MUTEX_LOCK(&mtx_);
        }
        void Unlock()
        {
            LOKI_THREADS_MUTEX_UNLOCK(&mtx_);
        }
    private:
        /// Copy-constructor not implemented.
        Mutex(const Mutex &);
        /// Copy-assignement operator not implemented.
        Mutex & operator = (const Mutex &);
        LOKI_THREADS_MUTEX(mtx_)
    };


     ////////////////////////////////////////////////////////////////////////////////
    ///  \class SingleThreaded
    ///
    ///  \ingroup ThreadingGroup
    ///  Implementation of the ThreadingModel policy used by various classes
    ///  Implements a single-threaded model; no synchronization
    ////////////////////////////////////////////////////////////////////////////////
    template <class Host, class MutexPolicy = LOKI_DEFAULT_MUTEX>
    class SingleThreaded
    {
    public:
        /// \struct Lock
        /// Dummy Lock class
        struct Lock
        {
            Lock() {}
            explicit Lock(const SingleThreaded&) {}
            explicit Lock(const SingleThreaded*) {}
        };

        typedef Host VolatileType;

        typedef int IntType;

        static IntType AtomicAdd(volatile IntType& lval, const IntType val)
        { return lval += val; }

        static IntType AtomicSubtract(volatile IntType& lval, const IntType val)
        { return lval -= val; }

        static IntType AtomicMultiply(volatile IntType& lval, const IntType val)
        { return lval *= val; }

        static IntType AtomicDivide(volatile IntType& lval, const IntType val)
        { return lval /= val; }

        static IntType AtomicIncrement(volatile IntType& lval)
        { return ++lval; }

        static IntType AtomicDecrement(volatile IntType& lval)
        { return --lval; }

        static void AtomicAssign(volatile IntType & lval, const IntType val)
        { lval = val; }

        static void AtomicAssign(IntType & lval, volatile IntType & val)
        { lval = val; }

        static IntType AtomicAdd(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )
        {
            lval += val;
            matches = ( lval == compare );
            return lval;
        }

        static IntType AtomicSubtract(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )
        {
            lval -= val;
            matches = ( lval == compare );
            return lval;
        }

        static IntType AtomicMultiply(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )
        {
            lval *= val;
            matches = ( lval == compare );
            return lval;
        }

        static IntType AtomicDivide(volatile IntType& lval, const IntType val, const IntType compare, bool & matches )
        {
            lval /= val;
            matches = ( lval == compare );
            return lval;
        }

        static IntType AtomicIncrement(volatile IntType& lval, const IntType compare, bool & matches )
        {
            ++lval;
            matches = ( lval == compare );
            return lval;
        }

        static IntType AtomicDecrement(volatile IntType& lval, const IntType compare, bool & matches )
        {
            --lval;
            matches = ( lval == compare );
            return lval;
        }

    };


#if defined(LOKI_WINDOWS_H) || defined(LOKI_PTHREAD_H)

    ////////////////////////////////////////////////////////////////////////////////
    ///  \class ObjectLevelLockable
    ///
    ///  \ingroup ThreadingGroup
    ///  Implementation of the ThreadingModel policy used by various classes
    ///  Implements a object-level locking scheme
    ////////////////////////////////////////////////////////////////////////////////
    template < class Host, class MutexPolicy = LOKI_DEFAULT_MUTEX >
    class ObjectLevelLockable
    {
        mutable MutexPolicy mtx_;

    public:
        ObjectLevelLockable() : mtx_() {}

        ObjectLevelLockable(const ObjectLevelLockable&) : mtx_() {}

        ~ObjectLevelLockable() {}

        class Lock;
        friend class Lock;

        ///  \struct Lock
        ///  Lock class to lock on object level
        class Lock
        {
        public:

            /// Lock object
            explicit Lock(const ObjectLevelLockable& host) : host_(host)
            {
                host_.mtx_.Lock();
            }

            /// Lock object
            explicit Lock(const ObjectLevelLockable* host) : host_(*host)
            {
                host_.mtx_.Lock();
            }

            /// Unlock object
            ~Lock()
            {
                host_.mtx_.Unlock();
            }

        private:
            /// private by design of the object level threading
            Lock();
            Lock(const Lock&);
            Lock& operator=(const Lock&);
            const ObjectLevelLockable& host_;
        };

        typedef volatile Host VolatileType;

        typedef LOKI_THREADS_LONG IntType;

        LOKI_THREADS_ATOMIC_FUNCTIONS

    };

#ifdef LOKI_PTHREAD_H
    template <class Host, class MutexPolicy>
    pthread_mutex_t ObjectLevelLockable<Host, MutexPolicy>::atomic_mutex_ = PTHREAD_MUTEX_INITIALIZER;
#endif

    ////////////////////////////////////////////////////////////////////////////////
    ///  \class ClassLevelLockable
    ///
    ///  \ingroup ThreadingGroup
    ///  Implementation of the ThreadingModel policy used by various classes
    ///  Implements a class-level locking scheme
    ////////////////////////////////////////////////////////////////////////////////
    template <class Host, class MutexPolicy = LOKI_DEFAULT_MUTEX >
    class ClassLevelLockable
    {
        struct Initializer
        {
            bool init_;
            MutexPolicy mtx_;

            Initializer() : init_(false), mtx_()
            {
                init_ = true;
            }

            ~Initializer()
            {
                assert(init_);
            }
        };

        static Initializer initializer_;

    public:

        class Lock;
        friend class Lock;

        ///  \struct Lock
        ///  Lock class to lock on class level
        class Lock
        {
        public:

            /// Lock class
            Lock()
            {
                assert(initializer_.init_);
                initializer_.mtx_.Lock();
            }

            /// Lock class
            explicit Lock(const ClassLevelLockable&)
            {
                assert(initializer_.init_);
                initializer_.mtx_.Lock();
            }

            /// Lock class
            explicit Lock(const ClassLevelLockable*)
            {
                assert(initializer_.init_);
                initializer_.mtx_.Lock();
            }

            /// Unlock class
            ~Lock()
            {
                assert(initializer_.init_);
                initializer_.mtx_.Unlock();
            }

        private:
            Lock(const Lock&);
            Lock& operator=(const Lock&);
        };

        typedef volatile Host VolatileType;

        typedef LOKI_THREADS_LONG IntType;

        LOKI_THREADS_ATOMIC_FUNCTIONS

    };

#ifdef LOKI_PTHREAD_H
    template <class Host, class MutexPolicy>
    pthread_mutex_t ClassLevelLockable<Host, MutexPolicy>::atomic_mutex_ = PTHREAD_MUTEX_INITIALIZER;
#endif

    template < class Host, class MutexPolicy >
    typename ClassLevelLockable< Host, MutexPolicy >::Initializer
    ClassLevelLockable< Host, MutexPolicy >::initializer_;

#endif // #if defined(LOKI_WINDOWS_H) || defined(LOKI_PTHREAD_H)

} // namespace Loki


#endif // end file guardian

/* vim:set et sw=2 ts=2: */
