Skip to content

Commit 2567d3f

Browse files
committed
add thread_name()
1 parent 2479ded commit 2567d3f

File tree

4 files changed

+386
-0
lines changed

4 files changed

+386
-0
lines changed

include/spdlog/details/os-inl.h

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
#include <fcntl.h>
4343
#include <unistd.h>
44+
#include <pthread.h> // for pthread_getname_np
4445

4546
#ifdef __linux__
4647
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
@@ -367,6 +368,127 @@ SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT {
367368
#endif
368369
}
369370

371+
// Helper function to check if current thread is the main thread
372+
SPDLOG_INLINE bool is_main_thread() SPDLOG_NOEXCEPT {
373+
// Cache the main thread ID for performance, but determine it more reliably
374+
static std::thread::id cached_main_thread_id;
375+
static bool main_thread_id_initialized = false;
376+
377+
if (!main_thread_id_initialized) {
378+
// Determine main thread ID using platform-specific reliable methods
379+
#ifdef __APPLE__
380+
// On macOS, check if current thread is main using pthread_main_np()
381+
if (pthread_main_np() != 0) {
382+
cached_main_thread_id = std::this_thread::get_id();
383+
main_thread_id_initialized = true;
384+
}
385+
#elif defined(__linux__)
386+
// On Linux, check if current thread is main (PID == TID)
387+
if (getpid() == static_cast<pid_t>(syscall(SYS_gettid))) {
388+
cached_main_thread_id = std::this_thread::get_id();
389+
main_thread_id_initialized = true;
390+
}
391+
#elif defined(_WIN32)
392+
// On Windows, we can check if current thread is main thread
393+
// Main thread ID is the same as process ID in Windows
394+
DWORD current_tid = GetCurrentThreadId();
395+
DWORD process_id = GetCurrentProcessId();
396+
397+
// In Windows, the main thread ID is typically the same as the process ID
398+
// or we can check if this is the first thread created
399+
if (current_tid == process_id) {
400+
cached_main_thread_id = std::this_thread::get_id();
401+
} else {
402+
// Alternative method: Check if we're in the main thread by examining
403+
// thread creation order or using GetMainThreadId (Windows 10+)
404+
// For compatibility, we'll use a heuristic approach
405+
static bool first_call = true;
406+
if (first_call) {
407+
cached_main_thread_id = std::this_thread::get_id();
408+
first_call = false;
409+
}
410+
}
411+
main_thread_id_initialized = true;
412+
#else
413+
// For other platforms, use the original approach
414+
// Assume first caller is from main thread (works in most cases)
415+
cached_main_thread_id = std::this_thread::get_id();
416+
main_thread_id_initialized = true;
417+
#endif
418+
}
419+
420+
// Fast comparison using cached thread ID
421+
if (main_thread_id_initialized) {
422+
return std::this_thread::get_id() == cached_main_thread_id;
423+
}
424+
return false;
425+
}
426+
427+
// Return current thread name as string
428+
SPDLOG_INLINE std::string thread_name() SPDLOG_NOEXCEPT {
429+
#ifdef _WIN32
430+
// Windows implementation using GetThreadDescription (Windows 10 version 1607+)
431+
HANDLE hThread = GetCurrentThread();
432+
PWSTR threadName = nullptr;
433+
434+
// GetThreadDescription is available from Windows 10 version 1607
435+
typedef HRESULT(WINAPI* GetThreadDescriptionFunc)(HANDLE, PWSTR*);
436+
static GetThreadDescriptionFunc getThreadDescFunc = nullptr;
437+
static bool initialized = false;
438+
439+
if (!initialized) {
440+
HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
441+
if (kernel32) {
442+
getThreadDescFunc = reinterpret_cast<GetThreadDescriptionFunc>(
443+
GetProcAddress(kernel32, "GetThreadDescription"));
444+
}
445+
initialized = true;
446+
}
447+
448+
if (getThreadDescFunc && SUCCEEDED(getThreadDescFunc(hThread, &threadName)) && threadName) {
449+
// Convert wide string to narrow string
450+
int size = WideCharToMultiByte(CP_UTF8, 0, threadName, -1, nullptr, 0, nullptr, nullptr);
451+
if (size > 0) {
452+
std::string result(size - 1, '\0');
453+
WideCharToMultiByte(CP_UTF8, 0, threadName, -1, &result[0], size, nullptr, nullptr);
454+
LocalFree(threadName);
455+
return result;
456+
}
457+
LocalFree(threadName);
458+
}
459+
460+
// Fallback: return "main" for main thread, thread ID as string for others
461+
return is_main_thread() ? "main" : std::to_string(thread_id());
462+
463+
#elif defined(__linux__) || defined(__APPLE__)
464+
// Linux and macOS implementation using pthread_getname_np
465+
char thread_name_buf[16]; // Linux thread names are limited to 16 characters including null terminator
466+
467+
#ifdef __APPLE__
468+
// macOS version
469+
if (pthread_getname_np(pthread_self(), thread_name_buf, sizeof(thread_name_buf)) == 0) {
470+
if (thread_name_buf[0] != '\0') {
471+
return std::string(thread_name_buf);
472+
}
473+
}
474+
#else
475+
// Linux version
476+
if (pthread_getname_np(pthread_self(), thread_name_buf, sizeof(thread_name_buf)) == 0) {
477+
if (thread_name_buf[0] != '\0') {
478+
return std::string(thread_name_buf);
479+
}
480+
}
481+
#endif
482+
483+
// Fallback: return "main" for main thread, thread ID as string for others
484+
return is_main_thread() ? "main" : std::to_string(thread_id());
485+
486+
#else
487+
// Other Unix systems: return "main" for main thread, thread ID for others
488+
return is_main_thread() ? "main" : std::to_string(thread_id());
489+
#endif
490+
}
491+
370492
// This is avoid msvc issue in sleep_for that happens if the clock changes.
371493
// See https://github.com/gabime/spdlog/issues/609
372494
SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT {

include/spdlog/details/os.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
7373
// Return current thread id as size_t (from thread local storage)
7474
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
7575

76+
// Return current thread name as string
77+
SPDLOG_API std::string thread_name() SPDLOG_NOEXCEPT;
78+
7679
// This is avoid msvc issue in sleep_for that happens if the clock changes.
7780
// See https://github.com/gabime/spdlog/issues/609
7881
SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(SPDLOG_UTESTS_SOURCES
3232
test_file_logging.cpp
3333
test_daily_logger.cpp
3434
test_misc.cpp
35+
test_thread_name.cpp
3536
test_eventlog.cpp
3637
test_pattern_formatter.cpp
3738
test_async.cpp

0 commit comments

Comments
 (0)