Paul Davis
1de00ab6bb
Code builds, runs and functions. Full code review still pending, and some possibly changes to organization of code within the backend is possible
411 lines
13 KiB
C++
411 lines
13 KiB
C++
/*
|
|
Copyright (C) 2013 Waves Audio Ltd.
|
|
|
|
This program 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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 for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
#ifndef __WCThreadSafe_h_
|
|
#define __WCThreadSafe_h_
|
|
|
|
/* Copy to include
|
|
#include "Threads/WCThreadSafe.h"
|
|
*/
|
|
|
|
//
|
|
// * WCThreadSafe.h (used to be called XPlatformOSServices.hpp)
|
|
// *
|
|
// * Consistent C++ interfaces to common Operating System services.
|
|
// *
|
|
// *
|
|
// *
|
|
// *
|
|
// * Created 2004-December-13 by Udi Barzilai as XPlatformOSServices.hpp
|
|
// * Moved to WCThreadSafe.h by Shai 26/10/2005
|
|
// * 26/10/2005: ThreadMutex now inhetites from ThreadMutexInited
|
|
// * namespace changed to wvThread
|
|
|
|
#include "WavesPublicAPI/wstdint.h"
|
|
#include <string>
|
|
|
|
#include "BasicTypes/WUDefines.h"
|
|
|
|
#if defined(__linux__) || defined(__MACOS__)
|
|
#define XPLATFORMOSSERVICES_UNIX 1
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#define XPLATFORMOSSERVICES_WIN32 1
|
|
#endif
|
|
|
|
#if XPLATFORMOSSERVICES_WIN32
|
|
#define XPLATFORMTHREADS_WINDOWS 1
|
|
#elif XPLATFORMOSSERVICES_UNIX
|
|
#define XPLATFORMTHREADS_POSIX 1
|
|
#endif
|
|
namespace wvNS {
|
|
typedef uint32_t WTThreadSafetyType;
|
|
const WTThreadSafetyType kNoThreadSafetyNeeded = 0;
|
|
const WTThreadSafetyType kpthreadsmutexThreadSafety = 1;
|
|
|
|
|
|
namespace wvThread
|
|
{
|
|
//#include "BasicTypes/WavesAPISetAligment.h"
|
|
//Packing affects the layout of classes, and commonly, if packing changes across header files, there can be problems.
|
|
#ifdef _WINDOWS
|
|
#pragma pack(push)
|
|
#pragma pack(8)
|
|
#endif
|
|
|
|
#ifdef __MACOS__
|
|
#ifdef __GNUC__
|
|
#pragma pack(push, 8)
|
|
#endif
|
|
#endif
|
|
|
|
//--------------------------------------------------------
|
|
typedef int32_t timediff; // in microseconds
|
|
static const timediff ktdOneSecond = 1000*1000;
|
|
//--------------------------------------------------------
|
|
class timestamp
|
|
{
|
|
protected:
|
|
typedef uint32_t tickcount;
|
|
tickcount m_nMicroseconds; // may wrap around
|
|
static const tickcount ms_knWraparoundThreshold = ~tickcount(0) ^ (~tickcount(0)>>1); // half the range
|
|
|
|
public:
|
|
timestamp() : m_nMicroseconds(0) { /* uninitialized */ }
|
|
timestamp(const timestamp &_ts) : m_nMicroseconds(_ts.m_nMicroseconds) {}
|
|
timestamp &operator=(const timestamp &_rhs) { m_nMicroseconds = _rhs.m_nMicroseconds; return *this; }
|
|
explicit timestamp(tickcount _i) : m_nMicroseconds(_i) {}
|
|
uint32_t ticks() const { return m_nMicroseconds; }
|
|
timediff operator-(timestamp _rhs) const { return timediff(m_nMicroseconds-_rhs.m_nMicroseconds); }
|
|
timestamp & operator+=(timediff _t) { m_nMicroseconds+=_t; return *this; }
|
|
timestamp & operator-=(timediff _t) { m_nMicroseconds-=_t; return *this; }
|
|
timestamp operator+(timediff _t) const { return timestamp(m_nMicroseconds+_t); }
|
|
timestamp operator-(timediff _t) const { return timestamp(m_nMicroseconds-_t); }
|
|
bool operator==(timestamp _rhs) const { return m_nMicroseconds==_rhs.m_nMicroseconds; }
|
|
bool operator!=(timestamp _rhs) const { return m_nMicroseconds!=_rhs.m_nMicroseconds; }
|
|
bool operator< (timestamp _rhs) const { return m_nMicroseconds-_rhs.m_nMicroseconds >= ms_knWraparoundThreshold; }
|
|
static timestamp null() { return timestamp(0); }
|
|
bool is_null() const { return m_nMicroseconds==0; }
|
|
};
|
|
//--------------------------------------------------------
|
|
#ifdef __MACOS__
|
|
bool FindNetInterfaceByIPAddress(const char *sIP, char *sInterface);
|
|
#endif // MACOS
|
|
//--------------------------------------------------------
|
|
timestamp now();
|
|
//--------------------------------------------------------
|
|
DllExport void sleep(timediff);
|
|
DllExport void sleep_milliseconds(unsigned int nMillisecs);
|
|
//--------------------------------------------------------
|
|
void yield();
|
|
//--------------------------------------------------------
|
|
|
|
|
|
|
|
typedef uintptr_t os_dependent_handle_type;
|
|
|
|
//--------------------------------------------------------
|
|
typedef int ThreadFunctionReturnType;
|
|
typedef void * ThreadFunctionArgument;
|
|
//--------------------------------------------------------
|
|
typedef ThreadFunctionReturnType (ThreadFunction)(ThreadFunctionArgument);
|
|
//--------------------------------------------------------
|
|
class ThreadHandle
|
|
{
|
|
public:
|
|
class OSDependent;
|
|
protected:
|
|
uintptr_t m_oshandle; // hopefully this is good enough for all systems
|
|
public:
|
|
static const ThreadHandle Invalid;
|
|
protected:
|
|
ThreadHandle(uintptr_t n) : m_oshandle(n) {}
|
|
public:
|
|
ThreadHandle() : m_oshandle(Invalid.m_oshandle) {}
|
|
bool is_invalid() const { return !m_oshandle || m_oshandle==Invalid.m_oshandle; }
|
|
};
|
|
//--------------------------------------------------------
|
|
class ThreadPriority
|
|
{
|
|
public: enum value { BelowNormal=1, Normal=2, AboveNormal=3, TimeCritical=4 };
|
|
protected: value m_value;
|
|
public: ThreadPriority(value v) : m_value(v) {}
|
|
public: operator value() const { return m_value; }
|
|
};
|
|
//--------------------------------------------------------
|
|
void SetMyThreadPriority(ThreadPriority);
|
|
//--------------------------------------------------------
|
|
ThreadHandle StartThread(ThreadFunction, ThreadFunctionArgument, ThreadPriority=ThreadPriority::Normal);
|
|
bool JoinThread(ThreadHandle, ThreadFunctionReturnType * = 0);
|
|
bool KillThread(ThreadHandle); // use only for abnormal termination
|
|
void Close(ThreadHandle); // should be called once for every handle obtained from StartThread.
|
|
//--------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------
|
|
class DllExport noncopyableobject
|
|
{
|
|
protected:
|
|
noncopyableobject() {}
|
|
private:
|
|
noncopyableobject(const noncopyableobject &);
|
|
noncopyableobject & operator=(const noncopyableobject &);
|
|
};
|
|
//--------------------------------------------------------
|
|
|
|
|
|
//--------------------------------------------------------
|
|
// Thread Mutex class that needs to be explicitly initialized
|
|
class DllExport ThreadMutexInited : public noncopyableobject
|
|
{
|
|
protected:
|
|
class OSDependentMutex;
|
|
OSDependentMutex* m_osdmutex;
|
|
|
|
public:
|
|
ThreadMutexInited();
|
|
~ThreadMutexInited();
|
|
|
|
void init();
|
|
void uninit();
|
|
inline bool is_init() { return 0 != m_osdmutex; }
|
|
void obtain();
|
|
bool tryobtain();
|
|
void release();
|
|
|
|
private:
|
|
ThreadMutexInited(const ThreadMutexInited&); // cannot be copied
|
|
ThreadMutexInited& operator=(const ThreadMutexInited&); // cannot be copied
|
|
|
|
public:
|
|
class lock : public noncopyableobject
|
|
{
|
|
protected:
|
|
ThreadMutexInited &m_mutex;
|
|
public:
|
|
inline lock(ThreadMutexInited &mtx) : m_mutex(mtx) { m_mutex.obtain(); }
|
|
inline ~lock() { m_mutex.release(); }
|
|
};
|
|
class trylock : public noncopyableobject
|
|
{
|
|
protected:
|
|
ThreadMutexInited &m_mutex;
|
|
bool m_bObtained;
|
|
public:
|
|
inline trylock(ThreadMutexInited &mtx) : m_mutex(mtx), m_bObtained(false) { m_bObtained = m_mutex.tryobtain(); }
|
|
inline ~trylock() { if (m_bObtained) m_mutex.release(); }
|
|
inline operator bool() const { return m_bObtained; }
|
|
};
|
|
};
|
|
//--------------------------------------------------------
|
|
|
|
// Thread Mutex class that is automatically initialized
|
|
class ThreadMutex : public ThreadMutexInited
|
|
{
|
|
public:
|
|
ThreadMutex() {init();}
|
|
};
|
|
|
|
//--------------------------------------------------------
|
|
class DllExport ThreadConditionSignal : public noncopyableobject
|
|
{
|
|
protected:
|
|
class OSDependentObject;
|
|
OSDependentObject &m_osdepobj;
|
|
|
|
protected:
|
|
void obtain_mutex();
|
|
bool tryobtain_mutex();
|
|
void release_mutex();
|
|
|
|
public:
|
|
class lock : public noncopyableobject
|
|
{
|
|
protected:
|
|
ThreadConditionSignal &m_tcs;
|
|
public:
|
|
lock(ThreadConditionSignal &tcs) : m_tcs(tcs) { m_tcs.obtain_mutex(); }
|
|
~lock() { m_tcs.release_mutex(); }
|
|
};
|
|
class trylock : public noncopyableobject
|
|
{
|
|
protected:
|
|
ThreadConditionSignal &m_tcs;
|
|
bool m_bObtained;
|
|
public:
|
|
trylock(ThreadConditionSignal &tcs) : m_tcs(tcs), m_bObtained(false) { m_bObtained = m_tcs.tryobtain_mutex(); }
|
|
~trylock() { if (m_bObtained) m_tcs.release_mutex(); }
|
|
operator bool() const { return m_bObtained; }
|
|
};
|
|
|
|
public:
|
|
ThreadConditionSignal();
|
|
~ThreadConditionSignal();
|
|
|
|
// IMPORTANT: All of the functions below MUST be called ONLY while holding a lock for this object !!!
|
|
void await_condition();
|
|
bool await_condition(timediff tdTimeout);
|
|
void signal_condition_single();
|
|
void signal_condition_broadcast();
|
|
};
|
|
//--------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------
|
|
// A doorbell is a simple communication mechanism that allows
|
|
// one thread two wake another when there is some work to be done.
|
|
// The signal is 'clear on read'. This class is not intended for
|
|
// multi-way communication (i.e. more than two threads).
|
|
//#define XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR (!XPLATFORMTHREADS_WINDOWS && !XPLATFORMOSSERVICES_MACOS)
|
|
#ifdef XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR
|
|
#undef XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR
|
|
#endif
|
|
#define XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR 1
|
|
#if XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR
|
|
class doorbell_type
|
|
{
|
|
protected:
|
|
ThreadConditionSignal m_signal;
|
|
bool m_rang;
|
|
protected:
|
|
template<bool wait_forever> bool wait_for_ring_internal(timediff timeout)
|
|
{// mutex
|
|
ThreadConditionSignal::lock guard(m_signal);
|
|
if (!m_rang)
|
|
{
|
|
if (wait_forever)
|
|
{
|
|
m_signal.await_condition();
|
|
}
|
|
else
|
|
{
|
|
m_signal.await_condition(timeout);
|
|
}
|
|
}
|
|
const bool rang = m_rang;
|
|
m_rang = false;
|
|
return rang;
|
|
}// mutex
|
|
|
|
public:
|
|
doorbell_type() : m_rang(false) {}
|
|
inline ~doorbell_type() {}
|
|
inline void ring()
|
|
{// mutex
|
|
ThreadConditionSignal::lock guard(m_signal);
|
|
m_rang = true;
|
|
m_signal.signal_condition_single();
|
|
}// mutex
|
|
inline bool wait_for_ring() { return wait_for_ring_internal<true>(0); }
|
|
inline bool wait_for_ring(timediff timeout) { return wait_for_ring_internal<false>(timeout); }
|
|
};
|
|
#else
|
|
class doorbell_type : public noncopyableobject
|
|
{
|
|
protected:
|
|
os_dependent_handle_type m_os_dependent_handle;
|
|
protected:
|
|
template<bool wait_forever> bool wait_for_ring_internal(timediff);
|
|
public:
|
|
doorbell_type();
|
|
~doorbell_type();
|
|
void ring();
|
|
bool wait_for_ring();
|
|
bool wait_for_ring(timediff timeout);
|
|
};
|
|
#endif // XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR
|
|
//--------------------------------------------------------
|
|
|
|
//---------------------------------------------------------------
|
|
class DllExport WCThreadRef // Class which holds the threadRef, DWORD in Windows and pthread_t in POSIX (Mac, Unix)
|
|
{
|
|
public:
|
|
class OSDependent; // the class which contains the OS Dependent implementation
|
|
|
|
WCThreadRef() : m_osThreadRef(0) {}
|
|
bool is_invalid() const { return m_osThreadRef == 0;}
|
|
|
|
operator uintptr_t() const {return m_osThreadRef;}
|
|
|
|
protected:
|
|
uintptr_t m_osThreadRef;
|
|
WCThreadRef(uintptr_t n) : m_osThreadRef(n) {}
|
|
|
|
friend DllExport bool operator==(const WCThreadRef& first, const WCThreadRef& second);
|
|
friend DllExport bool operator!=(const WCThreadRef& first, const WCThreadRef& second);
|
|
friend DllExport bool operator<(const WCThreadRef& first, const WCThreadRef& second);
|
|
friend DllExport bool operator>(const WCThreadRef& first, const WCThreadRef& second);
|
|
};
|
|
|
|
DllExport WCThreadRef GetCurrentThreadRef(); // getting the current thread reference - cross-platform implemented
|
|
bool IsThreadExists(const WCThreadRef& threadRef); // correct to the very snapshot of time of execution
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
class DllExport WCAtomicLock
|
|
{
|
|
public:
|
|
WCAtomicLock() : m_the_lock(0) {}
|
|
bool obtain(const uint32_t in_num_trys = 1);
|
|
void release();
|
|
private:
|
|
int32_t m_the_lock;
|
|
};
|
|
|
|
//#include "BasicTypes/WavesAPIResetAligment.h"
|
|
#ifdef _WINDOWS
|
|
#pragma pack(pop)
|
|
#endif
|
|
|
|
#ifdef __MACOS__
|
|
#ifdef __GNUC__
|
|
#pragma pack(pop)
|
|
#endif
|
|
#endif
|
|
|
|
class WCStThreadMutexLocker
|
|
{
|
|
public:
|
|
WCStThreadMutexLocker(wvNS::wvThread::ThreadMutexInited& in_mutex) :
|
|
m_mutex(in_mutex)
|
|
{
|
|
m_mutex.obtain();
|
|
}
|
|
|
|
~WCStThreadMutexLocker()
|
|
{
|
|
m_mutex.release();
|
|
}
|
|
protected:
|
|
wvNS::wvThread::ThreadMutexInited& m_mutex;
|
|
WCStThreadMutexLocker(const WCStThreadMutexLocker&);
|
|
WCStThreadMutexLocker& operator=(const WCStThreadMutexLocker&);
|
|
};
|
|
|
|
} // namespace wvThread
|
|
|
|
|
|
} //namespace wvNS {
|
|
#endif // #ifndef __WCThreadSafe_h_
|