Skip to content

Commit 77c939c

Browse files
authored
Setting Thread.Name sets the native thread name on MacOS. (#47465)
* Setting Thread.Name sets the native thread name on MacOS. Setting Thread.Name will now set the name of the native thread on MacOS - due to API constraints this will not work cross thread. Fix #47087 * adding tests and formatting fixes
1 parent 4f3545e commit 77c939c

File tree

7 files changed

+165
-9
lines changed

7 files changed

+165
-9
lines changed

src/coreclr/pal/src/thread/thread.cpp

+25-8
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ typedef cpuset_t cpu_set_t;
9191

9292
using namespace CorUnix;
9393

94+
#ifdef __APPLE__
95+
#define MAX_THREAD_NAME_SIZE 63
96+
#else
97+
#define MAX_THREAD_NAME_SIZE 15
98+
#endif
9499

95100
/* ------------------- Definitions ------------------------------*/
96101

@@ -1622,13 +1627,14 @@ CorUnix::InternalSetThreadDescription(
16221627
PAL_ERROR palError = NO_ERROR;
16231628
CPalThread *pTargetThread = NULL;
16241629
IPalObject *pobjThread = NULL;
1625-
int error;
1630+
int error = 0;
1631+
int maxNameSize = 0;
16261632
int nameSize;
16271633
char *nameBuf = NULL;
16281634

16291635
// The exact API of pthread_setname_np varies very wildly depending on OS.
1630-
// For now, only Linux is implemented.
1631-
#if defined(__linux__)
1636+
// For now, only Linux and macOS are implemented.
1637+
#if defined(__linux__) || defined(__APPLE__)
16321638

16331639
palError = InternalGetThreadDataFromHandle(
16341640
pThread,
@@ -1675,13 +1681,24 @@ CorUnix::InternalSetThreadDescription(
16751681
}
16761682

16771683
// Null terminate early.
1678-
// pthread_setname_np only accepts up to 16 chars.
1679-
if (nameSize > 15)
1684+
// pthread_setname_np only accepts up to 16 chars on Linux and
1685+
// 64 chars on macOS.
1686+
if (nameSize > MAX_THREAD_NAME_SIZE)
16801687
{
1681-
nameBuf[15] = '\0';
1688+
nameBuf[MAX_THREAD_NAME_SIZE] = '\0';
16821689
}
1683-
1690+
1691+
#if defined(__linux__)
16841692
error = pthread_setname_np(pTargetThread->GetPThreadSelf(), nameBuf);
1693+
#endif
1694+
1695+
#if defined(__APPLE__)
1696+
// on macOS, pthread_setname_np only works for the calling thread.
1697+
if (PlatformGetCurrentThreadId() == pTargetThread->GetThreadId())
1698+
{
1699+
error = pthread_setname_np(nameBuf);
1700+
}
1701+
#endif
16851702

16861703
if (error != 0)
16871704
{
@@ -1704,7 +1721,7 @@ CorUnix::InternalSetThreadDescription(
17041721
PAL_free(nameBuf);
17051722
}
17061723

1707-
#endif // defined(__linux__)
1724+
#endif //defined(__linux__) || defined(__APPLE__)
17081725

17091726
return palError;
17101727
}

src/coreclr/pal/tests/palsuite/CMakeLists.txt

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ if(FEATURE_EVENT_TRACE)
3232
add_subdirectory(eventprovider)
3333
endif(FEATURE_EVENT_TRACE)
3434

35+
if (CLR_CMAKE_TARGET_OSX OR (CLR_CMAKE_TARGET_LINUX AND NOT CLR_CMAKE_TARGET_ALPINE_LINUX))
36+
set(PALSUITE_THREAD_NAME_SOURCES
37+
threading/SetThreadDescription/test1/pthread_helpers.cpp
38+
threading/SetThreadDescription/test1/test1.cpp
39+
)
40+
endif()
41+
3542
_add_executable(paltests
3643
paltests.cpp
3744
common/palsuite.cpp
@@ -918,7 +925,7 @@ _add_executable(paltests
918925
threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.cpp
919926
threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp
920927
threading/YieldProcessor/test1/test1.cpp
921-
928+
${PALSUITE_THREAD_NAME_SOURCES}
922929
)
923930

924931
add_dependencies(paltests coreclrpal)

src/coreclr/pal/tests/palsuite/compilableTests.txt

+2
Original file line numberDiff line numberDiff line change
@@ -772,11 +772,13 @@ threading/ResetEvent/test2/paltest_resetevent_test2
772772
threading/ResetEvent/test3/paltest_resetevent_test3
773773
threading/ResetEvent/test4/paltest_resetevent_test4
774774
threading/ResumeThread/test1/paltest_resumethread_test1
775+
threading/SwitchToThread/test1/paltest_switchtothread_test1
775776
threading/SetErrorMode/test1/paltest_seterrormode_test1
776777
threading/SetEvent/test1/paltest_setevent_test1
777778
threading/SetEvent/test2/paltest_setevent_test2
778779
threading/SetEvent/test3/paltest_setevent_test3
779780
threading/SetEvent/test4/paltest_setevent_test4
781+
threading/SetThreadDescription/test1/paltest_setthreaddescription_test1
780782
threading/SignalObjectAndWait/paltest_signalobjectandwaittest
781783
threading/Sleep/test1/paltest_sleep_test1
782784
threading/Sleep/test2/paltest_sleep_test2

src/coreclr/pal/tests/palsuite/paltestlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ threading/SetEvent/test1/paltest_setevent_test1
679679
threading/SetEvent/test2/paltest_setevent_test2
680680
threading/SetEvent/test3/paltest_setevent_test3
681681
threading/SetEvent/test4/paltest_setevent_test4
682+
threading/SetThreadDescription/test1/paltest_setthreaddescription_test1
682683
threading/SwitchToThread/test1/paltest_switchtothread_test1
683684
threading/ThreadPriority/test1/paltest_threadpriority_test1
684685
threading/WaitForMultipleObjects/test1/paltest_waitformultipleobjects_test1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#include <stdlib.h>
5+
#include <pthread.h>
6+
7+
#define NAMELEN 256
8+
char* GetThreadName()
9+
{
10+
char* threadName = (char*)malloc(sizeof(char) * NAMELEN);
11+
pthread_t thread = pthread_self();
12+
int rc = pthread_getname_np(thread, threadName, NAMELEN);
13+
if (rc != 0)
14+
{
15+
free(threadName);
16+
return NULL;
17+
}
18+
19+
return threadName;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
char* GetThreadName();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
/*============================================================
5+
**
6+
** Source: test1.cpp
7+
**
8+
** Purpose: Test for SetThreadDescription. Create a thread, call
9+
** SetThreadDescription, and then verify that the name of the thread
10+
** matches what was set.
11+
**
12+
**=========================================================*/
13+
14+
#include <palsuite.h>
15+
#include "pthread_helpers.hpp"
16+
17+
char * threadName;
18+
char * expectedThreadName;
19+
char * actualThreadName;
20+
21+
DWORD PALAPI SetThreadDescriptionTestThread(LPVOID lpParameter)
22+
{
23+
HANDLE palThread = GetCurrentThread();
24+
WCHAR wideThreadName[256];
25+
26+
MultiByteToWideChar(CP_ACP, 0, threadName, strlen(threadName)+1, wideThreadName, 256);
27+
SetThreadDescription(palThread, wideThreadName);
28+
actualThreadName = GetThreadName();
29+
30+
return 0;
31+
}
32+
33+
BOOL SetThreadDescriptionTest(char* name, char* expected)
34+
{
35+
BOOL bResult = FALSE;
36+
LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
37+
DWORD dwStackSize = 0;
38+
LPTHREAD_START_ROUTINE lpStartAddress = &SetThreadDescriptionTestThread;
39+
LPVOID lpParameter = (LPVOID)SetThreadDescriptionTestThread;
40+
DWORD dwCreationFlags = 0;
41+
DWORD dwThreadId = 0;
42+
43+
threadName = name;
44+
expectedThreadName = expected;
45+
46+
HANDLE hThread = CreateThread(lpThreadAttributes,
47+
dwStackSize, lpStartAddress, lpParameter,
48+
dwCreationFlags, &dwThreadId );
49+
50+
if (hThread != INVALID_HANDLE_VALUE)
51+
{
52+
WaitForSingleObject(hThread, INFINITE);
53+
bResult = strcmp(actualThreadName, expectedThreadName) == 0;
54+
}
55+
else
56+
{
57+
Trace("Unable to create SetThreadDescription test thread");
58+
}
59+
60+
return bResult;
61+
}
62+
63+
BOOL SetThreadDescriptionTests()
64+
{
65+
if (!SetThreadDescriptionTest("Hello, World", "Hello, World"))
66+
{
67+
Trace("Setting thread name failed");
68+
return FAIL;
69+
}
70+
71+
// verify that thread name truncations works correctly on linux on macOS.
72+
char * threadName = "aaaaaaa_15chars_aaaaaaa_31chars_aaaaaaaaaaaaaaaaaaaaaaa_63chars_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
73+
char * expected;
74+
#if defined(__APPLE__)
75+
expected = "aaaaaaa_15chars_aaaaaaa_31chars_aaaaaaaaaaaaaaaaaaaaaaa_63chars";
76+
#else
77+
expected = "aaaaaaa_15chars";
78+
#endif
79+
80+
if (!SetThreadDescriptionTest(threadName, expected))
81+
{
82+
return PASS;
83+
}
84+
85+
return FAIL;
86+
}
87+
88+
PALTEST(threading_SetThreadDescription_test1_paltest_setthreaddescription_test1, "threading/SetThreadDescription/test1/paltest_setthreaddescription_test1")
89+
{
90+
if (0 != (PAL_Initialize(argc, argv)))
91+
{
92+
return FAIL;
93+
}
94+
95+
BOOL result = SetThreadDescriptionTests();
96+
if(actualThreadName) free(actualThreadName);
97+
if (!result)
98+
{
99+
Fail("Test Failed");
100+
}
101+
102+
PAL_Terminate();
103+
return PASS;
104+
}
105+

0 commit comments

Comments
 (0)