Skip to content

Commit cc7d7c1

Browse files
committed
[macOS] Threading: implement set_name
Macos has affinity requesting if using the policy for it, there is no dictating to the threads, it's only requesting. Create a signal handler with a feedback flag and yield until we're sure the name is set (probably only needed for the unit_test because it doesn't give the mac thread scheduler a chance)
1 parent 89074e2 commit cc7d7c1

File tree

1 file changed

+41
-26
lines changed

1 file changed

+41
-26
lines changed

src/libs/shared/shared/threading/Utility.cpp

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include <cwchar>
2020
#elif defined TARGET_OS_MAC
2121
#include <pthread.h>
22+
#include <mach/mach.h>
23+
#include <mach/thread_policy.h>
24+
#include <signal.h>
25+
#include <atomic>
26+
#include <chrono>
2227
#elif defined __linux__ || defined __unix__
2328
#include <sched.h>
2429
#include <pthread.h>
@@ -92,53 +97,68 @@ Result set_name([[maybe_unused]] auto& handle, const char* name) {
9297
FreeLibrary(lib);
9398

9499
#elif defined TARGET_OS_MAC
95-
auto ret = pthread_setname_np(name);
100+
// On macOS, we use a signal-based mechanism to request the target thread to name itself.
101+
pthread_t threadHandle = handle;
96102

97-
if(ret) {
98-
throw std::runtime_error("Unable to set thread name, error code" + std::to_string(ret));
103+
// Define a static flag (of type volatile sig_atomic_t) that the signal handler will set.
104+
static volatile sig_atomic_t nameSetFlag = 0;
105+
106+
static const char* desiredThreadName = name;
107+
108+
// Define the signal handler
109+
const auto threadNameHandler = +[](int /*signum*/) -> void {
110+
if (desiredThreadName != nullptr) {
111+
pthread_setname_np(desiredThreadName);
112+
}
113+
nameSetFlag = 1; // Feed-back Flag
114+
};
115+
116+
// Install the signal handler once for the process.
117+
static std::atomic_flag handlerInstalled = ATOMIC_FLAG_INIT;
118+
if (!handlerInstalled.test_and_set(std::memory_order_acquire)) {
119+
signal(SIGUSR1, threadNameHandler);
120+
}
121+
122+
// Send SIGUSR1 to the target thread so it runs our signal handler.
123+
if (pthread_kill(threadHandle, SIGUSR1) != 0) {
124+
return Result::unsupported;
99125
}
126+
127+
// Wait until the flag is set. Instead of sleeping for a fixed time,
128+
auto start = std::chrono::steady_clock::now();
129+
while (nameSetFlag == 0) {std::this_thread::yield();} // yield to reduce CPU usage
130+
131+
return Result::ok;
132+
100133
#elif defined __linux__ || defined __unix__
101134
auto ret = pthread_setname_np(handle, name);
102135

103136
if(ret) {
104137
throw std::runtime_error("Unable to set thread name, error code" + std::to_string(ret));
105138
}
139+
106140
#endif
107141

108142
return Result::ok;
109143
}
110144

111145
Result set_name(std::jthread& thread, const char* name) {
112-
#ifndef TARGET_OS_MAC
113146
const auto handle = thread.native_handle();
114147
return set_name(handle, name);
115-
#else
116-
#pragma message WARN("Setting thread names is not implemented for this platform. Implement it, please!")
117-
return Result::unsupported;
118-
#endif
119148
}
120149

121150
Result set_name(std::thread& thread, const char* name) {
122-
#ifndef TARGET_OS_MAC
123151
const auto handle = thread.native_handle();
124152
return set_name(handle, name);
125-
#else
126-
#pragma message WARN("Setting thread names is not implemented for this platform. Implement it, please!")
127-
return Result::unsupported;
128-
#endif
129153
}
130154

131155
Result set_name(const char* name) {
132156
#ifdef _WIN32
133157
auto handle = GetCurrentThread();
134-
return set_name(handle, name);
135-
#elif defined __linux__ || defined __unix__ || defined TARGET_OS_MAC
136-
auto handle = pthread_self();
137-
return set_name(handle, name);
138158
#else
139-
#pragma message WARN("Setting thread names is not implemented for this platform. Implement it, please!")
140-
return Result::unsupported;
159+
auto handle = pthread_self();
141160
#endif
161+
return set_name(handle, name);
142162
}
143163

144164
std::expected<std::wstring, Result> get_name(auto& thread) {
@@ -191,14 +211,10 @@ std::expected<std::wstring, Result> get_name(std::thread& thread) {
191211
std::expected<std::wstring, Result> get_name() {
192212
#ifdef _WIN32
193213
auto handle = GetCurrentThread();
194-
return get_name(handle);
195-
#elif defined __linux__ || defined __unix__ || TARGET_OS_MAC
196-
auto handle = pthread_self();
197-
return get_name(handle);
198214
#else
199-
#pragma message WARN("Getting thread names is not implemented for this platform. Implement it, please!")
200-
return std::unexpected(Result::unsupported);
215+
auto handle = pthread_self();
201216
#endif
217+
return get_name(handle);
202218
}
203219

204220
/*
@@ -228,5 +244,4 @@ unsigned int hardware_concurrency() {
228244
return hardware_concurrency(nullptr);
229245
}
230246

231-
232247
} // thread, ember

0 commit comments

Comments
 (0)