//------------------------------------------------------------------------------
// emThread.h
//
// Copyright (C) 2009-2010,2016-2017 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program 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 version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emThread_h
#define emThread_h

#ifndef emStd2_h
#include <emCore/emStd2.h>
#endif

class emThreadPrivate;
struct emThreadEventReceiver;


//==============================================================================
//================================= emThreadId =================================
//==============================================================================

typedef emUInt64 emThreadId;


//==============================================================================
//================================== emThread ==================================
//==============================================================================

class emThread : public emUncopyable {

public:

        // Class for a thread of program execution.
        //
        // Some hints on multi-threading:
        // - Where not stated otherwise, all classes and functions of emCore are
        //   thread-reentrant but not thread-safe. This means that the classes
        //   and functions can be used by multiple threads simultaneously only
        //   if no shared data is accessed. Yes, even emContext is not
        //   thread-safe.
        // - The objects of container classes like emString, emArray, emList and
        //   emImage are sharing data implicitly. You cannot protect that data
        //   with a mutex. Instead, these classes have a method named
        //   MakeNonShared which must always be called before giving the object
        //   to another thread.
        // - Multi-threading provides many pitfalls, and the correctness of
        //   multi-threaded programs cannot be proved by testing very well.
        // - Alternative concepts are: emEngine, emProcess.

        emThread();
                // Construct a thread object where the thread is not yet
                // running.

        virtual ~emThread();
                // If the thread is still running, WaitForTermination() is
                // called.

        void Start(void * arg);
                // This is a short cut for Start(NULL,arg). Should only be used
                // if Run has been overloaded.

        void Start(int (* func)(void *), void * arg);
                // Start this thread. The thread calls the method Run, and the
                // default Implementation of that method calls the function
                // func. arg is a custom pointer forwarded to Run and func.

        static void StartUnmanaged(int (* func)(void *), void * arg);
                // This function is like the method Start, but the new thread is
                // not managed by an emThread object and the function func is
                // called directly instead of Run. Yes, the return value of func
                // has no meaning and exists only for compatibility with managed
                // threads.

        bool WaitForTermination(unsigned timeoutMS=UINT_MAX);
                // Wait for the thread to terminate.
                // Arguments:
                //   timeoutMS - Time-out in milliseconds. UINT_MAX means
                //               infinite.
                // Returns:
                //   true if the thread has terminated (or never started), or
                //   false on time-out.

        bool IsRunning();
                // Whether the thread has been started and not yet exited.

        int GetExitStatus() const;
                // Get the return value of a terminated thread.

        emThreadId GetThreadId() const;
                // Get the identity number of this thread, if running.

        static emThreadId GetCurrentThreadId();
                // Get the identity number of the calling thread.

        static void ExitCurrentThread(int exitStatus);
                // Exit the calling thread.

        static int GetHardwareThreadCount();
                // Maximum number of threads the hardware can run concurrently.

protected:

        virtual int Run(void * arg);
                // The default implementation calls the function given with the
                // Start method.

private:

        friend class emThreadPrivate;
        emThreadPrivate * P;
};

inline void emThread::Start(void * arg)
{
        Start(NULL,arg);
}

inline bool emThread::IsRunning()
{
        return !WaitForTermination(0);
}


//==============================================================================
//============================ emThreadMutexLocker =============================
//==============================================================================

template <class OBJ> class emThreadMutexLocker : public emUncopyable {

public:

        // This is a template class for a locker which simply locks a mutex for
        // the time the locker exists. The template parameter OBJ describes the
        // class of the mutex.

        emThreadMutexLocker(OBJ & mutex);
                // Calls Lock() on the given mutex.
                // Arguments:
                //   mutex - The mutex to be locked by this locker.

        ~emThreadMutexLocker();
                // Calls Unlock() on the mutex.

        OBJ & GetMutex() const;
                // Get the mutex which is locked by this locker.

private:

        OBJ & Mutex;
};

template <class OBJ> inline emThreadMutexLocker<OBJ>::emThreadMutexLocker(
        OBJ & mutex
)
        : Mutex(mutex)
{
        Mutex.Lock();
}

template <class OBJ> inline emThreadMutexLocker<OBJ>::~emThreadMutexLocker()
{
        Mutex.Unlock();
}

template <class OBJ> inline OBJ & emThreadMutexLocker<OBJ>::GetMutex() const
{
        return Mutex;
}


//==============================================================================
//============================= emThreadMiniMutex ==============================
//==============================================================================

class emThreadMiniMutex : public emUncopyable {

public:

        // Class for a mutual exclusion variable with minimum costs. It could
        // also be called a "spin lock". This type of mutex may perform some
        // kind of busy waiting, and it may be unfair. Therefore it should be
        // used for very short critical sections only, so that it is very
        // unlikely that a thread really has to wait. This mutex also does not
        // support recursive locks.

        emThreadMiniMutex();
        ~emThreadMiniMutex();

        void Lock();
                // Lock this mutex. Only one thread can have the mutex locked at
                // a time. Therefore this method may block in order to wait for
                // unlocked state. It may be a kind of busy waiting (yielding to
                // another thread again and again).

        void Unlock();
                // Unlock this mutex.

        typedef emThreadMutexLocker<emThreadMiniMutex> Locker;
                // A locker class for this mutex class.

private:

        union {
                emInt64 Val;
                void * Ptr;
        };
};


//==============================================================================
//=============================== emThreadEvent ================================
//==============================================================================

class emThreadEvent : public emUncopyable {

public:

        // Class for an unary event that can be used for thread communication.
        // It is much like a classic semaphore, but the methods are named "Send"
        // and "Receive" instead of "Increment" and "Decrement".

        emThreadEvent();
                // Construct with no pending events.

        emThreadEvent(int count);
                // Construct with an initial number of pending event, or, if
                // count is negative, with an initial phantom receiver. See
                // SetCount.

        ~emThreadEvent();
                // Destructor.

        emInt64 Send(emInt64 n=1);
                // Send this event n times. If one or more threads are blocked
                // in Receive, they are served accordingly. A negative n means
                // to decrease the internal counter of pending events (which can
                // be negative) - it acts like a phantom receiver which has
                // precedence over all the other receivers.
                // Arguments:
                //   n         - How many events to send. A negative n acts like
                //               a phantom receiver.
                // Returns:
                //   Same as GetCount.

        bool Receive(emInt64 n=1, unsigned timeoutMS=UINT_MAX);
                // Receive this event n times. If there are at least n pending
                // events, this method returns immediately. Otherwise this
                // thread is added to an internal queue and it sleeps until it
                // is removed from the queue by Send, or until the time-out
                // elapses.
                // Arguments:
                //   n         - How many events to receive. A negative n acts
                //               exactly like Send(-n).
                //   timeoutMS - Time-out in milliseconds. UINT_MAX means
                //               infinite.
                // Returns:
                //   true if the events have been received, or false on
                //   time-out. On time-out, absolute no event is removed from
                //   the internal counter.

        emInt64 GetCount() const;
                // Get the number of pending sendings of this event. A negative
                // value indicates waiting receivers.

        void SetCount(emInt64 count);
                // Same as Send(count-GetCount()), but atomically.

        void Clear();
                // Same as SetCount(0).

private:

        void UpdateReceivers();

        emThreadMiniMutex Mutex;
        emInt64 Count;
        emThreadEventReceiver * Ring;
};

inline void emThreadEvent::Clear()
{
        SetCount(0);
}


//==============================================================================
//=============================== emThreadMutex ================================
//==============================================================================

class emThreadMutex : private emUncopyable {

public:

        // Class for a normal mutual exclusion variable. Its properties are:
        // - The lock methods support a time-out.
        // - Waiting is non-busy.
        // - Waiting is fair (first come, first serve).
        // - There are methods for solving the readers/writers problem. This
        //   means, the mutex can be locked by either a single thread for
        //   read/write access, or by one or more threads for read-only
        //   access. If you don't need that, simply don't use the *ReadOnly
        //   methods.
        // - This mutex does not support recursive locks. A thread would
        //   dead-lock when trying to lock the mutex more than once (except when
        //   locking for read-only access).

        emThreadMutex();
        ~emThreadMutex();

        bool Lock(unsigned timeoutMS=UINT_MAX);
                // Lock this mutex (for read/write access). Only one thread can
                // have the mutex locked this way at a time, without any
                // read-access locks in parallel. Therefore this method may
                // block in order to wait for unlocked state.
                // Arguments:
                //   timeoutMS - Time-out in milliseconds. UINT_MAX means
                //               infinite.
                // Returns:
                //   true if the mutex has been locked, false on time-out.

        void Unlock();
                // Unlock this mutex (from read/write access).

        bool IsLocked() const;
                // Whether this mutex is currently locked (for read/write
                // access).

        bool LockReadOnly(unsigned timeoutMS=UINT_MAX);
                // Lock this mutex for read-only access. Multiple threads
                // can have locked the mutex this way simultaneously. If a
                // thread has locked the mutex for read/write access (with
                // Lock), this method may block in order to wait for unlocked
                // state.
                // Arguments:
                //   timeoutMS - Time-out in milliseconds. UINT_MAX means
                //               infinite.
                // Returns:
                //   true if the mutex has been locked, false on time-out.

        void UnlockReadOnly();
                // Unlock this mutex from read/write access.

        bool IsLockedAnyhow() const;
                // Whether this mutex is currently locked for any access.

        typedef emThreadMutexLocker<emThreadMutex> Locker;
                // A locker class for this mutex class.

        class ReadOnlyLocker : public emUncopyable {
        public:
                // A read-only locker class for this mutex class.
                ReadOnlyLocker(emThreadMutex & mutex);
                ~ReadOnlyLocker();
                emThreadMutex & GetMutex() const;
        private:
                emThreadMutex & Mutex;
        };

private:

        enum { MAX_COUNT = 2147483647 };
        emThreadEvent Event;
};

inline bool emThreadMutex::Lock(unsigned timeoutMS)
{
        return Event.Receive(MAX_COUNT,timeoutMS);
}

inline bool emThreadMutex::IsLocked() const
{
        return Event.GetCount()<=0;
}

inline bool emThreadMutex::LockReadOnly(unsigned timeoutMS)
{
        return Event.Receive(1,timeoutMS);
}

inline bool emThreadMutex::IsLockedAnyhow() const
{
        return Event.GetCount()<MAX_COUNT;
}

inline emThreadMutex::ReadOnlyLocker::ReadOnlyLocker(emThreadMutex & mutex)
        : Mutex(mutex)
{
        Mutex.LockReadOnly();
}

inline emThreadMutex::ReadOnlyLocker::~ReadOnlyLocker()
{
        Mutex.UnlockReadOnly();
}

inline emThreadMutex & emThreadMutex::ReadOnlyLocker::GetMutex() const
{
        return Mutex;
}


//==============================================================================
//=========================== emThreadRecursiveMutex ===========================
//==============================================================================

class emThreadRecursiveMutex : public emUncopyable {

public:

        // This is just like emThreadMutex, but it supports recursive locks, and
        // therefore it lacks the readers/writers solution (having the whole
        // luxury would be very costly).

        emThreadRecursiveMutex();
        ~emThreadRecursiveMutex();

        bool Lock(unsigned timeoutMS=UINT_MAX);
        void Unlock();
        bool IsLocked() const;

        typedef emThreadMutexLocker<emThreadRecursiveMutex> Locker;

private:

        emThreadEvent Event;
        emThreadMiniMutex Mutex;
        emThreadId ThreadId;
        int LockCount;
};

inline bool emThreadRecursiveMutex::IsLocked() const
{
        return Event.GetCount()<=0;
}


#endif