1  
//
1  
//
2  
// Copyright (c) 2026 Michael Vandeberg
2  
// Copyright (c) 2026 Michael Vandeberg
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#include <boost/capy/test/thread_name.hpp>
10  
#include <boost/capy/test/thread_name.hpp>
11  

11  

12  
#if defined(_WIN32)
12  
#if defined(_WIN32)
13  

13  

14  
#ifndef NOMINMAX
14  
#ifndef NOMINMAX
15  
#define NOMINMAX
15  
#define NOMINMAX
16  
#endif
16  
#endif
17  
#include <windows.h>
17  
#include <windows.h>
18  
#include <string>
18  
#include <string>
19  

19  

20  
#elif defined(__APPLE__)
20  
#elif defined(__APPLE__)
21  

21  

22  
#include <pthread.h>
22  
#include <pthread.h>
23  
#include <cstring>
23  
#include <cstring>
24  

24  

25  
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
25  
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
26  

26  

27  
#include <pthread.h>
27  
#include <pthread.h>
28  
#include <cstring>
28  
#include <cstring>
29  

29  

30  
#endif
30  
#endif
31  

31  

32  
/*
32  
/*
33  
    Platform-specific thread naming implementation.
33  
    Platform-specific thread naming implementation.
34  

34  

35  
    Each platform has a different API and name length limit:
35  
    Each platform has a different API and name length limit:
36  
    - Windows: SetThreadDescription with UTF-8 to UTF-16 conversion (no limit)
36  
    - Windows: SetThreadDescription with UTF-8 to UTF-16 conversion (no limit)
37  
    - macOS: pthread_setname_np(name) with 63-char limit
37  
    - macOS: pthread_setname_np(name) with 63-char limit
38  
    - Linux/BSD: pthread_setname_np(thread, name) with 15-char limit
38  
    - Linux/BSD: pthread_setname_np(thread, name) with 15-char limit
39  

39  

40  
    All operations are best-effort and silently fail on error, since thread
40  
    All operations are best-effort and silently fail on error, since thread
41  
    naming is purely for debugging visibility and should never affect program
41  
    naming is purely for debugging visibility and should never affect program
42  
    correctness. The noexcept guarantee is maintained by catching exceptions
42  
    correctness. The noexcept guarantee is maintained by catching exceptions
43  
    from std::wstring allocation on Windows.
43  
    from std::wstring allocation on Windows.
44  
*/
44  
*/
45  

45  

46  
namespace boost {
46  
namespace boost {
47  
namespace capy {
47  
namespace capy {
48  

48  

49  
void
49  
void
50  
set_current_thread_name(char const* name) noexcept
50  
set_current_thread_name(char const* name) noexcept
51  
{
51  
{
52  
#if defined(_WIN32)
52  
#if defined(_WIN32)
53  
    // SetThreadDescription requires Windows 10 1607+. Older Windows versions
53  
    // SetThreadDescription requires Windows 10 1607+. Older Windows versions
54  
    // are unsupported; the program may fail to link on those systems.
54  
    // are unsupported; the program may fail to link on those systems.
55  

55  

56  
    // Query required buffer size for UTF-8 to wide conversion.
56  
    // Query required buffer size for UTF-8 to wide conversion.
57  
    int required = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
57  
    int required = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
58  
    if(required <= 0)
58  
    if(required <= 0)
59  
        return;
59  
        return;
60  

60  

61  
    // Allocate and convert; catch exceptions to maintain noexcept.
61  
    // Allocate and convert; catch exceptions to maintain noexcept.
62  
    std::wstring wname;
62  
    std::wstring wname;
63  
    try
63  
    try
64  
    {
64  
    {
65  
        wname.resize(static_cast<std::size_t>(required));
65  
        wname.resize(static_cast<std::size_t>(required));
66  
    }
66  
    }
67  
    catch(...)
67  
    catch(...)
68  
    {
68  
    {
69  
        return;
69  
        return;
70  
    }
70  
    }
71  

71  

72  
    if(MultiByteToWideChar(CP_UTF8, 0, name, -1, wname.data(), required) <= 0)
72  
    if(MultiByteToWideChar(CP_UTF8, 0, name, -1, wname.data(), required) <= 0)
73  
        return;
73  
        return;
74  

74  

75  
    // Ignore return value: thread naming is best-effort for debugging.
75  
    // Ignore return value: thread naming is best-effort for debugging.
76  
    (void)SetThreadDescription(GetCurrentThread(), wname.c_str());
76  
    (void)SetThreadDescription(GetCurrentThread(), wname.c_str());
77  
#elif defined(__APPLE__)
77  
#elif defined(__APPLE__)
78  
    // macOS pthread_setname_np takes only the name (no thread handle)
78  
    // macOS pthread_setname_np takes only the name (no thread handle)
79  
    // and has a 64 char limit (63 + null terminator)
79  
    // and has a 64 char limit (63 + null terminator)
80  
    char truncated[64];
80  
    char truncated[64];
81  
    std::strncpy(truncated, name, 63);
81  
    std::strncpy(truncated, name, 63);
82  
    truncated[63] = '\0';
82  
    truncated[63] = '\0';
83  

83  

84  
    // Ignore return value: thread naming is best-effort for debugging.
84  
    // Ignore return value: thread naming is best-effort for debugging.
85  
    (void)pthread_setname_np(truncated);
85  
    (void)pthread_setname_np(truncated);
86  
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
86  
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
87  
    // pthread_setname_np has 16 char limit (15 + null terminator)
87  
    // pthread_setname_np has 16 char limit (15 + null terminator)
88  
    char truncated[16];
88  
    char truncated[16];
89  
    std::strncpy(truncated, name, 15);
89  
    std::strncpy(truncated, name, 15);
90  
    truncated[15] = '\0';
90  
    truncated[15] = '\0';
91  

91  

92  
    // Ignore return value: thread naming is best-effort for debugging.
92  
    // Ignore return value: thread naming is best-effort for debugging.
93  
    (void)pthread_setname_np(pthread_self(), truncated);
93  
    (void)pthread_setname_np(pthread_self(), truncated);
94  
#else
94  
#else
95  
    (void)name;
95  
    (void)name;
96  
#endif
96  
#endif
97  
}
97  
}
98  

98  

99  
} // capy
99  
} // capy
100  
} // boost
100  
} // boost