166 using native_handle_type =
typename ThreadType::native_handle_type;
167 using id =
typename ThreadType::id;
169 BaseThreadWrapper() =
default;
173 virtual ~BaseThreadWrapper() =
default;
178 if (underlying().joinable())
186 if (underlying().joinable())
188 underlying().detach();
192 [[nodiscard]]
auto joinable()
const noexcept ->
bool
194 return underlying().joinable();
196 [[nodiscard]]
auto get_id()
const noexcept ->
id
198 return underlying().get_id();
200 [[nodiscard]]
auto native_handle()
noexcept -> native_handle_type
202 return underlying().native_handle();
210 auto const handle = native_handle();
211 std::wstring wide_name(name.begin(), name.end());
213 using SetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
214 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
217 auto set_desc =
reinterpret_cast<SetThreadDescriptionFn
>(
218 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadDescription")));
221 if (SUCCEEDED(set_desc(handle, wide_name.c_str())))
229 if (name.length() > 15)
232 auto const handle = native_handle();
233 if (pthread_setname_np(handle, name.c_str()) == 0)
239 [[nodiscard]]
auto get_name()
const -> std::optional<std::string>
242 const auto handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
243 using GetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PWSTR*);
244 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
247 auto get_desc =
reinterpret_cast<GetThreadDescriptionFn
>(
248 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadDescription")));
251 PWSTR thread_name =
nullptr;
252 HRESULT hr = get_desc(handle, &thread_name);
253 if (SUCCEEDED(hr) && thread_name)
255 int size = WideCharToMultiByte(CP_UTF8, 0, thread_name, -1,
nullptr, 0,
nullptr,
nullptr);
258 std::string result(size - 1,
'\0');
259 WideCharToMultiByte(CP_UTF8, 0, thread_name, -1, &result[0], size,
nullptr,
nullptr);
260 LocalFree(thread_name);
263 LocalFree(thread_name);
270 auto const handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
272 if (pthread_getname_np(handle, name,
sizeof(name)) == 0)
274 return std::string(name);
283 const auto handle = native_handle();
288 int prio_val = priority.value();
292 win_priority = THREAD_PRIORITY_IDLE;
294 else if (prio_val <= -5)
296 win_priority = THREAD_PRIORITY_LOWEST;
298 else if (prio_val < 0)
300 win_priority = THREAD_PRIORITY_BELOW_NORMAL;
302 else if (prio_val == 0)
304 win_priority = THREAD_PRIORITY_NORMAL;
306 else if (prio_val <= 5)
308 win_priority = THREAD_PRIORITY_ABOVE_NORMAL;
310 else if (prio_val <= 10)
312 win_priority = THREAD_PRIORITY_HIGHEST;
316 win_priority = THREAD_PRIORITY_TIME_CRITICAL;
319 if (SetThreadPriority(handle, win_priority) != 0)
321 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
323 const auto handle = native_handle();
324 int const policy = SCHED_OTHER;
326 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
328 if (!params_result.has_value())
333 if (pthread_setschedparam(handle, policy, ¶ms_result.value()) == 0)
335 return unexpected(std::error_code(errno, std::generic_category()));
339 [[nodiscard]]
auto set_scheduling_policy(SchedulingPolicy policy,
ThreadPriority priority)
345 return set_priority(priority);
347 const auto handle = native_handle();
348 int const policy_int =
static_cast<int>(policy);
350 auto params_result = SchedulerParams::create_for_policy(policy, priority);
351 if (!params_result.has_value())
356 if (pthread_setschedparam(handle, policy_int, ¶ms_result.value()) == 0)
358 return unexpected(std::error_code(errno, std::generic_category()));
365 const auto handle = native_handle();
367 using SetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE,
const GROUP_AFFINITY*, PGROUP_AFFINITY);
368 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
371 auto set_group_affinity =
reinterpret_cast<SetThreadGroupAffinityFn
>(
372 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadGroupAffinity")));
373 if (set_group_affinity && affinity.has_any())
376 ga.Mask =
static_cast<KAFFINITY
>(affinity.get_mask());
377 ga.Group = affinity.get_group();
378 if (set_group_affinity(handle, &ga,
nullptr) != 0)
380 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
384 DWORD_PTR mask =
static_cast<DWORD_PTR
>(affinity.get_mask());
385 if (SetThreadAffinityMask(handle, mask) != 0)
387 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
389 const auto handle = native_handle();
390 if (pthread_setaffinity_np(handle,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
392 return unexpected(std::error_code(errno, std::generic_category()));
396 [[nodiscard]]
auto get_affinity()
const -> std::optional<ThreadAffinity>
399 const auto handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
400 using GetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE, PGROUP_AFFINITY);
401 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
404 auto get_group_affinity =
reinterpret_cast<GetThreadGroupAffinityFn
>(
405 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadGroupAffinity")));
406 if (get_group_affinity)
409 if (get_group_affinity(handle, &ga) != 0)
412 for (
int i = 0; i < 64; ++i)
414 if ((ga.Mask & (
static_cast<KAFFINITY
>(1) << i)) != 0)
416 affinity.add_cpu(
static_cast<int>(ga.Group) * 64 + i);
419 if (affinity.has_any())
430 auto const handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
432 if (pthread_getaffinity_np(handle,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
441 static auto set_nice_value(
int nice_value) ->
bool
446 DWORD priority_class;
447 if (nice_value <= -15)
449 priority_class = HIGH_PRIORITY_CLASS;
451 else if (nice_value <= -10)
453 priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
455 else if (nice_value < 10)
457 priority_class = NORMAL_PRIORITY_CLASS;
459 else if (nice_value < 19)
461 priority_class = BELOW_NORMAL_PRIORITY_CLASS;
465 priority_class = IDLE_PRIORITY_CLASS;
467 return SetPriorityClass(GetCurrentProcess(), priority_class) != 0;
469 return setpriority(PRIO_PROCESS, 0, nice_value) == 0;
473 static auto get_nice_value() -> std::optional<int>
477 DWORD priority_class = GetPriorityClass(GetCurrentProcess());
478 if (priority_class == 0)
484 switch (priority_class)
486 case HIGH_PRIORITY_CLASS:
488 case ABOVE_NORMAL_PRIORITY_CLASS:
490 case NORMAL_PRIORITY_CLASS:
492 case BELOW_NORMAL_PRIORITY_CLASS:
494 case IDLE_PRIORITY_CLASS:
501 int const nice = getpriority(PRIO_PROCESS, 0);
551class ThreadWrapper :
public BaseThreadWrapper<std::thread, detail::OwningTag>
554 ThreadWrapper() =
default;
557 ThreadWrapper(std::thread&& t)
noexcept
559 this->underlying() = std::move(t);
562 template <
typename F,
typename... Args>
563 explicit ThreadWrapper(F&& f, Args&&... args) : BaseThreadWrapper()
565 this->underlying() = std::thread(std::forward<F>(f), std::forward<Args>(args)...);
568 ThreadWrapper(ThreadWrapper
const&) =
delete;
569 auto operator=(ThreadWrapper
const&) -> ThreadWrapper& =
delete;
571 ThreadWrapper(ThreadWrapper&& other)
noexcept
573 this->underlying() = std::move(other.underlying());
576 auto operator=(ThreadWrapper&& other)
noexcept -> ThreadWrapper&
580 if (this->underlying().joinable())
582 this->underlying().join();
584 this->underlying() = std::move(other.underlying());
589 ~ThreadWrapper()
override
591 if (this->underlying().joinable())
593 this->underlying().join();
598 auto release()
noexcept -> std::thread
600 return std::move(this->underlying());
603 explicit operator std::thread() &&
noexcept
605 return std::move(this->underlying());
609 template <
typename F,
typename... Args>
610 static auto create_with_config(std::string
const& name, SchedulingPolicy policy,
ThreadPriority priority, F&& f,
611 Args&&... args) -> ThreadWrapper
614 ThreadWrapper wrapper(std::forward<F>(f), std::forward<Args>(args)...);
615 if (
auto r = wrapper.set_name(name); !r.has_value())
618 if (
auto r = wrapper.set_scheduling_policy(policy, priority); !r.has_value())
877class ThreadByNameView
881 using native_handle_type =
void*;
883 using native_handle_type = pid_t;
886 explicit ThreadByNameView(
const std::string& name)
892 DIR* dir = opendir(
"/proc/self/task");
895 struct dirent* entry =
nullptr;
896 while ((entry = readdir(dir)) !=
nullptr)
898 if (entry->d_name[0] ==
'.')
900 std::string tid_str(entry->d_name);
901 std::string path = std::string(
"/proc/self/task/") + tid_str +
"/comm";
902 std::ifstream in(path);
906 std::getline(in, current);
907 if (!current.empty() && current.back() ==
'\n')
911 handle_ =
static_cast<pid_t
>(std::stoi(tid_str));
919 [[nodiscard]]
auto found()
const noexcept ->
bool
931 return unexpected(std::make_error_code(std::errc::function_not_supported));
934 return unexpected(std::make_error_code(std::errc::no_such_process));
935 if (name.length() > 15)
936 return unexpected(std::make_error_code(std::errc::invalid_argument));
937 std::string path = std::string(
"/proc/self/task/") + std::to_string(handle_) +
"/comm";
938 std::ofstream out(path);
940 return unexpected(std::error_code(errno, std::generic_category()));
944 return unexpected(std::error_code(errno, std::generic_category()));
949 [[nodiscard]]
auto get_name()
const -> std::optional<std::string>
956 std::string path = std::string(
"/proc/self/task/") + std::to_string(handle_) +
"/comm";
957 std::ifstream in(path);
961 std::getline(in, current);
962 if (!current.empty() && current.back() ==
'\n')
968 [[nodiscard]]
auto native_handle()
const noexcept -> native_handle_type
976 return unexpected(std::make_error_code(std::errc::function_not_supported));
979 return unexpected(std::make_error_code(std::errc::no_such_process));
980 int const policy = SCHED_OTHER;
981 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
982 if (!params_result.has_value())
984 if (sched_setscheduler(handle_, policy, ¶ms_result.value()) == 0)
986 return unexpected(std::error_code(errno, std::generic_category()));
990 [[nodiscard]]
auto set_scheduling_policy(SchedulingPolicy policy,
ThreadPriority priority)
const
994 return unexpected(std::make_error_code(std::errc::function_not_supported));
997 return unexpected(std::make_error_code(std::errc::no_such_process));
998 int policy_int =
static_cast<int>(policy);
999 auto params_result = SchedulerParams::create_for_policy(policy, priority);
1000 if (!params_result.has_value())
1002 if (sched_setscheduler(handle_, policy_int, ¶ms_result.value()) == 0)
1004 return unexpected(std::error_code(errno, std::generic_category()));
1011 return unexpected(std::make_error_code(std::errc::function_not_supported));
1014 return unexpected(std::make_error_code(std::errc::no_such_process));
1015 if (sched_setaffinity(handle_,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
1017 return unexpected(std::error_code(errno, std::generic_category()));
1023 native_handle_type handle_ =
nullptr;
1025 native_handle_type handle_ = 0;