374 lines
11 KiB
C++
374 lines
11 KiB
C++
|
/****************************************************************************
|
|||
|
Copyright (c) 2006, Radon Labs GmbH
|
|||
|
Copyright (c) 2011-2013,WebJet Business Division,CYOU
|
|||
|
|
|||
|
http://www.genesis-3d.com.cn
|
|||
|
|
|||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|||
|
of this software and associated documentation files (the "Software"), to deal
|
|||
|
in the Software without restriction, including without limitation the rights
|
|||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
|
copies of the Software, and to permit persons to whom the Software is
|
|||
|
furnished to do so, subject to the following conditions:
|
|||
|
|
|||
|
The above copyright notice and this permission notice shall be included in
|
|||
|
all copies or substantial portions of the Software.
|
|||
|
|
|||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
|
THE SOFTWARE.
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
#if WIN32
|
|||
|
|
|||
|
#include "stdneb.h"
|
|||
|
#include "threading/win360/win360thread.h"
|
|||
|
#include "system/systeminfo.h"
|
|||
|
#include "core/sysfunc.h"
|
|||
|
|
|||
|
//#if __XBOX360__
|
|||
|
//#include "threading/xbox360/xbox360threading.h"
|
|||
|
//#endif
|
|||
|
|
|||
|
namespace Win360
|
|||
|
{
|
|||
|
__ImplementClass(Win360::Win360Thread, 'THRD', Core::RefCounted);
|
|||
|
|
|||
|
using namespace Util;
|
|||
|
using namespace System;
|
|||
|
|
|||
|
#if NEBULA3_DEBUG
|
|||
|
Threading::CriticalSection Win360Thread::criticalSection;
|
|||
|
List<Win360Thread*> Win360Thread::ThreadList;
|
|||
|
#endif
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
*/
|
|||
|
Win360Thread::Win360Thread() :
|
|||
|
threadHandle(0),
|
|||
|
priority(Normal),
|
|||
|
stackSize(65536),
|
|||
|
coreId(Cpu::InvalidCoreId)
|
|||
|
{
|
|||
|
// register with thread list
|
|||
|
#if NEBULA3_DEBUG
|
|||
|
Win360Thread::criticalSection.Enter();
|
|||
|
this->threadListIterator = ThreadList.AddBack(this);
|
|||
|
Win360Thread::criticalSection.Leave();
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
*/
|
|||
|
Win360Thread::~Win360Thread()
|
|||
|
{
|
|||
|
if (this->IsRunning())
|
|||
|
{
|
|||
|
this->Stop();
|
|||
|
}
|
|||
|
|
|||
|
// unregister from thread list
|
|||
|
#if NEBULA3_DEBUG
|
|||
|
n_assert(0 != this->threadListIterator);
|
|||
|
Win360Thread::criticalSection.Enter();
|
|||
|
ThreadList.Remove(this->threadListIterator);
|
|||
|
Win360Thread::criticalSection.Leave();
|
|||
|
this->threadListIterator = 0;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Start the thread, this creates a Win32 thread and calls the static
|
|||
|
ThreadProc, which in turn calls the virtual DoWork() class of this object.
|
|||
|
The method waits for the thread to start and then returns.
|
|||
|
*/
|
|||
|
void
|
|||
|
Win360Thread::Start()
|
|||
|
{
|
|||
|
n_assert(!this->IsRunning());
|
|||
|
n_assert(0 == this->threadHandle);
|
|||
|
this->threadHandle = CreateThread(NULL, // lpThreadAttributes
|
|||
|
this->stackSize, // dwStackSize
|
|||
|
ThreadProc, // lpStartAddress
|
|||
|
(LPVOID) this, // lpParameter
|
|||
|
CREATE_SUSPENDED, // dwCreationFlags
|
|||
|
NULL); // lpThreadId
|
|||
|
n_assert(NULL != this->threadHandle);
|
|||
|
|
|||
|
// apply thread priority
|
|||
|
switch (this->priority)
|
|||
|
{
|
|||
|
case Low:
|
|||
|
SetThreadPriority(this->threadHandle, THREAD_PRIORITY_BELOW_NORMAL);
|
|||
|
break;
|
|||
|
|
|||
|
case Normal:
|
|||
|
SetThreadPriority(this->threadHandle, THREAD_PRIORITY_NORMAL);
|
|||
|
break;
|
|||
|
|
|||
|
case High:
|
|||
|
SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
#if __WIN32__
|
|||
|
// select a good processor for the thread
|
|||
|
/*
|
|||
|
SystemInfo systemInfo;
|
|||
|
SizeT numCpuCores = systemInfo.GetNumCpuCores();
|
|||
|
DWORD threadIdealProc = 0;
|
|||
|
if (Cpu::InvalidCoreId != this->coreId)
|
|||
|
{
|
|||
|
threadIdealProc = this->coreId % systemInfo.GetNumCpuCores();
|
|||
|
}
|
|||
|
SetThreadIdealProcessor(this->threadHandle, threadIdealProc);
|
|||
|
*/
|
|||
|
#elif __XBOX360__
|
|||
|
// on the 360 we need to define the hardware thread this thread should run on
|
|||
|
n_assert(this->coreId != Cpu::InvalidCoreId)
|
|||
|
Xbox360::Xbox360Threading::SetThreadProcessor(this->threadHandle, this->coreId);
|
|||
|
#endif
|
|||
|
|
|||
|
// resume thread (since it was actived in suspended state)
|
|||
|
ResumeThread(this->threadHandle);
|
|||
|
|
|||
|
// wait for the thread to start
|
|||
|
this->threadStartedEvent.Wait();
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
This method is called by Thread::Stop() after setting the
|
|||
|
stopRequest event and before waiting for the thread to stop. If your
|
|||
|
thread runs a loop and waits for jobs it may need an extra wakeup
|
|||
|
signal to stop waiting and check for the ThreadStopRequested() event. In
|
|||
|
this case, override this method and signal your event object.
|
|||
|
*/
|
|||
|
void
|
|||
|
Win360Thread::EmitWakeupSignal()
|
|||
|
{
|
|||
|
// empty, override in subclass!
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
This stops the thread by signalling the stopRequestEvent and waits for the
|
|||
|
thread to actually quit. If the thread code runs in a loop it should use the
|
|||
|
IsStopRequested() method to see if the thread object wants it to shutdown.
|
|||
|
If so DoWork() should simply return.
|
|||
|
*/
|
|||
|
void
|
|||
|
Win360Thread::Stop()
|
|||
|
{
|
|||
|
n_assert(this->IsRunning());
|
|||
|
n_assert(0 != this->threadHandle);
|
|||
|
|
|||
|
// signal the thread to stop
|
|||
|
this->stopRequestEvent.Signal();
|
|||
|
|
|||
|
// call the wakeup-thread method, may be derived in a subclass
|
|||
|
// if the threads needs to be woken up, it is important that this
|
|||
|
// method is called AFTER the stopRequestEvent is signalled!
|
|||
|
this->EmitWakeupSignal();
|
|||
|
|
|||
|
// wait for the thread to terminate
|
|||
|
WaitForSingleObject(this->threadHandle, INFINITE);
|
|||
|
CloseHandle(this->threadHandle);
|
|||
|
this->threadHandle = 0;
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Returns true if the thread is currently running.
|
|||
|
*/
|
|||
|
bool
|
|||
|
Win360Thread::IsRunning() const
|
|||
|
{
|
|||
|
if (0 != this->threadHandle)
|
|||
|
{
|
|||
|
DWORD exitCode = 0;
|
|||
|
if (GetExitCodeThread(this->threadHandle, &exitCode))
|
|||
|
{
|
|||
|
if (STILL_ACTIVE == exitCode)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// fallthrough: thread not yet, or no longer
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
This method should be derived in a Thread subclass and contains the
|
|||
|
actual code which is run in the thread. The method must not call
|
|||
|
C-Lib functions under Win32. To terminate the thread, just return from
|
|||
|
this function. If DoWork() runs in an infinite loop, call ThreadStopRequested()
|
|||
|
to check whether the Thread object wants the thread code to quit.
|
|||
|
*/
|
|||
|
void
|
|||
|
Win360Thread::DoWork()
|
|||
|
{
|
|||
|
// empty
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Internal static helper method. This is called by CreateThread() and
|
|||
|
simply calls the virtual DoWork() method on the thread object.
|
|||
|
*/
|
|||
|
DWORD WINAPI
|
|||
|
Win360Thread::ThreadProc(LPVOID self)
|
|||
|
{
|
|||
|
n_assert(0 != self);
|
|||
|
|
|||
|
Win360Thread* threadObj = (Win360Thread*) self;
|
|||
|
|
|||
|
// must before localStringAtomTable construct
|
|||
|
Win360Thread::SetupMyThreadRunTime(threadObj->GetName().AsCharPtr());
|
|||
|
|
|||
|
// for destroy localStringAtomTable before Win360Thread::DestoryThreadRunTime()
|
|||
|
{
|
|||
|
#if NEBULA3_ENABLE_THREADLOCAL_STRINGATOM_TABLES
|
|||
|
// setup thread-local string atom table (will be discarded when thread terminates)
|
|||
|
LocalStringAtomTable localStringAtomTable;
|
|||
|
#endif
|
|||
|
|
|||
|
threadObj->threadStartedEvent.Signal();
|
|||
|
threadObj->DoWork();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Win360Thread::DestoryThreadRunTime();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Static method which sets the name of this thread. This is called from
|
|||
|
within ThreadProc. The string pointed to must remain valid until
|
|||
|
the thread is terminated!
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Win360Thread::SetupMyThreadRunTime(const char* n)
|
|||
|
{
|
|||
|
Threading::ThreadRunTimeInfo::SetupMyThreadRunTime( n );
|
|||
|
|
|||
|
// update the Windows thread name so that it shows up correctly
|
|||
|
// in the Debugger
|
|||
|
struct THREADNAME_INFO
|
|||
|
{
|
|||
|
DWORD dwType; // must be 0x1000
|
|||
|
LPCSTR szName; // pointer to name (in user address space)
|
|||
|
DWORD dwThreadID; // thread ID (-1 = caller thread)
|
|||
|
DWORD dwFlags; // reserved for future use, must be zero
|
|||
|
};
|
|||
|
|
|||
|
THREADNAME_INFO info;
|
|||
|
info.dwType = 0x1000;
|
|||
|
info.szName = n;
|
|||
|
info.dwThreadID = ::GetCurrentThreadId();
|
|||
|
info.dwFlags = 0;
|
|||
|
__try
|
|||
|
{
|
|||
|
RaiseException( 0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD*)&info );
|
|||
|
}
|
|||
|
__except( EXCEPTION_CONTINUE_EXECUTION )
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
//------------------------------------------------------------------------
|
|||
|
void
|
|||
|
Win360Thread::DestoryThreadRunTime( bool isMainThread /*= false*/ )
|
|||
|
{
|
|||
|
Threading::ThreadRunTimeInfo::DestoryThreadRunTime( isMainThread );
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Static method to obtain the current thread name from anywhere
|
|||
|
in the thread's code.
|
|||
|
*/
|
|||
|
const char*
|
|||
|
Win360Thread::GetMyThreadName()
|
|||
|
{
|
|||
|
Threading::ThreadRunTimeInfo* pInfo = Threading::ThreadRunTimeInfo::GetMyThreadRuntime();
|
|||
|
|
|||
|
// <09>п<EFBFBD><D0BF><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD><DAB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̵߳<DFB3><CCB5>øú<C3B8><C3BA><EFBFBD>
|
|||
|
if ( pInfo )
|
|||
|
{
|
|||
|
return pInfo->mThreadName;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Static method which returns the ThreadId of this thread.
|
|||
|
*/
|
|||
|
Threading::ThreadId
|
|||
|
Win360Thread::GetMyThreadId()
|
|||
|
{
|
|||
|
return ::GetCurrentThreadId();
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
The yield function is empty on Win32 and Xbox360.
|
|||
|
*/
|
|||
|
void
|
|||
|
Win360Thread::YieldThread()
|
|||
|
{
|
|||
|
// empty
|
|||
|
}
|
|||
|
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
/**
|
|||
|
Returns an array with infos about all currently existing thread objects.
|
|||
|
*/
|
|||
|
#if NEBULA3_DEBUG
|
|||
|
Array<Win360Thread::ThreadDebugInfo>
|
|||
|
Win360Thread::GetRunningThreadDebugInfos()
|
|||
|
{
|
|||
|
// NOTE: Portions of this loop aren't completely thread-safe
|
|||
|
// (getting the thread-name for instance), but since those
|
|||
|
// attributes don't change when the thread has been started
|
|||
|
// this shouldn't be a problem.
|
|||
|
Array<ThreadDebugInfo> infos;
|
|||
|
Win360Thread::criticalSection.Enter();
|
|||
|
List<Win360Thread*>::Iterator iter;
|
|||
|
for (iter = ThreadList.Begin(); iter != ThreadList.End(); iter++)
|
|||
|
{
|
|||
|
Win360Thread* cur = *iter;
|
|||
|
if (cur->IsRunning())
|
|||
|
{
|
|||
|
ThreadDebugInfo info;
|
|||
|
info.threadName = cur->GetName();
|
|||
|
info.threadPriority = cur->GetPriority();
|
|||
|
info.threadCoreId = cur->GetCoreId();
|
|||
|
info.threadStackSize = cur->GetStackSize();
|
|||
|
infos.Append(info);
|
|||
|
}
|
|||
|
}
|
|||
|
Win360Thread::criticalSection.Leave();
|
|||
|
return infos;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
} // namespace Win360
|
|||
|
|
|||
|
#endif
|