284 lines
6.5 KiB
C++
284 lines
6.5 KiB
C++
|
#include "ehs/system/Thread.h"
|
||
|
#include "ehs/system/CPU.h"
|
||
|
|
||
|
#if defined(EHS_OS_WINDOWS)
|
||
|
#include <avrt.h>
|
||
|
#elif defined(EHS_OS_LINUX)
|
||
|
#include <pthread.h>
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
|
||
|
namespace ehs
|
||
|
{
|
||
|
UInt_32 Thread::mainId = GetCurrentId();
|
||
|
|
||
|
#ifdef EHS_OS_WINDOWS
|
||
|
Handle Thread::mainTaskHdl = nullptr;
|
||
|
UInt_32 Thread::mainTaskIndex = 0;
|
||
|
#endif
|
||
|
|
||
|
Thread::~Thread()
|
||
|
{
|
||
|
Join();
|
||
|
}
|
||
|
|
||
|
Thread::Thread(const UInt_64 stackSize)
|
||
|
: stackSize(stackSize), hdl(EHS_INVALID_THREAD), id(0)
|
||
|
#ifdef EHS_OS_WINDOWS
|
||
|
,taskHdl(nullptr), taskIndex(0)
|
||
|
#endif
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Thread::Thread(const Thread& thread)
|
||
|
: stackSize(thread.stackSize), hdl(EHS_INVALID_THREAD), id(0)
|
||
|
#ifdef EHS_OS_WINDOWS
|
||
|
, taskHdl(nullptr), taskIndex(0)
|
||
|
#endif
|
||
|
{
|
||
|
}
|
||
|
|
||
|
Thread& Thread::operator=(const Thread& thread)
|
||
|
{
|
||
|
if (this == &thread)
|
||
|
return *this;
|
||
|
|
||
|
stackSize = thread.stackSize;
|
||
|
hdl = EHS_INVALID_THREAD;
|
||
|
id = 0;
|
||
|
|
||
|
#ifdef EHS_OS_WINDOWS
|
||
|
taskHdl = nullptr;
|
||
|
taskIndex = 0;
|
||
|
#endif
|
||
|
|
||
|
return* this;
|
||
|
}
|
||
|
|
||
|
void Thread::Start(UInt_32 (*cb)(void*), void* args)
|
||
|
{
|
||
|
#if defined(EHS_OS_WINDOWS)
|
||
|
hdl = CreateThread(nullptr, stackSize, (LPTHREAD_START_ROUTINE)cb, args, 0, (DWORD*)&id);
|
||
|
if (!hdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to start thread with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
#elif defined(EHS_OS_LINUX)
|
||
|
UInt_64* rArgs = new UInt_64[sizeof(UInt_64) * 2];
|
||
|
rArgs[0] = (UInt_64)cb;
|
||
|
rArgs[1] = (UInt_64)args;
|
||
|
|
||
|
pthread_create((pthread_t*)&hdl, nullptr, Redirect, (void*)rArgs);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool Thread::Join(const unsigned int timeout)
|
||
|
{
|
||
|
if (hdl == EHS_INVALID_THREAD)
|
||
|
return false;
|
||
|
|
||
|
#if defined(EHS_WINDOWS)
|
||
|
unsigned int r = WaitForSingleObject(hdl, timeout);
|
||
|
if (r == WAIT_ABANDONED)
|
||
|
{
|
||
|
EHS_LOG_INT("Error", 0, "Abandoned wait because a mutex was not released.");
|
||
|
return false;
|
||
|
}
|
||
|
else if (r == WAIT_TIMEOUT)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
else if (r == WAIT_FAILED)
|
||
|
{
|
||
|
EHS_LOG_INT("Error", 1, "Failed to wait for thread with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
mainTaskIndex = 0;
|
||
|
#elif defined(EHS_OS_LINUX)
|
||
|
int code = pthread_join((pthread_t)hdl, nullptr);
|
||
|
if (code != 0)
|
||
|
EHS_LOG_INT("Error", 1, "Failed to wait for thread with error #" + Str_8::FromNum(code) + ".");
|
||
|
|
||
|
hdl = EHS_INVALID_THREAD;
|
||
|
#endif
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Thread::Detach()
|
||
|
{
|
||
|
if (!hdl)
|
||
|
return;
|
||
|
|
||
|
#if defined(EHS_OS_WINDOWS)
|
||
|
if (!CloseHandle(hdl))
|
||
|
{
|
||
|
EHS_LOG_INT("Error", 0, "Failed to detach thread with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
return;
|
||
|
}
|
||
|
#elif defined(EHS_OS_LINUX)
|
||
|
pthread_detach(hdl);
|
||
|
|
||
|
hdl = EHS_INVALID_THREAD;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
UInt_64 Thread::GetStackSize() const
|
||
|
{
|
||
|
return stackSize;
|
||
|
}
|
||
|
|
||
|
THandle Thread::GetHandle() const
|
||
|
{
|
||
|
return hdl;
|
||
|
}
|
||
|
|
||
|
UInt_32 Thread::GetId() const
|
||
|
{
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
bool Thread::IsCurrent() const
|
||
|
{
|
||
|
return id == GetCurrentId();
|
||
|
}
|
||
|
|
||
|
bool Thread::IsValid() const
|
||
|
{
|
||
|
return hdl;
|
||
|
}
|
||
|
|
||
|
#ifdef EHS_OS_WINDOWS
|
||
|
void Thread::SetTaskType_32(const Str_32& task)
|
||
|
{
|
||
|
if (!IsCurrent())
|
||
|
return;
|
||
|
|
||
|
taskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&taskIndex);
|
||
|
if (!taskHdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to set the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
}
|
||
|
|
||
|
void Thread::SetTaskType_16(const Str_16& task)
|
||
|
{
|
||
|
if (!IsCurrent())
|
||
|
return;
|
||
|
|
||
|
taskHdl = AvSetMmThreadCharacteristicsW(task, (LPDWORD)&taskIndex);
|
||
|
if (!taskHdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to set the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
}
|
||
|
|
||
|
void Thread::SetTaskType_8(const Str_8& task)
|
||
|
{
|
||
|
if (!IsCurrent())
|
||
|
return;
|
||
|
|
||
|
taskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&taskIndex);
|
||
|
if (!taskHdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to set the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
}
|
||
|
|
||
|
void Thread::RevertTaskType()
|
||
|
{
|
||
|
if (!IsCurrent())
|
||
|
return;
|
||
|
|
||
|
if (!AvRevertMmThreadCharacteristics(taskHdl))
|
||
|
EHS_LOG_INT("Error", 0, "Failed to revert the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
|
||
|
taskIndex = 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
UInt_32 Thread::GetMainId()
|
||
|
{
|
||
|
return mainId;
|
||
|
}
|
||
|
|
||
|
UInt_64 Thread::GetCurrentId()
|
||
|
{
|
||
|
#if defined(EHS_OS_WINDOWS)
|
||
|
return GetCurrentThreadId();
|
||
|
#elif defined(EHS_OS_LINUX)
|
||
|
return pthread_self();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef EHS_OS_WINDOWS
|
||
|
void Thread::SetMainTaskType_32(const Str_32& task)
|
||
|
{
|
||
|
if (GetCurrentId() != mainId)
|
||
|
return;
|
||
|
|
||
|
mainTaskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&mainTaskIndex);
|
||
|
if (!mainTaskHdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to set the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
}
|
||
|
|
||
|
void Thread::SetMainTaskType_16(const Str_16& task)
|
||
|
{
|
||
|
if (GetCurrentId() != mainId)
|
||
|
return;
|
||
|
|
||
|
mainTaskHdl = AvSetMmThreadCharacteristicsW(task, (LPDWORD)&mainTaskIndex);
|
||
|
if (!mainTaskHdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to set the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
}
|
||
|
|
||
|
void Thread::SetMainTaskType_8(const Str_8& task)
|
||
|
{
|
||
|
if (GetCurrentId() != mainId)
|
||
|
return;
|
||
|
|
||
|
mainTaskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&mainTaskIndex);
|
||
|
if (!mainTaskHdl)
|
||
|
EHS_LOG_INT("Error", 0, "Failed to set the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
}
|
||
|
|
||
|
void Thread::RevertMainTaskType()
|
||
|
{
|
||
|
if (GetCurrentId() != mainId)
|
||
|
return;
|
||
|
|
||
|
if (!AvRevertMmThreadCharacteristics(mainTaskHdl))
|
||
|
EHS_LOG_INT("Error", 0, "Failed to revert the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||
|
|
||
|
mainTaskIndex = 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
float Thread::HardSleepFor(const float seconds)
|
||
|
{
|
||
|
UInt_64 freq = CPU::GetTSC_Freq();
|
||
|
UInt_64 start = CPU::GetTSC();
|
||
|
|
||
|
float elapsed = 0.0f;
|
||
|
while ((elapsed = (float)(CPU::GetTSC() - start) / (float)freq) <= seconds);
|
||
|
|
||
|
return elapsed;
|
||
|
}
|
||
|
|
||
|
void Thread::SleepFor(const UInt_32 miliseconds)
|
||
|
{
|
||
|
#if defined(EHS_OS_WINDOWS)
|
||
|
Sleep(miliseconds);
|
||
|
#elif defined(EHS_OS_LINUX)
|
||
|
timespec req = {miliseconds / 1000, miliseconds % 1000 * 1000000};
|
||
|
nanosleep(&req, nullptr);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void* Thread::Redirect(void *args)
|
||
|
{
|
||
|
UInt_64* rArgs = (UInt_64*)args;
|
||
|
UInt_32 (*cb)(void*) = (UInt_32 (*)(void*))rArgs[0];
|
||
|
void* params = (void*)rArgs[1];
|
||
|
|
||
|
UInt_32* code = new UInt_32();
|
||
|
*code = cb(params);
|
||
|
|
||
|
delete[] rArgs;
|
||
|
|
||
|
return code;
|
||
|
}
|
||
|
}
|