From edffd7f826c01894e3a97362f35d2f5b07a5240d Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 5 Nov 2023 03:13:36 +0100 Subject: [PATCH 1/2] Set custom names for all of our threads This is helpful when debugging, especially with all of the IO worker threads created by the asio-based socket code. --- Sources/Plasma/Apps/plClient/plClientLoader.cpp | 2 ++ Sources/Plasma/Apps/plClient/win32/winmain.cpp | 1 + .../Apps/plUruLauncher/plClientLauncher.cpp | 4 ++++ Sources/Plasma/CoreLib/hsThread.cpp | 1 + Sources/Plasma/CoreLib/hsThread.h | 5 +++++ Sources/Plasma/CoreLib/hsThread_Unix.cpp | 17 +++++++++++++++++ Sources/Plasma/CoreLib/hsThread_Win.cpp | 15 +++++++++++++++ .../Plasma/FeatureLib/pfPatcher/pfPatcher.cpp | 1 + .../NucleusLib/pnAsyncCoreExe/pnAceDns.cpp | 1 + .../NucleusLib/pnAsyncCoreExe/pnAceSocket.cpp | 5 ++++- .../NucleusLib/pnAsyncCoreExe/pnAceThread.cpp | 2 ++ .../NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp | 1 + .../PubUtilLib/plAudioCore/plSoundBuffer.cpp | 2 ++ .../Tools/MaxComponent/plPickMaterialMap.cpp | 1 + 14 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Sources/Plasma/Apps/plClient/plClientLoader.cpp b/Sources/Plasma/Apps/plClient/plClientLoader.cpp index 114b3c0724..a57a6b42bd 100644 --- a/Sources/Plasma/Apps/plClient/plClientLoader.cpp +++ b/Sources/Plasma/Apps/plClient/plClientLoader.cpp @@ -53,6 +53,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com void plClientLoader::Run() { + SetThisThreadName(ST_LITERAL("plClientLoader")); + plResManager *resMgr = new plResManager; resMgr->SetDataPath("dat"); hsgResMgr::Init(resMgr); diff --git a/Sources/Plasma/Apps/plClient/win32/winmain.cpp b/Sources/Plasma/Apps/plClient/win32/winmain.cpp index db80f092fc..58ccae7fa9 100644 --- a/Sources/Plasma/Apps/plClient/win32/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/win32/winmain.cpp @@ -743,6 +743,7 @@ INT_PTR CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPA { s_loginDlgRunning = true; s_statusThread = std::thread([hwndDlg]() { + hsThread::SetThisThreadName(ST_LITERAL("LoginDialogShardStatus")); #ifdef USE_VLD VLDEnable(); #endif diff --git a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp index a86f8d6928..36a0110198 100644 --- a/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp +++ b/Sources/Plasma/Apps/plUruLauncher/plClientLauncher.cpp @@ -105,6 +105,8 @@ static size_t ICurlCallback(void* buffer, size_t size, size_t nmemb, void* threa void plShardStatus::Run() { + SetThisThreadName(ST_LITERAL("plShardStatus")); + ST::string url = GetServerStatusUrl(); // initialize CURL @@ -180,6 +182,8 @@ class plRedistUpdater : public hsThread void Run() override { + SetThisThreadName(ST_LITERAL("plRedistUpdater")); + while (!fRedistQueue.empty()) { if (fInstallProc(fRedistQueue.back())) fRedistQueue.pop_back(); diff --git a/Sources/Plasma/CoreLib/hsThread.cpp b/Sources/Plasma/CoreLib/hsThread.cpp index 0614f40d71..e32d4ca97a 100644 --- a/Sources/Plasma/CoreLib/hsThread.cpp +++ b/Sources/Plasma/CoreLib/hsThread.cpp @@ -52,6 +52,7 @@ void hsThread::Start() { if (!fThread.joinable()) { fThread = std::thread([this]() { + hsThread::SetThisThreadName(ST_LITERAL("hsNoNameThread")); #ifdef USE_VLD // Needs to be enabled for each thread except the WinMain VLDEnable(); diff --git a/Sources/Plasma/CoreLib/hsThread.h b/Sources/Plasma/CoreLib/hsThread.h index b4cfdb9941..0306d88ece 100644 --- a/Sources/Plasma/CoreLib/hsThread.h +++ b/Sources/Plasma/CoreLib/hsThread.h @@ -100,6 +100,11 @@ class hsThread // WILL NOT stop a detached thread! void StartDetached(); + // Set a name for the current thread, to be displayed in debuggers and such. + // If possible, don't use names longer than 15 characters, + // because Linux has a really low limit. + static void SetThisThreadName(const ST::string& name); + static inline size_t ThisThreadHash() { return std::hash()(std::this_thread::get_id()); diff --git a/Sources/Plasma/CoreLib/hsThread_Unix.cpp b/Sources/Plasma/CoreLib/hsThread_Unix.cpp index ee3682510e..0093d16079 100644 --- a/Sources/Plasma/CoreLib/hsThread_Unix.cpp +++ b/Sources/Plasma/CoreLib/hsThread_Unix.cpp @@ -75,6 +75,23 @@ int clock_gettime(int clocktype, struct timespec* ts) ///////////////////////////////////////////////////////////////////////////// +void hsThread::SetThisThreadName(const ST::string& name) +{ +#if defined(HS_BUILD_FOR_APPLE) + // The Apple version doesn't take a thread argument and always operates on the current thread. + int res = pthread_setname_np(name.c_str()); + hsAssert(res == 0, "Failed to set thread name"); +#elif defined(HS_BUILD_FOR_LINUX) + // On Linux, thread names must fit into 16 bytes, including the terminator. + char buf[16]; + strncpy(buf, name.c_str(), sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + int res = pthread_setname_np(pthread_self(), buf); + hsAssert(res == 0, "Failed to set thread name"); +#endif + // Because this is just a debugging help, do nothing by default (sorry, BSDs). +} + hsGlobalSemaphore::hsGlobalSemaphore(int initialValue, const ST::string& name) { #ifdef USE_SEMA diff --git a/Sources/Plasma/CoreLib/hsThread_Win.cpp b/Sources/Plasma/CoreLib/hsThread_Win.cpp index 7a366fad3d..f00663bfc0 100644 --- a/Sources/Plasma/CoreLib/hsThread_Win.cpp +++ b/Sources/Plasma/CoreLib/hsThread_Win.cpp @@ -46,6 +46,21 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsThread.h" #include "hsExceptions.h" +void hsThread::SetThisThreadName(const ST::string& name) +{ + // SetThreadDescription is only supported since Windows 10 v1607. + // There's an alternative solution that also works on older versions, + // but it's specific to Visual Studio and the "interface" is really gnarly. + // Because this is just a debugging help, that probably isn't worth the effort. + // https://learn.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2022#set-a-thread-name-by-throwing-an-exception + static plOptionalWinCall SetThreadDescription(L"KernelBase", "SetThreadDescription"); + + auto result = SetThreadDescription(GetCurrentThread(), name.to_wchar().c_str()); + if (result) { + hsAssert(SUCCEEDED(*result), ST::format("Failed to set thread name: {}", hsCOMError(*result)).c_str()); + } +} + hsGlobalSemaphore::hsGlobalSemaphore(int initialValue, const ST::string& name) { fSemaH = ::CreateSemaphoreW(nullptr, initialValue, std::numeric_limits::max(), name.to_wchar().data()); diff --git a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp index 4da76ea590..c6171923d2 100644 --- a/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp +++ b/Sources/Plasma/FeatureLib/pfPatcher/pfPatcher.cpp @@ -525,6 +525,7 @@ bool pfPatcherWorker::IssueRequest() void pfPatcherWorker::Run() { + SetThisThreadName(ST_LITERAL("pfPatcherWorker")); // So here's the rub: // We have one or many manifests in the fRequests deque. We begin issuing those requests one-by one, starting here. // As we receive the answer, the NetCli thread populates fQueuedFiles and pings the fFileSignal semaphore, then issues the next request... diff --git a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceDns.cpp b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceDns.cpp index 6aa2291701..7a7af37225 100644 --- a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceDns.cpp +++ b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceDns.cpp @@ -56,6 +56,7 @@ struct DnsResolver { // Start the resolver thread fLookupThread = AsyncThreadCreate([this] { + hsThread::SetThisThreadName(ST_LITERAL("AceDnsResolver")); fContext.run(); }); } diff --git a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceSocket.cpp b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceSocket.cpp index 6d38c5d27c..eb52011dad 100644 --- a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceSocket.cpp +++ b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceSocket.cpp @@ -71,11 +71,14 @@ struct AsyncIoPool } // create IO worker threads + size_t i = 0; for (auto& threadHandle : fThreadHandles) { - threadHandle = AsyncThreadCreate([this] { + threadHandle = AsyncThreadCreate([this, i] { + hsThread::SetThisThreadName(ST::format("AceSocketPool{02d}", i)); // This can be run concurrently from several threads fContext.run(); }); + i++; } } diff --git a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp index 339e1f9ba3..1fd4d0a77a 100644 --- a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp +++ b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp @@ -49,6 +49,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com static void CreateThreadProc(AsyncThreadRef thread) { + hsThread::SetThisThreadName(ST_LITERAL("NoNameAceThread")); + #ifdef USE_VLD VLDEnable(); #endif diff --git a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp index 25b6e088c1..75fa866577 100644 --- a/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp +++ b/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp @@ -71,6 +71,7 @@ struct AsyncTimerManager AsyncTimerManager() : fWorkGuard(fContext.get_executor()) { fTimerThread = AsyncThreadCreate([this] { + hsThread::SetThisThreadName(ST_LITERAL("AceTimerMgr")); fContext.run(); }); } diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp index ecc39f72ed..8ea715b2c9 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp @@ -83,6 +83,8 @@ static plAudioFileReader *CreateReader( bool fullpath, const plFileName &filenam void plSoundPreloader::Run() { + SetThisThreadName(ST_LITERAL("SoundPreloader")); + std::vector templist; while (fRunning) diff --git a/Sources/Tools/MaxComponent/plPickMaterialMap.cpp b/Sources/Tools/MaxComponent/plPickMaterialMap.cpp index f2f3686d24..0439f8c4b7 100644 --- a/Sources/Tools/MaxComponent/plPickMaterialMap.cpp +++ b/Sources/Tools/MaxComponent/plPickMaterialMap.cpp @@ -69,6 +69,7 @@ class hsHackWinFindThread : public hsThread public: void Run() override { + SetThisThreadName(ST_LITERAL("hsHackWinFindThread")); while (1) { HWND hMtlDlg = FindWindow(nullptr, _T("Material/Map Browser")); From bdadd6999c1e262e25b0cd4dd21c620ff1c5a7ab Mon Sep 17 00:00:00 2001 From: dgelessus Date: Sun, 5 Nov 2023 12:28:37 +0100 Subject: [PATCH 2/2] Apply suggestion from code review Co-authored-by: Adam Johnson --- Sources/Plasma/CoreLib/hsThread_Unix.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/Plasma/CoreLib/hsThread_Unix.cpp b/Sources/Plasma/CoreLib/hsThread_Unix.cpp index 0093d16079..e704f3574c 100644 --- a/Sources/Plasma/CoreLib/hsThread_Unix.cpp +++ b/Sources/Plasma/CoreLib/hsThread_Unix.cpp @@ -83,10 +83,7 @@ void hsThread::SetThisThreadName(const ST::string& name) hsAssert(res == 0, "Failed to set thread name"); #elif defined(HS_BUILD_FOR_LINUX) // On Linux, thread names must fit into 16 bytes, including the terminator. - char buf[16]; - strncpy(buf, name.c_str(), sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - int res = pthread_setname_np(pthread_self(), buf); + int res = pthread_setname_np(pthread_self(), name.left(15).c_str()); hsAssert(res == 0, "Failed to set thread name"); #endif // Because this is just a debugging help, do nothing by default (sorry, BSDs).