15#include <system_error>
23#include <sys/resource.h>
32using Tid =
unsigned long;
86 DEADLINE = SCHED_DEADLINE
124 constexpr explicit ThreadPriority(
int priority = 0) : priority_(std::clamp(priority, min_priority, max_priority))
128 [[nodiscard]]
constexpr auto value() const noexcept ->
int
132 [[nodiscard]]
constexpr auto is_valid() const noexcept ->
bool
134 return priority_ >= min_priority && priority_ <= max_priority;
152 return priority_ == other.priority_;
156 return priority_ != other.priority_;
160 return priority_ < other.priority_;
164 return priority_ <= other.priority_;
168 return priority_ > other.priority_;
172 return priority_ >= other.priority_;
177 std::ostringstream oss;
178 oss <<
"ThreadPriority(" << priority_ <<
")";
183 static constexpr int min_priority = -20;
184 static constexpr int max_priority = 19;
248 WORD g =
static_cast<WORD
>(cpu / 64);
259 mask_ |= (
static_cast<unsigned long long>(1) << bit);
261 if (cpu >= 0 && cpu < CPU_SETSIZE)
263 CPU_SET(cpu, &cpuset_);
273 WORD g =
static_cast<WORD
>(cpu / 64);
277 mask_ &= ~(
static_cast<unsigned long long>(1) << bit);
280 if (cpu >= 0 && cpu < CPU_SETSIZE)
282 CPU_CLR(cpu, &cpuset_);
287 [[nodiscard]]
auto is_set(
int cpu)
const ->
bool
292 WORD g =
static_cast<WORD
>(cpu / 64);
294 return g == group_ && (mask_ & (
static_cast<unsigned long long>(1) << bit)) != 0;
296 return cpu >= 0 && cpu < CPU_SETSIZE && CPU_ISSET(cpu, &cpuset_);
300 [[nodiscard]]
auto has_cpu(
int cpu)
const ->
bool
314 [[nodiscard]]
auto get_cpus() const -> std::vector<
int>
316 std::vector<int> cpus;
318 for (
int i = 0; i < 64; ++i)
320 if (mask_ & (
static_cast<unsigned long long>(1) << i))
322 cpus.push_back(
static_cast<int>(group_) * 64 + i);
326 for (
int i = 0; i < CPU_SETSIZE; ++i)
328 if (CPU_ISSET(i, &cpuset_))
338 [[nodiscard]]
unsigned long long get_mask()
const
342 [[nodiscard]] WORD get_group()
const
346 [[nodiscard]]
bool has_any()
const
360 std::ostringstream oss;
361 oss <<
"ThreadAffinity({";
362 for (
size_t i = 0; i < cpus.size(); ++i)
375 unsigned long long mask_;
420 struct sched_param_win
428 sched_param_win param{};
430 param.sched_priority = priority.
value();
445 int const policy_int =
static_cast<int>(policy);
446 int const min_prio = sched_get_priority_min(policy_int);
447 int const max_prio = sched_get_priority_max(policy_int);
449 if (min_prio == -1 || max_prio == -1)
451 return unexpected(std::make_error_code(std::errc::invalid_argument));
454 param.sched_priority = std::clamp(priority.
value(), min_prio, max_prio);
460 int const policy_int =
static_cast<int>(policy);
461 int const min_prio = sched_get_priority_min(policy_int);
462 int const max_prio = sched_get_priority_max(policy_int);
464 if (min_prio == -1 || max_prio == -1)
466 return unexpected(std::make_error_code(std::errc::invalid_argument));
469 return max_prio - min_prio;
491#if defined(SCHED_DEADLINE) && !defined(_WIN32)
492 case SchedulingPolicy::DEADLINE:
512inline auto map_priority_to_win32(
int prio_val) ->
int
515 return THREAD_PRIORITY_IDLE;
517 return THREAD_PRIORITY_LOWEST;
519 return THREAD_PRIORITY_BELOW_NORMAL;
521 return THREAD_PRIORITY_NORMAL;
523 return THREAD_PRIORITY_ABOVE_NORMAL;
525 return THREAD_PRIORITY_HIGHEST;
526 return THREAD_PRIORITY_TIME_CRITICAL;
529inline auto apply_priority(HANDLE handle, ThreadPriority priority) -> expected<void, std::error_code>
532 return unexpected(std::make_error_code(std::errc::no_such_process));
533 if (SetThreadPriority(handle, map_priority_to_win32(priority.value())) != 0)
535 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
539 -> expected<void, std::error_code>
544inline auto apply_affinity(HANDLE handle, ThreadAffinity
const& affinity) -> expected<void, std::error_code>
547 return unexpected(std::make_error_code(std::errc::no_such_process));
548 using SetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE,
const GROUP_AFFINITY*, PGROUP_AFFINITY);
549 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
552 auto set_group_affinity =
reinterpret_cast<SetThreadGroupAffinityFn
>(
553 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadGroupAffinity")));
554 if (set_group_affinity && affinity.has_any())
557 ga.Mask =
static_cast<KAFFINITY
>(affinity.get_mask());
558 ga.Group = affinity.get_group();
559 if (set_group_affinity(handle, &ga,
nullptr) != 0)
561 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
564 DWORD_PTR mask =
static_cast<DWORD_PTR
>(affinity.get_mask());
565 if (SetThreadAffinityMask(handle, mask) != 0)
567 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
570inline auto apply_name(HANDLE handle, std::string
const& name) -> expected<void, std::error_code>
573 return unexpected(std::make_error_code(std::errc::no_such_process));
574 using SetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
575 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
577 return unexpected(std::make_error_code(std::errc::function_not_supported));
578 auto set_desc =
reinterpret_cast<SetThreadDescriptionFn
>(
579 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadDescription")));
581 return unexpected(std::make_error_code(std::errc::function_not_supported));
582 std::wstring wide(name.begin(), name.end());
583 if (SUCCEEDED(set_desc(handle, wide.c_str())))
585 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
588inline auto read_name(HANDLE handle) -> std::optional<std::string>
592 using GetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PWSTR*);
593 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
596 auto get_desc =
reinterpret_cast<GetThreadDescriptionFn
>(
597 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadDescription")));
600 PWSTR thread_name =
nullptr;
601 if (SUCCEEDED(get_desc(handle, &thread_name)) && thread_name)
603 int size = WideCharToMultiByte(CP_UTF8, 0, thread_name, -1,
nullptr, 0,
nullptr,
nullptr);
606 std::string result(size - 1,
'\0');
607 WideCharToMultiByte(CP_UTF8, 0, thread_name, -1, &result[0], size,
nullptr,
nullptr);
608 LocalFree(thread_name);
611 LocalFree(thread_name);
616inline auto read_affinity(HANDLE handle) -> std::optional<ThreadAffinity>
620 using GetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE, PGROUP_AFFINITY);
621 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
624 auto get_group_affinity =
reinterpret_cast<GetThreadGroupAffinityFn
>(
625 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadGroupAffinity")));
626 if (!get_group_affinity)
629 if (get_group_affinity(handle, &ga) != 0)
631 ThreadAffinity affinity;
632 for (
int i = 0; i < 64; ++i)
634 if ((ga.Mask & (
static_cast<KAFFINITY
>(1) << i)) != 0)
635 affinity.add_cpu(
static_cast<int>(ga.Group) * 64 + i);
637 if (affinity.has_any())
643inline auto read_priority(HANDLE handle) -> std::optional<int>
647 int const priority = GetThreadPriority(handle);
648 if (priority == THREAD_PRIORITY_ERROR_RETURN)
660inline auto apply_priority(
Tid tid, ThreadPriority priority) -> expected<void, std::error_code>
662 HANDLE handle = OpenThread(THREAD_SET_INFORMATION, FALSE, tid);
664 return unexpected(std::make_error_code(std::errc::no_such_process));
672 -> expected<void, std::error_code>
674 HANDLE handle = OpenThread(THREAD_SET_INFORMATION, FALSE, tid);
676 return unexpected(std::make_error_code(std::errc::no_such_process));
683inline auto apply_affinity(
Tid tid, ThreadAffinity
const& affinity) -> expected<void, std::error_code>
685 HANDLE handle = OpenThread(THREAD_SET_INFORMATION, FALSE, tid);
687 return unexpected(std::make_error_code(std::errc::no_such_process));
694inline auto apply_name(
Tid tid, std::string
const& name) -> expected<void, std::error_code>
696 HANDLE handle = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, tid);
698 return unexpected(std::make_error_code(std::errc::no_such_process));
705inline auto read_name(
Tid tid) -> std::optional<std::string>
707 HANDLE handle = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, tid);
718 HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
729 HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
740 HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
753template <
typename SetSchedFn>
757 int const policy_int =
static_cast<int>(policy);
759 if (!params_result.has_value())
761 if (set_sched(policy_int, ¶ms_result.value()) == 0)
763 return unexpected(std::error_code(errno, std::generic_category()));
772 [handle](
int p, sched_param* sp) {
return pthread_setschedparam(handle, p, sp); });
782 if (pthread_setaffinity_np(handle,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
784 return unexpected(std::error_code(errno, std::generic_category()));
789 if (name.length() > 15)
790 return unexpected(std::make_error_code(std::errc::invalid_argument));
791 if (pthread_setname_np(handle, name.c_str()) == 0)
793 return unexpected(std::error_code(errno, std::generic_category()));
796inline auto read_name(pthread_t handle) -> std::optional<std::string>
799 if (pthread_getname_np(handle, name,
sizeof(name)) == 0)
800 return std::string(name);
808 if (pthread_getaffinity_np(handle,
sizeof(cpu_set_t), &cpuset) == 0)
810 std::vector<int> cpus;
811 for (
int i = 0; i < CPU_SETSIZE; ++i)
813 if (CPU_ISSET(i, &cpuset))
825 if (pthread_getschedparam(handle, &policy, ¶m) == 0)
826 return param.sched_priority;
834 if (pthread_getschedparam(handle, &policy, ¶m) == 0)
845 [tid](
int p, sched_param* sp) {
return sched_setscheduler(tid, p, sp); });
855 if (sched_setaffinity(tid,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
857 return unexpected(std::error_code(errno, std::generic_category()));
862 if (name.length() > 15)
863 return unexpected(std::make_error_code(std::errc::invalid_argument));
865 std::string
const path = std::string(
"/proc/self/task/") + std::to_string(tid) +
"/comm";
866 std::ofstream out(path);
868 return unexpected(std::error_code(errno, std::generic_category()));
873 return unexpected(std::error_code(errno, std::generic_category()));
877inline auto read_name(pid_t tid) -> std::optional<std::string>
879 std::string
const path = std::string(
"/proc/self/task/") + std::to_string(tid) +
"/comm";
880 std::ifstream in(path);
885 std::getline(in, current);
886 if (!current.empty() && current.back() ==
'\n')
895 if (sched_getaffinity(tid,
sizeof(cpu_set_t), &cpuset) != 0)
898 std::vector<int> cpus;
899 for (
int i = 0; i < CPU_SETSIZE; ++i)
901 if (CPU_ISSET(i, &cpuset))
910 if (sched_getparam(tid, ¶m) == 0)
911 return param.sched_priority;
917 int const policy = sched_getscheduler(tid);
Static utility class for constructing OS-native scheduling parameters.
static auto get_priority_range(SchedulingPolicy policy) -> expected< int, std::error_code >
static auto create_for_policy(SchedulingPolicy policy, ThreadPriority priority) -> expected< sched_param, std::error_code >
Manages a set of CPU indices to which a thread may be bound.
auto to_string() const -> std::string
auto get_cpus() const -> std::vector< int >
auto has_cpu(int cpu) const -> bool
auto native_handle() const -> cpu_set_t const &
ThreadAffinity(std::vector< int > const &cpus)
auto is_set(int cpu) const -> bool
Value-semantic wrapper for a thread scheduling priority.
static constexpr auto normal() noexcept -> ThreadPriority
constexpr ThreadPriority(int priority=0)
constexpr auto value() const noexcept -> int
constexpr auto is_valid() const noexcept -> bool
constexpr auto operator==(ThreadPriority const &other) const noexcept -> bool
auto to_string() const -> std::string
constexpr auto operator!=(ThreadPriority const &other) const noexcept -> bool
static constexpr auto highest() noexcept -> ThreadPriority
constexpr auto operator>=(ThreadPriority const &other) const noexcept -> bool
constexpr auto operator>(ThreadPriority const &other) const noexcept -> bool
static constexpr auto lowest() noexcept -> ThreadPriority
constexpr auto operator<=(ThreadPriority const &other) const noexcept -> bool
constexpr auto operator<(ThreadPriority const &other) const noexcept -> bool
A result type that holds either a value of type T or an error of type E.
Exception thrown by expected::value() when the object is in the error state.
Polyfill for std::expected (C++23) for pre-C++23 compilers.
auto read_priority(pthread_t handle) -> std::optional< int >
auto read_scheduling_policy(pthread_t handle) -> std::optional< SchedulingPolicy >
auto apply_affinity(pthread_t handle, ThreadAffinity const &affinity) -> expected< void, std::error_code >
auto apply_priority(pthread_t handle, ThreadPriority priority) -> expected< void, std::error_code >
auto read_name(pthread_t handle) -> std::optional< std::string >
auto apply_scheduling_policy(pthread_t handle, SchedulingPolicy policy, ThreadPriority priority) -> expected< void, std::error_code >
auto apply_name(pthread_t handle, std::string const &name) -> expected< void, std::error_code >
auto read_affinity(pthread_t handle) -> std::optional< ThreadAffinity >
auto apply_sched_params(SchedulingPolicy policy, ThreadPriority priority, SetSchedFn &&set_sched) -> expected< void, std::error_code >
SchedulingPolicy
Enumeration of available thread scheduling policies.
@ OTHER
Standard round-robin time-sharing.
@ BATCH
For batch style execution.
@ IDLE
For very low priority background tasks.
@ FIFO
First in, first out.
auto to_string(SchedulingPolicy policy) -> std::string
String conversion utilities.