322 lines
6.6 KiB
C++
322 lines
6.6 KiB
C++
#include <ZUtil/ZThread.hpp>
|
|
#include <ZUtil/ZLog.hpp>
|
|
#include <ZSTL/ZString.hpp>
|
|
|
|
#ifdef _MSC_VER //begin Microsoft Visual C++ specific code
|
|
#include <windows.h>
|
|
#undef CreateMutex
|
|
#undef CreateEvent
|
|
const DWORD MS_VC_EXCEPTION=0x406D1388;
|
|
|
|
#pragma pack(push,8)
|
|
typedef struct tagTHREADNAME_INFO
|
|
{
|
|
DWORD dwType; // Must be 0x1000.
|
|
LPCSTR szName; // Pointer to name (in user addr space).
|
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
|
} THREADNAME_INFO;
|
|
#pragma pack(pop)
|
|
|
|
//This disgusting little hack sets the thread's name in the MSVC debugger
|
|
void NativeSetThreadName(const char* threadName)
|
|
{
|
|
THREADNAME_INFO info;
|
|
info.dwType = 0x1000;
|
|
info.szName = threadName;
|
|
info.dwThreadID = (DWORD)~0;
|
|
info.dwFlags = 0;
|
|
|
|
__try
|
|
{
|
|
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
}
|
|
|
|
#else
|
|
#define NativeSetThreadName(name)
|
|
#endif //end Microsoft Visual C++ specific code
|
|
|
|
/*************************************************************************/
|
|
|
|
ZThread::ZThread(ZString _threadName)
|
|
:
|
|
ThreadName(_threadName),
|
|
bShouldShutdown(false), bShouldPause(false),
|
|
ThreadContext(),
|
|
ThreadLock(),
|
|
InitEvent(),
|
|
PreviousTickCount(0), CurrentTickCount(0),
|
|
bIsRunning(false), bIsInitialized(false)
|
|
{
|
|
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
ZThread::~ZThread()
|
|
{
|
|
ShutdownThread();
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
int ZThread::InitThread(void *_runnable)
|
|
{
|
|
ZThread *thr = ((ZThread*)_runnable);
|
|
|
|
NativeSetThreadName(thr->GetThreadName().Data());
|
|
|
|
//TODO - hack for now, need to fix CreateThread
|
|
thr->ThreadContext.ThreadId = ZConcurrency::GetThreadId();
|
|
|
|
thr->initThread();
|
|
|
|
thr->bIsInitialized = true;
|
|
|
|
thr->InitEvent.Notify();
|
|
|
|
thr->Run();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::Run()
|
|
{
|
|
uint64_t dt;
|
|
|
|
bIsRunning = true;
|
|
|
|
while (!bShouldShutdown)
|
|
{
|
|
while (bShouldPause)
|
|
ZConcurrency::YieldThread();
|
|
|
|
ExecuteThreadRequests();
|
|
|
|
PreviousTickCount = CurrentTickCount;
|
|
CurrentTickCount = ZConcurrency::GetTicks();
|
|
dt = CurrentTickCount - PreviousTickCount;
|
|
|
|
switch (run(dt))
|
|
{
|
|
case ZTR_LOOP: continue;
|
|
case ZTR_PAUSE: bShouldPause = true; break;
|
|
case ZTR_TERMINATE: bShouldShutdown = true; break;
|
|
default: bShouldShutdown = true; break;
|
|
}
|
|
}
|
|
|
|
ExecuteThreadRequests();
|
|
|
|
shutdownThread();
|
|
|
|
bIsInitialized = false;
|
|
bIsRunning = false;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::AddThreadRequest(ZPtr<ZThreadRequest> _request, bool _wait)
|
|
{
|
|
|
|
//If this is thread adding a request to itself, don't deadlock
|
|
if(IsCallerTheThread()) {
|
|
_request->Execute(this);
|
|
return;
|
|
}
|
|
|
|
ThreadLock.Acquire();
|
|
{
|
|
ZTRCallbacks.PushBack(_request);
|
|
}
|
|
ThreadLock.Release();
|
|
|
|
if (_wait)
|
|
_request->Wait();
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::AddCallbackRequest(ZThreadCallbackFunc function, void* arg, bool async)
|
|
{
|
|
ZThreadCallbackRequest req;
|
|
|
|
//If this is the thread adding a callback to itself, don't deadlock
|
|
if(IsCallerTheThread()) {
|
|
function(this, arg);
|
|
return;
|
|
}
|
|
|
|
req.arg = arg;
|
|
req.callback = function;
|
|
req.doneEvent = (async ? NULL : SST_Concurrency_CreateEvent());
|
|
|
|
ThreadLock.Acquire();
|
|
{
|
|
Callbacks.PushBack(req);
|
|
}
|
|
ThreadLock.Release();
|
|
|
|
//Non-async -> wait for done
|
|
if(!async) {
|
|
SST_Concurrency_WaitEvent(req.doneEvent, SST_WAIT_INFINITE);
|
|
SST_Concurrency_DestroyEvent(req.doneEvent);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::ExecuteThreadRequests()
|
|
{
|
|
ZList< ZPtr<ZThreadRequest> > persistentRequests;
|
|
|
|
ThreadLock.Acquire();
|
|
|
|
while (!ZTRCallbacks.Empty())
|
|
{
|
|
//Don't execute if we are told to stop
|
|
if (ZTRCallbacks.Front()->Stop)
|
|
{
|
|
ZTRCallbacks.Front()->Persist = false;
|
|
}
|
|
else
|
|
{
|
|
ZTRCallbacks.Front()->CompletedEvent.Reset();
|
|
|
|
ZTRCallbacks.Front()->Execute(this);
|
|
|
|
ZTRCallbacks.Front()->CompletedEvent.Notify();
|
|
}
|
|
|
|
//If we are to persist, do so
|
|
if (ZTRCallbacks.Front()->Persist)
|
|
persistentRequests.PushBack(ZTRCallbacks.PopFront());
|
|
else
|
|
ZTRCallbacks.PopFront();
|
|
}
|
|
|
|
while(!Callbacks.Empty()) {
|
|
ZThreadCallbackRequest& req = Callbacks.Front();
|
|
|
|
req.callback(this, req.arg);
|
|
if(req.doneEvent) {
|
|
SST_Concurrency_SignalEvent(req.doneEvent);
|
|
}
|
|
|
|
Callbacks.PopFront();
|
|
}
|
|
|
|
|
|
//Swap back in persistent requests
|
|
ZTRCallbacks.Swap(persistentRequests);
|
|
|
|
ThreadLock.Release();
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
bool ZThread::StartThread()
|
|
{
|
|
bShouldShutdown = false;
|
|
bShouldPause = false;
|
|
|
|
ThreadContext = ZConcurrency::CreateThread(ZThread::InitThread, this);
|
|
|
|
if (!ThreadContext.IsValid())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::ShutdownThread()
|
|
{
|
|
bShouldShutdown = true;
|
|
|
|
if (ThreadContext.IsValid())
|
|
{
|
|
ZConcurrency::WaitThread(ThreadContext);
|
|
ZConcurrency::DestroyThread(ThreadContext);
|
|
ThreadContext.Invalidate();
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::PauseThread()
|
|
{
|
|
bShouldPause = true;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::RestartThread()
|
|
{
|
|
bShouldPause = false;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
bool ZThread::ThreadRunning()
|
|
{
|
|
return bIsRunning;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
bool ZThread::ThreadInitialized()
|
|
{
|
|
return bIsInitialized;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
uint32_t ZThread::GetThreadId()
|
|
{
|
|
return ThreadContext.ThreadId;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
ZString ZThread::GetThreadName()
|
|
{
|
|
ZString name;
|
|
|
|
ThreadLock.Acquire();
|
|
|
|
name = ThreadName;
|
|
|
|
ThreadLock.Release();
|
|
|
|
return name;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::WaitInitialized()
|
|
{
|
|
InitEvent.Wait();
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
void ZThread::WaitShutdown()
|
|
{
|
|
if (ThreadContext.IsValid())
|
|
ZConcurrency::WaitThread(ThreadContext);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
bool ZThread::IsCallerTheThread()
|
|
{
|
|
uint32_t callerThreadID = SST_Concurrency_GetThreadId();
|
|
|
|
return (callerThreadID == this->GetThreadId());
|
|
} |