//------------------------------------------------------------------------------
// 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