Files
FlaxEngine/Source/Engine/Platform/Win32/Win32Thread.cpp
Ari Vuollet b0bc1fa310 Fix error when joining exited threads
The internal thread handles were cleared prematurely when attempting to join them. The handles should be also cleared when trying to kill already exited threads.
2023-01-29 21:30:01 +02:00

162 lines
3.7 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if PLATFORM_WIN32
#include "Win32Thread.h"
#include "Engine/Core/Log.h"
#include "Engine/Threading/IRunnable.h"
#include "Engine/Threading/ThreadRegistry.h"
#include "IncludeWindowsHeaders.h"
#if PLATFORM_WINDOWS
#define WINDOWS_ENABLE_THREAD_NAMING 1
#endif
#if WINDOWS_ENABLE_THREAD_NAMING
// Source: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
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)
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)
}
#endif
Win32Thread::Win32Thread(IRunnable* runnable, const String& name, ThreadPriority priority)
: ThreadBase(runnable, name, priority)
, _thread(nullptr)
{
}
Win32Thread::~Win32Thread()
{
ASSERT(_thread == nullptr);
}
Win32Thread* Win32Thread::Create(IRunnable* runnable, const String& name, ThreadPriority priority, uint32 stackSize)
{
auto result = New<Win32Thread>(runnable, name, priority);
if (result->Start(stackSize))
{
SAFE_DELETE(result);
}
return result;
}
bool Win32Thread::Start(uint32 stackSize)
{
// Spawn thread
DWORD id;
_thread = (void*)CreateThread(nullptr, stackSize, (LPTHREAD_START_ROUTINE)ThreadProc, this, STACK_SIZE_PARAM_IS_A_RESERVATION, &id);
if (_thread == nullptr)
return true;
// Initialize priority
SetPriorityInternal(_priority);
#if WINDOWS_ENABLE_THREAD_NAMING
SetThreadName(id, _name.ToStringAnsi().Get());
#endif
return false;
}
#if PLATFORM_WINDOWS
extern LONG CALLBACK SehExceptionHandler(EXCEPTION_POINTERS* ep);
#endif
unsigned long Win32Thread::ThreadProc(void* pThis)
{
auto thread = (Win32Thread*)pThis;
#if PLATFORM_WINDOWS
__try
#endif
{
const int32 exitCode = thread->Run();
return static_cast<unsigned long>(exitCode);
}
#if PLATFORM_WINDOWS
__except (SehExceptionHandler(GetExceptionInformation()))
{
return -1;
}
#endif
}
void Win32Thread::Join()
{
WaitForSingleObject((HANDLE)_thread, INFINITE);
ClearHandleInternal();
}
void Win32Thread::ClearHandleInternal()
{
_thread = nullptr;
}
void Win32Thread::SetPriorityInternal(ThreadPriority priority)
{
int32 winPriority;
switch (priority)
{
case ThreadPriority::Lowest:
winPriority = THREAD_PRIORITY_LOWEST;
break;
case ThreadPriority::BelowNormal:
winPriority = THREAD_PRIORITY_BELOW_NORMAL;
break;
case ThreadPriority::Normal:
winPriority = THREAD_PRIORITY_NORMAL;
break;
case ThreadPriority::AboveNormal:
winPriority = THREAD_PRIORITY_ABOVE_NORMAL;
break;
case ThreadPriority::Highest:
winPriority = THREAD_PRIORITY_HIGHEST;
break;
default:
winPriority = THREAD_PRIORITY_NORMAL;
break;
}
::SetThreadPriority((HANDLE)_thread, winPriority);
}
void Win32Thread::KillInternal(bool waitForJoin)
{
if (waitForJoin)
{
WaitForSingleObject((HANDLE)_thread, INFINITE);
}
CloseHandle((HANDLE)_thread);
}
#endif