85 using native_handle_type =
typename ThreadType::native_handle_type;
86 using id =
typename ThreadType::id;
88 BaseThreadWrapper() =
default;
92 virtual ~BaseThreadWrapper() =
default;
97 if (underlying().joinable())
105 if (underlying().joinable())
107 underlying().detach();
111 [[nodiscard]]
auto joinable()
const noexcept ->
bool
113 return underlying().joinable();
115 [[nodiscard]]
auto get_id()
const noexcept ->
id
117 return underlying().get_id();
119 [[nodiscard]]
auto native_handle()
noexcept -> native_handle_type
121 return underlying().native_handle();
129 auto const handle = native_handle();
130 std::wstring wide_name(name.begin(), name.end());
132 using SetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
133 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
136 auto set_desc =
reinterpret_cast<SetThreadDescriptionFn
>(
137 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadDescription")));
140 if (SUCCEEDED(set_desc(handle, wide_name.c_str())))
148 if (name.length() > 15)
151 auto const handle = native_handle();
152 if (pthread_setname_np(handle, name.c_str()) == 0)
158 [[nodiscard]]
auto get_name()
const -> std::optional<std::string>
161 const auto handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
162 using GetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PWSTR*);
163 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
166 auto get_desc =
reinterpret_cast<GetThreadDescriptionFn
>(
167 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadDescription")));
170 PWSTR thread_name =
nullptr;
171 HRESULT hr = get_desc(handle, &thread_name);
172 if (SUCCEEDED(hr) && thread_name)
174 int size = WideCharToMultiByte(CP_UTF8, 0, thread_name, -1,
nullptr, 0,
nullptr,
nullptr);
177 std::string result(size - 1,
'\0');
178 WideCharToMultiByte(CP_UTF8, 0, thread_name, -1, &result[0], size,
nullptr,
nullptr);
179 LocalFree(thread_name);
182 LocalFree(thread_name);
189 auto const handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
191 if (pthread_getname_np(handle, name,
sizeof(name)) == 0)
193 return std::string(name);
202 const auto handle = native_handle();
207 int prio_val = priority.value();
211 win_priority = THREAD_PRIORITY_IDLE;
213 else if (prio_val <= -5)
215 win_priority = THREAD_PRIORITY_LOWEST;
217 else if (prio_val < 0)
219 win_priority = THREAD_PRIORITY_BELOW_NORMAL;
221 else if (prio_val == 0)
223 win_priority = THREAD_PRIORITY_NORMAL;
225 else if (prio_val <= 5)
227 win_priority = THREAD_PRIORITY_ABOVE_NORMAL;
229 else if (prio_val <= 10)
231 win_priority = THREAD_PRIORITY_HIGHEST;
235 win_priority = THREAD_PRIORITY_TIME_CRITICAL;
238 if (SetThreadPriority(handle, win_priority) != 0)
240 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
242 const auto handle = native_handle();
243 int const policy = SCHED_OTHER;
245 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
247 if (!params_result.has_value())
252 if (pthread_setschedparam(handle, policy, ¶ms_result.value()) == 0)
254 return unexpected(std::error_code(errno, std::generic_category()));
258 [[nodiscard]]
auto set_scheduling_policy(SchedulingPolicy policy,
ThreadPriority priority)
264 return set_priority(priority);
266 const auto handle = native_handle();
267 int const policy_int =
static_cast<int>(policy);
269 auto params_result = SchedulerParams::create_for_policy(policy, priority);
270 if (!params_result.has_value())
275 if (pthread_setschedparam(handle, policy_int, ¶ms_result.value()) == 0)
277 return unexpected(std::error_code(errno, std::generic_category()));
284 const auto handle = native_handle();
286 using SetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE,
const GROUP_AFFINITY*, PGROUP_AFFINITY);
287 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
290 auto set_group_affinity =
reinterpret_cast<SetThreadGroupAffinityFn
>(
291 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadGroupAffinity")));
292 if (set_group_affinity && affinity.has_any())
295 ga.Mask =
static_cast<KAFFINITY
>(affinity.get_mask());
296 ga.Group = affinity.get_group();
297 if (set_group_affinity(handle, &ga,
nullptr) != 0)
299 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
303 DWORD_PTR mask =
static_cast<DWORD_PTR
>(affinity.get_mask());
304 if (SetThreadAffinityMask(handle, mask) != 0)
306 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
308 const auto handle = native_handle();
309 if (pthread_setaffinity_np(handle,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
311 return unexpected(std::error_code(errno, std::generic_category()));
315 [[nodiscard]]
auto get_affinity()
const -> std::optional<ThreadAffinity>
318 const auto handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
319 using GetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE, PGROUP_AFFINITY);
320 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
323 auto get_group_affinity =
reinterpret_cast<GetThreadGroupAffinityFn
>(
324 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadGroupAffinity")));
325 if (get_group_affinity)
328 if (get_group_affinity(handle, &ga) != 0)
331 for (
int i = 0; i < 64; ++i)
333 if ((ga.Mask & (
static_cast<KAFFINITY
>(1) << i)) != 0)
335 affinity.add_cpu(
static_cast<int>(ga.Group) * 64 + i);
338 return affinity.has_any() ? affinity : std::nullopt;
345 auto const handle =
const_cast<BaseThreadWrapper*
>(
this)->native_handle();
347 if (pthread_getaffinity_np(handle,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
356 static auto set_nice_value(
int nice_value) ->
bool
361 DWORD priority_class;
362 if (nice_value <= -15)
364 priority_class = HIGH_PRIORITY_CLASS;
366 else if (nice_value <= -10)
368 priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
370 else if (nice_value < 10)
372 priority_class = NORMAL_PRIORITY_CLASS;
374 else if (nice_value < 19)
376 priority_class = BELOW_NORMAL_PRIORITY_CLASS;
380 priority_class = IDLE_PRIORITY_CLASS;
382 return SetPriorityClass(GetCurrentProcess(), priority_class) != 0;
384 return setpriority(PRIO_PROCESS, 0, nice_value) == 0;
388 static auto get_nice_value() -> std::optional<int>
392 DWORD priority_class = GetPriorityClass(GetCurrentProcess());
393 if (priority_class == 0)
399 switch (priority_class)
401 case HIGH_PRIORITY_CLASS:
403 case ABOVE_NORMAL_PRIORITY_CLASS:
405 case NORMAL_PRIORITY_CLASS:
407 case BELOW_NORMAL_PRIORITY_CLASS:
409 case IDLE_PRIORITY_CLASS:
416 int const nice = getpriority(PRIO_PROCESS, 0);
433class ThreadWrapper :
public BaseThreadWrapper<std::thread, detail::OwningTag>
436 ThreadWrapper() =
default;
439 ThreadWrapper(std::thread&& t)
noexcept
441 this->underlying() = std::move(t);
444 template <
typename F,
typename... Args>
445 explicit ThreadWrapper(F&& f, Args&&... args) : BaseThreadWrapper()
447 this->underlying() = std::thread(std::forward<F>(f), std::forward<Args>(args)...);
450 ThreadWrapper(ThreadWrapper
const&) =
delete;
451 auto operator=(ThreadWrapper
const&) -> ThreadWrapper& =
delete;
453 ThreadWrapper(ThreadWrapper&& other)
noexcept
455 this->underlying() = std::move(other.underlying());
458 auto operator=(ThreadWrapper&& other)
noexcept -> ThreadWrapper&
462 if (this->underlying().joinable())
464 this->underlying().join();
466 this->underlying() = std::move(other.underlying());
471 ~ThreadWrapper()
override
473 if (this->underlying().joinable())
475 this->underlying().join();
480 auto release()
noexcept -> std::thread
482 return std::move(this->underlying());
485 explicit operator std::thread() &&
noexcept
487 return std::move(this->underlying());
491 template <
typename F,
typename... Args>
492 static auto create_with_config(std::string
const& name, SchedulingPolicy policy,
ThreadPriority priority, F&& f,
493 Args&&... args) -> ThreadWrapper
496 ThreadWrapper wrapper(std::forward<F>(f), std::forward<Args>(args)...);
497 if (
auto r = wrapper.set_name(name); !r.has_value())
500 if (
auto r = wrapper.set_scheduling_policy(policy, priority); !r.has_value())
651class ThreadByNameView
655 using native_handle_type =
void*;
657 using native_handle_type = pid_t;
660 explicit ThreadByNameView(
const std::string& name)
666 DIR* dir = opendir(
"/proc/self/task");
669 struct dirent* entry =
nullptr;
670 while ((entry = readdir(dir)) !=
nullptr)
672 if (entry->d_name[0] ==
'.')
674 std::string tid_str(entry->d_name);
675 std::string path = std::string(
"/proc/self/task/") + tid_str +
"/comm";
676 std::ifstream in(path);
680 std::getline(in, current);
681 if (!current.empty() && current.back() ==
'\n')
685 handle_ =
static_cast<pid_t
>(std::stoi(tid_str));
693 [[nodiscard]]
auto found()
const noexcept ->
bool
705 return unexpected(std::make_error_code(std::errc::function_not_supported));
708 return unexpected(std::make_error_code(std::errc::no_such_process));
709 if (name.length() > 15)
710 return unexpected(std::make_error_code(std::errc::invalid_argument));
711 std::string path = std::string(
"/proc/self/task/") + std::to_string(handle_) +
"/comm";
712 std::ofstream out(path);
714 return unexpected(std::error_code(errno, std::generic_category()));
718 return unexpected(std::error_code(errno, std::generic_category()));
723 [[nodiscard]]
auto get_name()
const -> std::optional<std::string>
730 std::string path = std::string(
"/proc/self/task/") + std::to_string(handle_) +
"/comm";
731 std::ifstream in(path);
735 std::getline(in, current);
736 if (!current.empty() && current.back() ==
'\n')
742 [[nodiscard]]
auto native_handle()
const noexcept -> native_handle_type
750 return unexpected(std::make_error_code(std::errc::function_not_supported));
753 return unexpected(std::make_error_code(std::errc::no_such_process));
754 int const policy = SCHED_OTHER;
755 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
756 if (!params_result.has_value())
758 if (sched_setscheduler(handle_, policy, ¶ms_result.value()) == 0)
760 return unexpected(std::error_code(errno, std::generic_category()));
764 [[nodiscard]]
auto set_scheduling_policy(SchedulingPolicy policy,
ThreadPriority priority)
const
768 return unexpected(std::make_error_code(std::errc::function_not_supported));
771 return unexpected(std::make_error_code(std::errc::no_such_process));
772 int policy_int =
static_cast<int>(policy);
773 auto params_result = SchedulerParams::create_for_policy(policy, priority);
774 if (!params_result.has_value())
776 if (sched_setscheduler(handle_, policy_int, ¶ms_result.value()) == 0)
778 return unexpected(std::error_code(errno, std::generic_category()));
785 return unexpected(std::make_error_code(std::errc::function_not_supported));
788 return unexpected(std::make_error_code(std::errc::no_such_process));
789 if (sched_setaffinity(handle_,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
791 return unexpected(std::error_code(errno, std::generic_category()));
797 native_handle_type handle_ =
nullptr;
799 native_handle_type handle_ = 0;