///////////////////////////////////////////////////////////// // CINEMA 4D SDK // ///////////////////////////////////////////////////////////// // (c) MAXON Computer GmbH, all rights reserved // ///////////////////////////////////////////////////////////// #ifndef __C4DTHREAD_H #define __C4DTHREAD_H #ifndef __API_INTERN__ #include "operatingsystem.h" #define ThreadCall(fnc) (this->*C4DOS.Bt->fnc) struct GeSpinlock { GeSpinlock() : state(0) { } volatile Int32 state; inline void Lock() { ThreadCall(Lock) (); } inline void Unlock() { ThreadCall(Unlock) (); } inline Bool AttemptLock() { return ThreadCall(AttemptLock) (); } }; struct GeRWSpinlock { GeRWSpinlock() : state(0) { } volatile Int32 state; inline void ReadLock() { ThreadCall(ReadLock) (); } inline void ReadUnlock() { ThreadCall(ReadUnlock) (); } inline void WriteLock() { ThreadCall(WriteLock) (); } inline void WriteUnlock() { ThreadCall(WriteUnlock) (); } inline Bool AttemptWriteLock() { return ThreadCall(AttemptWriteLock) (); } }; class BaseThread { private: BaseThread(); ~BaseThread(); public: Bool TestBreak(void) { return C4DOS.Bt->TestBreak(this); } void End(Bool wait = true) { C4DOS.Bt->End(this, wait); } Bool IsRunning(void) { return C4DOS.Bt->IsRunning(this); } }; class C4DThread { friend class MPThreadPool; friend void XThreadMain(void* data); friend Bool XThreadTest(void* data); private: Bool weak; BaseThread* bt; public: C4DThread(void); virtual ~C4DThread(void); BaseThread* Get(void) const { return bt; } Bool Start(THREADMODE mode = THREADMODE_ASYNC, THREADPRIORITY priority = THREADPRIORITY_NORMAL); void End(Bool wait = true); Bool IsRunning(void) { return C4DOS.Bt->IsRunning(bt); } Bool TestBreak(void) { return C4DOS.Bt->TestBreak(bt); } void Wait(Bool checkevents = false); // routines for overriding virtual Bool TestDBreak(void) { return false; } virtual void Main(void) = 0; virtual const Char* GetThreadName(void) = 0; }; class MPThreadPool { private: MPBaseThread* mp; Int32 mpcount; public: MPThreadPool(void); ~MPThreadPool(void); Bool Init(BaseThread* parent, Int32 count, C4DThread** thread); Bool Init(const C4DThread& parent, Int32 count, C4DThread** thread); Bool Start(THREADPRIORITY worker_priority); C4DThread* WaitForNextFree(void); void Wait(void); void End(void); }; class Semaphore { private: Semaphore(); ~Semaphore(); public: Bool AttemptLock(void) { return C4DOS.Bt->SMLock(this); } Bool Lock(BaseThread* bt) { return C4DOS.Bt->SMLockAndWait(this, bt); } Bool Lock(C4DThread* bt) { return C4DOS.Bt->SMLockAndWait(this, bt ? bt->Get() : nullptr); } #ifdef _DEBUG #define AttemptLockDebug _AttemptLock(__LINE__, __FILE__) #define LockDebug(_x_) _LockDebug(_x_, __LINE__, __FILE__) Bool _AttemptLockDebug(Int32 line, const char* file) { return C4DOS.Bt->SMLockDebug(this, line, file); } Bool _LockDebug(BaseThread* bt, Int32 line, const char* file) { return C4DOS.Bt->SMLockAndWaitDebug(this, bt, line, file); } Bool _LockDebug(C4DThread* bt, Int32 line, const char* file) { return C4DOS.Bt->SMLockAndWaitDebug(this, bt ? bt->Get() : nullptr, line, file); } #else #define AttemptLockDebug AttemptLock #define LockDebug(_x_) Lock(_x_) #endif void Unlock(void) { C4DOS.Bt->SMUnlock(this); } static Semaphore* Alloc(void); static void Free(Semaphore*& sm); }; class GeSignal { private: GeSignal(); ~GeSignal(); public: Bool Init(SIGNALMODE mode = SIGNALMODE_DEFAULT) { return ThreadCall(SIGInit) (mode); } //---------------------------------------------------------------------------------------- /// Wakes up the thread waiting for this condition. /// THREADSAFE. //---------------------------------------------------------------------------------------- void Set() { ThreadCall(SIGSet) (); } //---------------------------------------------------------------------------------------- /// Clears the condition variable. /// When Clear() is called after Wait() you must make sure that there are no more threads /// still waiting for the same condition. Only after the last thread has left Wait() you are /// allowed to call Clear(). Otherwise one of the threads may keep waiting because the /// condition was cleared before it was able to wake up. //---------------------------------------------------------------------------------------- void Clear() { ThreadCall(SIGClear) (); } //---------------------------------------------------------------------------------------- /// Waits until the condition has been set or a certain amount of time has passed. /// GeSignal is auto-clear by default, this means only one thread can wait for the condition /// and the condition is cleared after Wait(). /// You can only call this from a BaseThread. You are not allowed to wait for a condition /// variable from a job - doing this could result in a deadlock. /// For compatibility reasons the return value works differently than in [Core]ConditionVariable. /// THREADSAFE. /// @param[in] timeout Maximum wait interval in milliseconds (or FOREVER) /// @return Will return true if the wait timed-out, otherwise false. //---------------------------------------------------------------------------------------- Bool Wait(Int32 timeout) { return ThreadCall(SIGWait) (timeout); } static GeSignal* Alloc(void); static void Free(GeSignal*& sm); }; inline Int32 GeGetCPUCount(void) { return C4DOS.Bt->GetCPUCount(); } inline void GeThreadLock(void) { C4DOS.Bt->ThreadLock(); } inline void GeThreadUnlock(void) { C4DOS.Bt->ThreadUnlock(); } inline THREADTYPE IdentifyThread(BaseThread* bt) { return C4DOS.Bt->Identify(bt); } inline UInt32 GeGetCurrentThreadId() { return C4DOS.Bt->GetCurrentThreadId(); } inline BaseThread* GeGetCurrentThread() { return C4DOS.Bt->GetCurrentThread(); } #endif class AutoSpinLock { private: GeSpinlock* l; AutoSpinLock& operator = (const AutoSpinLock& d); AutoSpinLock(AutoSpinLock& data); public: AutoSpinLock(GeSpinlock* lock = nullptr) { l = nullptr; if (!lock) return; DoLock(lock); } ~AutoSpinLock() { Unlock(); } void DoLock(GeSpinlock* lock) { if (l || !lock) return; lock->Lock(); l = lock; } void Unlock(void) { if (!l) return; l->Unlock(); l = nullptr; } }; class AutoLocker; class AutoLock { private: friend class AutoLocker; GeSpinlock lock; volatile UInt32 threadid; AutoLock(const AutoLock& al); AutoLock(const AutoLocker& al); public: AutoLock() { threadid = NOTOK; } }; class AutoLocker { private: GeSpinlock* l; volatile UInt32* ct; AutoLocker& operator = (const AutoLocker& d); AutoLocker(AutoLocker& data); public: AutoLocker() { l = nullptr; ct = nullptr; } AutoLocker(AutoLock& data) { l = nullptr; ct = nullptr; DoLock(data); } ~AutoLocker() { Unlock(); } void DoLock(AutoLock& data) { if (l) return; UInt32 id = GeGetCurrentThreadId(); if (data.threadid == id) return; data.lock.Lock(); l = &data.lock; ct = &data.threadid; if (ct) *ct = id; } void Unlock(void) { if (!l) return; *ct = NOTOK; l->Unlock(); l = nullptr; ct = nullptr; } }; class AutoRWLock { friend class AutoRWLocker; GeRWSpinlock lock; volatile UInt32 threadid; public: AutoRWLock() { threadid = NOTOK; } }; class AutoRWLocker { private: GeRWSpinlock* l; volatile UInt32* ct; volatile Bool is_write; AutoRWLocker& operator = (const AutoRWLocker& d); AutoRWLocker(AutoRWLocker& lock); public: AutoRWLocker() { l = nullptr; ct = nullptr; is_write = false; } AutoRWLocker(AutoRWLock& lock, Bool write_lock = true) { l = nullptr; ct = nullptr; is_write = write_lock; DoLock(lock, write_lock); } ~AutoRWLocker() { Unlock(); } void DoLock(AutoRWLock& lock, Bool write_lock = true) { if (l) return; UInt32 id = 0; id = GeGetCurrentThreadId(); if (lock.threadid == id) return; if (write_lock) lock.lock.WriteLock(); else lock.lock.ReadLock(); is_write = write_lock; l = &lock.lock; ct = &lock.threadid; if (ct) *ct = id; } void Unlock(void) { if (!l) return; *ct = NOTOK; if (is_write) l->WriteUnlock(); else l->ReadUnlock(); l = nullptr; ct = nullptr; } }; #endif