1+
12#include " threading.h"
23
4+ #include < cstring>
5+ #include < iostream>
6+ #include < optional>
37#include < string>
8+ #include < system_error>
9+ #include < thread>
410
511#include " spdlog/spdlog.h"
612
915#endif
1016
1117#if defined(__linux__)
18+ #include < sched.h>
1219#include < sys/syscall.h>
1320#include < unistd.h>
21+ #elif defined(__APPLE__)
22+ #include < mach/mach.h>
23+ #include < mach/thread_policy.h>
1424#elif defined(_WIN32)
1525#include < windows.h>
1626
@@ -46,6 +56,51 @@ void Threading::set_thread_name(const std::string& name) {
4656#endif
4757}
4858
59+ // static function
60+ std::string Threading::get_thread_name () {
61+ #if defined(__linux__)
62+ char name[16 ] = {0 }; // Linux max length for pthread_getname_np
63+ int rc = pthread_getname_np (pthread_self (), name, sizeof (name));
64+ if (rc != 0 ) {
65+ spdlog::error (" failed to get thread name. error [{}]" ,
66+ std::system_category ().message (rc));
67+ return {};
68+ }
69+ return std::string (name);
70+
71+ #elif defined(__APPLE__)
72+ char name[64 ] = {0 }; // macOS max 64 including null terminator
73+ int rc = pthread_getname_np (pthread_self (), name, sizeof (name));
74+ if (rc != 0 ) {
75+ spdlog::error (" failed to get thread name on macOS. error [{}]" , rc);
76+ return {};
77+ }
78+ return std::string (name);
79+
80+ #elif defined(_WIN32)
81+ // Windows 10 1607+ has GetThreadDescription
82+ PWSTR wname = nullptr ;
83+ HRESULT hr = GetThreadDescription (GetCurrentThread (), &wname);
84+ if (FAILED (hr) || wname == nullptr ) {
85+ return {};
86+ }
87+ // Convert UTF-16 to UTF-8
88+ int size_needed =
89+ WideCharToMultiByte (CP_UTF8, 0 , wname, -1 , nullptr , 0 , nullptr , nullptr );
90+ std::string name (size_needed, 0 );
91+ WideCharToMultiByte (CP_UTF8, 0 , wname, -1 , &name[0 ], size_needed, nullptr , nullptr );
92+ LocalFree (wname); // free memory allocated by GetThreadDescription
93+ // remove trailing null if present
94+ if (!name.empty () && name.back () == ' \0 ' ) {
95+ name.pop_back ();
96+ }
97+ return name;
98+
99+ #else
100+ return {}; // Unsupported platform
101+ #endif
102+ }
103+
49104// static function
50105uint64_t Threading::get_os_thread_id () {
51106#if defined(__linux__)
@@ -66,4 +121,99 @@ uint64_t Threading::get_os_thread_id() {
66121#endif
67122}
68123
124+ // static function
125+ unsigned int Threading::get_cpu_count () {
126+ #if defined(__linux__) || defined(__APPLE__)
127+ long n = ::sysconf (_SC_NPROCESSORS_ONLN);
128+ if (n > 0 ) {
129+ return static_cast <unsigned int >(n);
130+ }
131+ // Fallback if sysconf fails
132+ unsigned int std_n = std::thread::hardware_concurrency ();
133+ return std_n > 0 ? std_n : 1 ;
134+
135+ #elif defined(_WIN32)
136+ SYSTEM_INFO sysinfo;
137+ GetSystemInfo (&sysinfo);
138+ return sysinfo.dwNumberOfProcessors ;
139+
140+ #else
141+ unsigned int std_n = std::thread::hardware_concurrency ();
142+ return std_n > 0 ? std_n : 1 ;
143+ #endif
144+ }
145+
146+ // static function
147+ bool Threading::set_native_thread_affinity (std::thread::native_handle_type handle,
148+ unsigned int cpu_id) {
149+ #if defined(__linux__)
150+ cpu_set_t cpuset;
151+ CPU_ZERO (&cpuset);
152+ CPU_SET (cpu_id, &cpuset);
153+ unsigned int num_cpus = get_cpu_count ();
154+ if (cpu_id >= num_cpus) {
155+ spdlog::error (" cpu_id exceeds available CPUs, cpu_id [{}] available [{}]" , cpu_id,
156+ num_cpus);
157+ return false ;
158+ }
159+ int rc = pthread_setaffinity_np (handle, sizeof (cpu_set_t ), &cpuset);
160+ if (rc != 0 ) {
161+ spdlog::error (" linux: failed to set thread affinity. error [{}]" ,
162+ std::system_category ().message (rc));
163+ return false ;
164+ }
165+ spdlog::info (" linux: successfully set thread affinity. thread_name [{}] cpu [{}]" ,
166+ get_thread_name (), cpu_id);
167+ return true ;
168+
169+ #elif defined(__APPLE__)
170+ thread_port_t mach_thread = pthread_mach_thread_np (handle);
171+ thread_affinity_policy_data_t policy{.affinity_tag = static_cast <int >(cpu_id + 1 )};
172+ kern_return_t kr = thread_policy_set (mach_thread, THREAD_AFFINITY_POLICY,
173+ reinterpret_cast <thread_policy_t >(&policy),
174+ THREAD_AFFINITY_POLICY_COUNT);
175+ if (kr != KERN_SUCCESS) {
176+ spdlog::error (" macOS: failed to set thread affinity. kern_return_t [{}]" , kr);
177+ return false ;
178+ }
179+ spdlog::info (" macOS: successfully set thread affinity. thread_name [{}] cpu [{}]" ,
180+ get_thread_name (), cpu_id);
181+ return true ;
182+
183+ #elif defined(_WIN32)
184+ DWORD_PTR mask = static_cast <DWORD_PTR>(1 ) << cpu_id;
185+ DWORD_PTR result = SetThreadAffinityMask (handle, mask);
186+ if (result == 0 ) {
187+ spdlog::error (" windows: failed to set thread affinity. error [{}]" , GetLastError ());
188+ return false ;
189+ }
190+ spdlog::info (" windows: successfully set thread affinity. thread_name [{}] cpu [{}]" ,
191+ get_thread_name (), cpu_id);
192+ return true ;
193+
194+ #else
195+ (void )handle;
196+ (void )cpu_id;
197+ spdlog::error (" thread affinity not supported on this platform" );
198+ return false ;
199+ #endif
200+ }
201+
202+ // static function
203+ bool Threading::set_thread_affinity (std::thread& t, unsigned int cpu_id) {
204+ return set_native_thread_affinity (t.native_handle (), cpu_id);
205+ }
206+
207+ // static function
208+ bool Threading::set_current_thread_affinity (unsigned int cpu_id) {
209+ #if defined(__linux__) || defined(__APPLE__)
210+ return set_native_thread_affinity (pthread_self (), cpu_id);
211+ #elif defined(_WIN32)
212+ return set_native_thread_affinity (GetCurrentThread (), cpu_id);
213+ #else
214+ spdlog::error (" thread affinity not supported on this platform" );
215+ return false ;
216+ #endif
217+ }
218+
69219} // namespace utils
0 commit comments