Skip to content

Commit

Permalink
Add functions to set/get the active thread name
Browse files Browse the repository at this point in the history
  • Loading branch information
dacap committed Aug 29, 2023
1 parent 7847847 commit 99225ef
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 0 deletions.
75 changes: 75 additions & 0 deletions base/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,46 @@
#include "base/thread.h"

#if LAF_WINDOWS
#include "base/dll.h"
#include "base/string.h"

#include <windows.h>
#include <process.h>
#include <mutex>
#else
#include <pthread.h> // Use pthread library in Unix-like systems

#include <unistd.h>
#include <sys/time.h>
#include <algorithm>
#endif

#if LAF_WINDOWS
namespace {

class KernelBaseApi {
public:
using SetThreadDescription_Func = HRESULT(WINAPI*)(HANDLE, PCWSTR);
using GetThreadDescription_Func = HRESULT(WINAPI*)(HANDLE, PWSTR*);

SetThreadDescription_Func SetThreadDescription = nullptr;
GetThreadDescription_Func GetThreadDescription = nullptr;

KernelBaseApi() {
m_dll = base::load_dll("KernelBase.dll");
if (m_dll) {
SetThreadDescription = base::get_dll_proc<SetThreadDescription_Func>(m_dll, "SetThreadDescription");
GetThreadDescription = base::get_dll_proc<GetThreadDescription_Func>(m_dll, "GetThreadDescription");
}
}

private:
base::dll m_dll;
};

KernelBaseApi kernelBaseApi;

} // anonymous namespace
#endif

namespace base {
Expand Down Expand Up @@ -56,4 +89,46 @@ void this_thread::sleep_for(double seconds)
#endif
}

void this_thread::set_name(const std::string& name)
{
#if LAF_WINDOWS
if (kernelBaseApi.SetThreadDescription)
kernelBaseApi.SetThreadDescription(GetCurrentThread(),
base::from_utf8(name).c_str());
#elif LAF_MACOS
// macOS has a non-standard pthread_setname_np() impl
pthread_setname_np(name.c_str());
#else
int res = pthread_setname_np(pthread_self(), name.c_str());
if (res != 0 && name.size() > 15) {
// Try with a shorter string (no more than 16 chars including the
// null char, as the spec says).
pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
}
#endif
}

std::string this_thread::get_name()
{
#if LAF_WINDOWS
if (kernelBaseApi.GetThreadDescription) {
PWSTR desc = nullptr;
HRESULT hr = kernelBaseApi.GetThreadDescription(GetCurrentThread(), &desc);
if (SUCCEEDED(hr) && desc) {
std::string result(base::to_utf8(desc));
LocalFree(desc);
return result;
}
}
#else
char name[65];
int result = pthread_getname_np(pthread_self(), name, sizeof(name)-1);
if (result == 0) { // Returns 0 if it was successful
// pthread_getname_np() returns a null terminated name.
return std::string(name);
}
#endif
return std::string();
}

} // namespace base
8 changes: 8 additions & 0 deletions base/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#define BASE_THREAD_H_INCLUDED
#pragma once

#include <string>

namespace base {
namespace this_thread {

Expand All @@ -17,6 +19,12 @@ void yield();
// TODO replace with std::this_thread::sleep_for(std::chrono::seconds(...)) or similar
void sleep_for(double seconds);

// Associates a name/description to the current thread. Useful for
// debugging purposes. E.g. When we receive a crash dump from Sentry
// we can identify a thread by its name.
void set_name(const std::string& name);
std::string get_name();

} // this_thread
} // base

Expand Down
50 changes: 50 additions & 0 deletions base/thread_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// LAF Base Library
// Copyright (c) 2023 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

#include <gtest/gtest.h>

#include "base/thread.h"

using namespace base;

TEST(Thread, SetGetName)
{
// Empty string by default
EXPECT_EQ("", this_thread::get_name());

this_thread::set_name("main");
EXPECT_EQ("main", this_thread::get_name());

this_thread::set_name("testing");
EXPECT_EQ("testing", this_thread::get_name());
}

TEST(Thread, NameLimits)
{
const char* fullName = "123456789012345678901234567890"
"123456789012345678901234567890"
"123456789012345678901234567890";

this_thread::set_name(fullName);

#if LAF_WINDOWS
EXPECT_EQ(fullName, this_thread::get_name());
#elif LAF_APPLE
// Limited to 64 chars
EXPECT_EQ("123456789012345678901234567890"
"123456789012345678901234567890"
"1234", this_thread::get_name());
#else
// Limited to 16 chars (including the null char)
EXPECT_EQ("123456789012345", this_thread::get_name());
#endif
}

int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit 99225ef

Please sign in to comment.