You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
4.6 KiB
246 lines
4.6 KiB
#include <windows.h>
|
|
#include <cassert>
|
|
#include "thread.h"
|
|
#include <interface\STaskLoop-i.h>
|
|
#include <process.h>
|
|
|
|
namespace SOUI
|
|
{
|
|
class ThreadPrivate
|
|
{
|
|
public:
|
|
ThreadPrivate():_hthread(NULL), _threadID(0)
|
|
{
|
|
|
|
}
|
|
HANDLE _hthread;
|
|
unsigned int _threadID;
|
|
};
|
|
|
|
const long Thread::INVALID_THREAD_ID = -1;
|
|
|
|
Thread::Thread() :
|
|
_stopping(false),
|
|
_runnable(NULL),
|
|
_name(),
|
|
_private(*(new ThreadPrivate())),
|
|
_start(false)
|
|
{
|
|
}
|
|
|
|
Thread::~Thread()
|
|
{
|
|
waitForStop();
|
|
delete &_private;
|
|
}
|
|
|
|
long Thread::getCurrentThreadID()
|
|
{
|
|
DWORD id = ::GetCurrentThreadId();
|
|
return (long) id;
|
|
}
|
|
|
|
bool Thread::start(IRunnable *runnable, const std::string &name, ThreadPriority priority)
|
|
{
|
|
assert(runnable);
|
|
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
|
|
if (_start)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_stopping = false;
|
|
_runnable = runnable->clone();
|
|
_name = name;
|
|
_start = true;
|
|
_priorityLevel = priority;
|
|
|
|
_private._hthread = (HANDLE)_beginthreadex(NULL, 0, Thread::threadProcWin32thunk, this, 0, &_private._threadID);
|
|
|
|
if (_private._hthread == NULL)
|
|
{
|
|
clear();
|
|
return false;
|
|
}
|
|
setPriority(priority);
|
|
}
|
|
|
|
_startSem.wait(100);
|
|
return true;
|
|
}
|
|
|
|
void Thread::stop()
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
|
|
if (!_start)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_stopping = true;
|
|
}
|
|
|
|
void Thread::waitForStop()
|
|
{
|
|
// we must release lock before the wait function, to avoid deadlock
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
|
|
if (!_start)
|
|
{
|
|
if ( _private._hthread )
|
|
{
|
|
::CloseHandle( _private._hthread); // resource must be released
|
|
_private._hthread = NULL;
|
|
_private._threadID = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
DWORD ret = ::WaitForSingleObject(_private._hthread, INFINITE);
|
|
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
|
|
if ( WAIT_OBJECT_0 == ret )
|
|
{
|
|
::CloseHandle( _private._hthread);
|
|
}
|
|
|
|
_private._hthread = NULL;
|
|
_private._threadID = 0;
|
|
}
|
|
|
|
if (WAIT_OBJECT_0 != 0)
|
|
{
|
|
}
|
|
}
|
|
|
|
bool Thread::isStopping()
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
return _stopping;
|
|
}
|
|
|
|
bool Thread::isStopped()
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
return _stopping || (!_start);
|
|
}
|
|
|
|
std::string Thread::getName()
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
return _name;
|
|
}
|
|
|
|
// we have undocumented Win32 APIs to set thread name.
|
|
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)
|
|
static void SetThreadName(DWORD dwThreadID, const char *threadName)
|
|
{
|
|
THREADNAME_INFO info;
|
|
info.dwType = 0x1000;
|
|
info.szName = threadName;
|
|
info.dwThreadID = dwThreadID;
|
|
info.dwFlags = 0;
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 6320 6322)
|
|
|
|
__try
|
|
{
|
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
#pragma warning(pop)
|
|
}
|
|
|
|
void Thread::setThreadName(const std::string &name)
|
|
{
|
|
SetThreadName(::GetCurrentThreadId(), name.c_str());
|
|
}
|
|
|
|
void Thread::setPriority(ThreadPriority priorityLevel)
|
|
{
|
|
int priority = 0;
|
|
|
|
switch (priorityLevel)
|
|
{
|
|
case High:
|
|
priority = THREAD_PRIORITY_TIME_CRITICAL;
|
|
break;
|
|
|
|
case Low:
|
|
priority = THREAD_PRIORITY_IDLE;
|
|
break;
|
|
|
|
default:
|
|
priority = THREAD_PRIORITY_NORMAL;
|
|
break;
|
|
}
|
|
|
|
BOOL ret = SetThreadPriority(_private._hthread, priority);
|
|
|
|
if (!ret)
|
|
{
|
|
}
|
|
}
|
|
|
|
void Thread::clear()
|
|
{
|
|
SAutoLock autolock (_lock);
|
|
_stopping = false;
|
|
delete _runnable;
|
|
_runnable = NULL;
|
|
_name = "";
|
|
_start = false;
|
|
_threadID = INVALID_THREAD_ID;
|
|
}
|
|
|
|
// `stdcall' to 'cdecl' thunk
|
|
unsigned WINAPI Thread::threadProcWin32thunk(LPVOID lpParameter )
|
|
{
|
|
Thread::threadProc(lpParameter);
|
|
return 0;
|
|
}
|
|
|
|
void *Thread::threadProc(void *args)
|
|
{
|
|
Thread *thread = static_cast<Thread *> (args);
|
|
|
|
{
|
|
SAutoLock autoLock(thread->_lock);
|
|
thread->_threadID = getCurrentThreadID();
|
|
thread->setThreadName(thread->_name);
|
|
thread->setPriority(thread->_priorityLevel);
|
|
}
|
|
|
|
thread->_startSem.notify();
|
|
thread->_runnable->run();
|
|
|
|
{
|
|
SAutoLock autoLock(thread->_lock);
|
|
thread->clear();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|