add api for getting/setting thread names

This commit is contained in:
ConfiG 2024-01-28 15:33:33 +03:00
parent 5498ecb015
commit ce53fb3135
No known key found for this signature in database
GPG key ID: 44DA1983F524C11B
8 changed files with 120 additions and 2 deletions

View file

@ -141,3 +141,9 @@ namespace geode::utils::game {
GEODE_DLL void restart();
GEODE_DLL void launchLoaderUninstaller(bool deleteSaveData);
}
namespace geode::utils::thread {
GEODE_DLL std::string getName();
GEODE_DLL void setName(std::string const& name);
GEODE_DLL void setName(std::wstring const& wName);
}

View file

@ -94,6 +94,7 @@ void tryShowForwardCompat() {
}
int geodeEntry(void* platformData) {
thread::setName("Main Thread");
log::Logger::get()->setup();
std::string forwardCompatSuffix;

View file

@ -363,3 +363,7 @@ void geode::utils::permission::requestPermission(Permission permission, utils::M
clearJNIException();
}
}
#include "../../utils/thread.hpp"
void geode::utils::thread::platformSetNameOrdinary(std::string const& name) { }
void geode::utils::thread::platformSetNameWide(std::wstring const& wName) { }

View file

@ -309,3 +309,7 @@ bool geode::utils::permission::getPermissionStatus(Permission permission) {
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) {
callback(true); // unimplemented
}
#include "../../utils/thread.hpp"
void geode::utils::thread::platformSetNameOrdinary(std::string const& name) { }
void geode::utils::thread::platformSetNameWide(std::wstring const& wName) { }

View file

@ -10,6 +10,8 @@ using namespace geode::prelude;
static constexpr auto IPC_BUFFER_SIZE = 512;
void ipcPipeThread(HANDLE pipe) {
thread::setName("Geode IPC Pipe");
char buffer[IPC_BUFFER_SIZE * sizeof(TCHAR)];
DWORD read;
@ -35,6 +37,7 @@ void ipcPipeThread(HANDLE pipe) {
void ipc::setup() {
std::thread ipcThread([]() {
thread::setName("Geode Main IPC");
while (true) {
auto pipe = CreateNamedPipeA(
ipc::IPC_PIPE_NAME,
@ -59,7 +62,6 @@ void ipc::setup() {
if (ConnectNamedPipe(pipe, nullptr)) {
// log::debug("Got connection, creating thread");
std::thread pipeThread(&ipcPipeThread, pipe);
// SetThreadDescription(pipeThread.native_handle(), L"Geode IPC Pipe");
pipeThread.detach();
}
else {
@ -68,7 +70,6 @@ void ipc::setup() {
}
}
});
// SetThreadDescription(ipcThread.native_handle(), L"Geode Main IPC");
ipcThread.detach();
log::debug("IPC set up");

View file

@ -6,6 +6,7 @@ using namespace geode::prelude;
#include "nfdwin.hpp"
#include <ghc/fs_fwd.hpp>
#include <Windows.h>
#include <processthreadsapi.h>
#include <iostream>
#include <ShlObj.h>
#include <shlwapi.h>
@ -277,3 +278,42 @@ bool geode::utils::permission::getPermissionStatus(Permission permission) {
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) {
callback(true); // unimplemented
}
#include "../../utils/thread.hpp"
// https://learn.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code?view=vs-2022
#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)
// the SetThreadDescription and exception methods are needed for the thread names to show up
// in places like task managers and debuggers
auto setThreadDesc = reinterpret_cast<decltype(&SetThreadDescription)>(GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription"));
void geode::utils::thread::platformSetNameWide(std::wstring const& wName) {
// SetThreadDescription
if (!setThreadDesc)
return;
auto res = setThreadDesc(GetCurrentThread(), wName.c_str());
if (FAILED(res))
log::warn("Native method to set thread name failed ({}), using only fallback methods.", res);
}
void geode::utils::thread::platformSetNameOrdinary(std::string const& name) {
// exception
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name.c_str();
info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try {
RaiseException(0x406d1388, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
}
__except (EXCEPTION_EXECUTE_HANDLER) { }
#pragma warning(pop)
}

View file

@ -0,0 +1,54 @@
#include <Geode/DefaultInclude.hpp>
using namespace geode::prelude;
#include <Geode/Utils.hpp>
#include "thread.hpp"
static thread_local std::string s_threadName;
std::string geode::utils::thread::getName() {
// only use the thread-local variable here, no need for platform get methods
if (s_threadName.empty())
return fmt::format("Thread #{}", GetThreadId(GetCurrentThread()));
return s_threadName;
}
void geode::utils::thread::setName(std::string const& name) {
std::wstring wName(name.size(), L' ');
size_t numConv;
auto res = mbstowcs_s(&numConv, wName.data(), sizeof(wchar_t) * wName.size(), name.data(), wName.size());
if (res == EINVAL) {
log::warn(
"Native method to set thread name failed (EINVAL), using only fallback methods.");
}
else if (res == ERANGE) {
log::warn(
"Native method to set thread name failed (ERANGE), using only fallback methods.");
}
else {
platformSetNameWide(wName);
}
platformSetNameOrdinary(name);
s_threadName = name;
}
void geode::utils::thread::setName(std::wstring const& wName) {
platformSetNameWide(wName);
std::string name(wName.size(), ' ');
size_t numConv;
auto res = wcstombs_s(&numConv, name.data(), name.size(), wName.data(), name.size());
if (res == EINVAL) {
log::warn("Fallback method to set thread name failed (EINVAL).");
return;
}
if (res == ERANGE) {
log::warn("Fallback method to set thread name failed (ERANGE).");
return;
}
platformSetNameOrdinary(name);
s_threadName = name;
}

View file

@ -0,0 +1,8 @@
#pragma once
#include <string>
namespace geode::utils::thread {
void platformSetNameOrdinary(std::string const& name);
void platformSetNameWide(std::wstring const& wName);
}