diff --git a/include/Nazara/Core/ThreadExt.hpp b/include/Nazara/Core/ThreadExt.hpp new file mode 100644 index 000000000..8ecdba71f --- /dev/null +++ b/include/Nazara/Core/ThreadExt.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CORE_THREADEXT_HPP +#define NAZARA_CORE_THREADEXT_HPP + +#include +#include +#include + +namespace Nz +{ + NAZARA_CORE_API std::string GetCurrentThreadName(); + NAZARA_CORE_API std::string GetThreadName(std::thread& thread); + + NAZARA_CORE_API void SetCurrentThreadName(const char* name); + NAZARA_CORE_API void SetThreadName(std::thread& thread, const char* name); +} + +#include + +#endif // NAZARA_CORE_THREADEXT_HPP diff --git a/include/Nazara/Core/ThreadExt.inl b/include/Nazara/Core/ThreadExt.inl new file mode 100644 index 000000000..3846a912d --- /dev/null +++ b/include/Nazara/Core/ThreadExt.inl @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +namespace Nz +{ +} + +#include diff --git a/src/Nazara/Audio/Music.cpp b/src/Nazara/Audio/Music.cpp index 0125724b2..a9d50c61f 100644 --- a/src/Nazara/Audio/Music.cpp +++ b/src/Nazara/Audio/Music.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -425,6 +426,8 @@ namespace Nz void Music::MusicThread(std::condition_variable& cv, std::mutex& m, std::exception_ptr& err, bool startPaused) { + SetCurrentThreadName("MusicThread"); + std::optional> exitLock; // Allocation of streaming buffers diff --git a/src/Nazara/Core/Posix/ThreadImpl.cpp b/src/Nazara/Core/Posix/ThreadImpl.cpp new file mode 100644 index 000000000..dc0c0ba78 --- /dev/null +++ b/src/Nazara/Core/Posix/ThreadImpl.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +// https://stackoverflow.com/questions/2369738/how-to-set-the-name-of-a-thread-in-linux-pthreads + +namespace Nz::PlatformImpl +{ + pthread_t GetCurrentThreadHandle() + { + return ::pthread_self(); + } + + std::string GetThreadName(pthread_t threadHandle) + { +#if defined(__linux__) || defined(__APPLE__) + std::array name; + ::pthread_getname_np(threadHandle, &name[0], name.size()); + + return std::string(&name[0]); +#else + return ""; +#endif + } + + void SetCurrentThreadName(const char* threadName) + { +#if defined(__linux__) + ::pthread_setname_np(::pthread_self(), threadName); +#elif defined(__APPLE__) + ::pthread_setname_np(threadName); +#elif defined(__FreeBSD__) + ::pthread_set_name_np(::pthread_self(), threadName); +#else + NazaraWarning("setting current thread name is not supported on this platform"); +#endif + } + + void SetThreadName(pthread_t threadHandle, const char* threadName) + { +#if defined(__linux__) + ::pthread_setname_np(threadHandle, threadName); +#elif defined(__APPLE__) + NazaraWarning("only current thread name can be set on MacOS X"); +#elif defined(__FreeBSD__) + ::pthread_set_name_np(threadHandle, threadName); +#else + NazaraWarning("setting a thread name is not supported on this platform"); +#endif + } +} + diff --git a/src/Nazara/Core/Posix/ThreadImpl.hpp b/src/Nazara/Core/Posix/ThreadImpl.hpp new file mode 100644 index 000000000..51f292721 --- /dev/null +++ b/src/Nazara/Core/Posix/ThreadImpl.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CORE_POSIX_THREADIMPL_HPP +#define NAZARA_CORE_POSIX_THREADIMPL_HPP + +#include +#include +#include + +namespace Nz::PlatformImpl +{ + using ThreadHandle = pthread_t; + + pthread_t GetCurrentThreadHandle(); + std::string GetCurrentThreadName(); + std::string GetThreadName(pthread_t threadHandle); + void SetCurrentThreadName(const char* threadName); + void SetThreadName(pthread_t threadHandle, const char* threadName); +} + +#endif // NAZARA_CORE_POSIX_THREADIMPL_HPP diff --git a/src/Nazara/Core/ThreadExt.cpp b/src/Nazara/Core/ThreadExt.cpp new file mode 100644 index 000000000..1773b432f --- /dev/null +++ b/src/Nazara/Core/ThreadExt.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include + +#if defined(NAZARA_PLATFORM_WINDOWS) +#include +#elif defined(NAZARA_PLATFORM_POSIX) +#include +#else +#error OS not handled +#endif + +#include + +namespace Nz +{ + std::string GetCurrentThreadName() + { + return PlatformImpl::GetCurrentThreadName(); + } + + std::string GetThreadName(std::thread& thread) + { + // std::thread::native_handle returns a void* with MSVC instead of a HANDLE + return PlatformImpl::GetThreadName(static_cast(thread.native_handle())); + } + + void SetCurrentThreadName(const char* name) + { + PlatformImpl::SetCurrentThreadName(name); + } + + void SetThreadName(std::thread& thread, const char* name) + { + // std::thread::native_handle returns a void* with MSVC instead of a HANDLE + PlatformImpl::SetThreadName(static_cast(thread.native_handle()), name); + } +} diff --git a/src/Nazara/Core/Win32/ThreadImpl.cpp b/src/Nazara/Core/Win32/ThreadImpl.cpp new file mode 100644 index 000000000..b4363e3e1 --- /dev/null +++ b/src/Nazara/Core/Win32/ThreadImpl.cpp @@ -0,0 +1,102 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz::PlatformImpl +{ + namespace NAZARA_ANONYMOUS_NAMESPACE + { + // Windows 10, version 1607 added SetThreadDescription in order to name a thread + using SetThreadDescriptionFunc = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription); + +#ifdef NAZARA_COMPILER_MSVC +#pragma pack(push,8) + struct THREADNAME_INFO + { + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; + }; +#pragma pack(pop) +#endif + } + + HANDLE GetCurrentThreadHandle() + { + return ::GetCurrentThread(); + } + + std::string GetCurrentThreadName() + { + return GetThreadName(::GetCurrentThread()); + } + + std::string GetThreadName(HANDLE threadHandle) + { + PWSTR namePtr; + HRESULT hr = ::GetThreadDescription(threadHandle, &namePtr); + if (FAILED(hr)) + return ""; + + CallOnExit freeName([&] { LocalFree(namePtr); }); + + return FromWideString(namePtr); + } + + void RaiseThreadNameException(DWORD threadId, const char* threadName) + { +#ifdef NAZARA_COMPILER_MSVC + NAZARA_USE_ANONYMOUS_NAMESPACE + + if (!::IsDebuggerPresent()) + return; + + // https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx + constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = threadId; + info.dwFlags = 0; + +#pragma warning(push) +#pragma warning(disable: 6320 6322) + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast(&info)); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } +#pragma warning(pop) +#else + NazaraWarning("ThreadName exception is only supported with MSVC"); +#endif + } + + void SetCurrentThreadName(const char* threadName) + { + SetThreadName(::GetCurrentThread(), threadName); + } + + void SetThreadName(HANDLE threadHandle, const char* threadName) + { + NAZARA_USE_ANONYMOUS_NAMESPACE + + // Try to use SetThreadDescription if available + static SetThreadDescriptionFunc SetThreadDescription = reinterpret_cast(::GetProcAddress(::GetModuleHandleW(L"Kernel32.dll"), "SetThreadDescription")); + if (SetThreadDescription) + SetThreadDescription(threadHandle, ToWideString(threadName).data()); + else + RaiseThreadNameException(::GetThreadId(threadHandle), threadName); + } +} + +#include diff --git a/src/Nazara/Core/Win32/ThreadImpl.hpp b/src/Nazara/Core/Win32/ThreadImpl.hpp new file mode 100644 index 000000000..90963eecf --- /dev/null +++ b/src/Nazara/Core/Win32/ThreadImpl.hpp @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_CORE_WIN32_THREADIMPL_HPP +#define NAZARA_CORE_WIN32_THREADIMPL_HPP + +#include +#include +#include + +namespace Nz::PlatformImpl +{ + using ThreadHandle = HANDLE; + + HANDLE GetCurrentThreadHandle(); + std::string GetCurrentThreadName(); + std::string GetThreadName(HANDLE threadHandle); + void RaiseThreadNameException(DWORD threadId, const char* threadName); + void SetCurrentThreadName(const char* threadName); + void SetThreadName(HANDLE threadHandle, const char* threadName); +} + +#endif // NAZARA_CORE_WIN32_THREADIMPL_HPP