14#include <system_error>
22#include <sys/resource.h>
78 DEADLINE = SCHED_DEADLINE
116 constexpr explicit ThreadPriority(
int priority = 0) : priority_(std::clamp(priority, min_priority, max_priority))
120 [[nodiscard]]
constexpr auto value() const noexcept ->
int
124 [[nodiscard]]
constexpr auto is_valid() const noexcept ->
bool
126 return priority_ >= min_priority && priority_ <= max_priority;
144 return priority_ == other.priority_;
148 return priority_ != other.priority_;
152 return priority_ < other.priority_;
156 return priority_ <= other.priority_;
160 return priority_ > other.priority_;
164 return priority_ >= other.priority_;
169 std::ostringstream oss;
170 oss <<
"ThreadPriority(" << priority_ <<
")";
175 static constexpr int min_priority = -20;
176 static constexpr int max_priority = 19;
240 WORD g =
static_cast<WORD
>(cpu / 64);
251 mask_ |= (
static_cast<unsigned long long>(1) << bit);
253 if (cpu >= 0 && cpu < CPU_SETSIZE)
255 CPU_SET(cpu, &cpuset_);
265 WORD g =
static_cast<WORD
>(cpu / 64);
269 mask_ &= ~(
static_cast<unsigned long long>(1) << bit);
272 if (cpu >= 0 && cpu < CPU_SETSIZE)
274 CPU_CLR(cpu, &cpuset_);
279 [[nodiscard]]
auto is_set(
int cpu)
const ->
bool
284 WORD g =
static_cast<WORD
>(cpu / 64);
286 return g == group_ && (mask_ & (
static_cast<unsigned long long>(1) << bit)) != 0;
288 return cpu >= 0 && cpu < CPU_SETSIZE && CPU_ISSET(cpu, &cpuset_);
292 [[nodiscard]]
auto has_cpu(
int cpu)
const ->
bool
306 [[nodiscard]]
auto get_cpus() const -> std::vector<
int>
308 std::vector<int> cpus;
310 for (
int i = 0; i < 64; ++i)
312 if (mask_ & (
static_cast<unsigned long long>(1) << i))
314 cpus.push_back(
static_cast<int>(group_) * 64 + i);
318 for (
int i = 0; i < CPU_SETSIZE; ++i)
320 if (CPU_ISSET(i, &cpuset_))
330 [[nodiscard]]
unsigned long long get_mask()
const
334 [[nodiscard]] WORD get_group()
const
338 [[nodiscard]]
bool has_any()
const
352 std::ostringstream oss;
353 oss <<
"ThreadAffinity({";
354 for (
size_t i = 0; i < cpus.size(); ++i)
367 unsigned long long mask_;
412 struct sched_param_win
420 sched_param_win param{};
422 param.sched_priority = priority.
value();
437 int const policy_int =
static_cast<int>(policy);
438 int const min_prio = sched_get_priority_min(policy_int);
439 int const max_prio = sched_get_priority_max(policy_int);
441 if (min_prio == -1 || max_prio == -1)
443 return unexpected(std::make_error_code(std::errc::invalid_argument));
446 param.sched_priority = std::clamp(priority.
value(), min_prio, max_prio);
452 int const policy_int =
static_cast<int>(policy);
453 int const min_prio = sched_get_priority_min(policy_int);
454 int const max_prio = sched_get_priority_max(policy_int);
456 if (min_prio == -1 || max_prio == -1)
458 return unexpected(std::make_error_code(std::errc::invalid_argument));
461 return max_prio - min_prio;
483#if defined(SCHED_DEADLINE) && !defined(_WIN32)
484 case SchedulingPolicy::DEADLINE:
504inline auto map_priority_to_win32(
int prio_val) ->
int
507 return THREAD_PRIORITY_IDLE;
509 return THREAD_PRIORITY_LOWEST;
511 return THREAD_PRIORITY_BELOW_NORMAL;
513 return THREAD_PRIORITY_NORMAL;
515 return THREAD_PRIORITY_ABOVE_NORMAL;
517 return THREAD_PRIORITY_HIGHEST;
518 return THREAD_PRIORITY_TIME_CRITICAL;
521inline auto apply_priority(HANDLE handle, ThreadPriority priority) -> expected<void, std::error_code>
524 return unexpected(std::make_error_code(std::errc::no_such_process));
525 if (SetThreadPriority(handle, map_priority_to_win32(priority.value())) != 0)
527 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
531 -> expected<void, std::error_code>
536inline auto apply_affinity(HANDLE handle, ThreadAffinity
const& affinity) -> expected<void, std::error_code>
539 return unexpected(std::make_error_code(std::errc::no_such_process));
540 using SetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE,
const GROUP_AFFINITY*, PGROUP_AFFINITY);
541 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
544 auto set_group_affinity =
reinterpret_cast<SetThreadGroupAffinityFn
>(
545 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadGroupAffinity")));
546 if (set_group_affinity && affinity.has_any())
549 ga.Mask =
static_cast<KAFFINITY
>(affinity.get_mask());
550 ga.Group = affinity.get_group();
551 if (set_group_affinity(handle, &ga,
nullptr) != 0)
553 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
556 DWORD_PTR mask =
static_cast<DWORD_PTR
>(affinity.get_mask());
557 if (SetThreadAffinityMask(handle, mask) != 0)
559 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
562inline auto apply_name(HANDLE handle, std::string
const& name) -> expected<void, std::error_code>
565 return unexpected(std::make_error_code(std::errc::no_such_process));
566 using SetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
567 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
569 return unexpected(std::make_error_code(std::errc::function_not_supported));
570 auto set_desc =
reinterpret_cast<SetThreadDescriptionFn
>(
571 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadDescription")));
573 return unexpected(std::make_error_code(std::errc::function_not_supported));
574 std::wstring wide(name.begin(), name.end());
575 if (SUCCEEDED(set_desc(handle, wide.c_str())))
577 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
580inline auto read_name(HANDLE handle) -> std::optional<std::string>
584 using GetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PWSTR*);
585 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
588 auto get_desc =
reinterpret_cast<GetThreadDescriptionFn
>(
589 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadDescription")));
592 PWSTR thread_name =
nullptr;
593 if (SUCCEEDED(get_desc(handle, &thread_name)) && thread_name)
595 int size = WideCharToMultiByte(CP_UTF8, 0, thread_name, -1,
nullptr, 0,
nullptr,
nullptr);
598 std::string result(size - 1,
'\0');
599 WideCharToMultiByte(CP_UTF8, 0, thread_name, -1, &result[0], size,
nullptr,
nullptr);
600 LocalFree(thread_name);
603 LocalFree(thread_name);
608inline auto read_affinity(HANDLE handle) -> std::optional<ThreadAffinity>
612 using GetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE, PGROUP_AFFINITY);
613 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
616 auto get_group_affinity =
reinterpret_cast<GetThreadGroupAffinityFn
>(
617 reinterpret_cast<void*
>(GetProcAddress(hMod,
"GetThreadGroupAffinity")));
618 if (!get_group_affinity)
621 if (get_group_affinity(handle, &ga) != 0)
623 ThreadAffinity affinity;
624 for (
int i = 0; i < 64; ++i)
626 if ((ga.Mask & (
static_cast<KAFFINITY
>(1) << i)) != 0)
627 affinity.add_cpu(
static_cast<int>(ga.Group) * 64 + i);
629 if (affinity.has_any())
639template <
typename SetSchedFn>
643 int const policy_int =
static_cast<int>(policy);
645 if (!params_result.has_value())
647 if (set_sched(policy_int, ¶ms_result.value()) == 0)
649 return unexpected(std::error_code(errno, std::generic_category()));
658 [handle](
int p, sched_param* sp) {
return pthread_setschedparam(handle, p, sp); });
668 if (pthread_setaffinity_np(handle,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
670 return unexpected(std::error_code(errno, std::generic_category()));
675 if (name.length() > 15)
676 return unexpected(std::make_error_code(std::errc::invalid_argument));
677 if (pthread_setname_np(handle, name.c_str()) == 0)
679 return unexpected(std::error_code(errno, std::generic_category()));
682inline auto read_name(pthread_t handle) -> std::optional<std::string>
685 if (pthread_getname_np(handle, name,
sizeof(name)) == 0)
686 return std::string(name);
694 if (pthread_getaffinity_np(handle,
sizeof(cpu_set_t), &cpuset) == 0)
696 std::vector<int> cpus;
697 for (
int i = 0; i < CPU_SETSIZE; ++i)
699 if (CPU_ISSET(i, &cpuset))
713 [tid](
int p, sched_param* sp) {
return sched_setscheduler(tid, p, sp); });
723 if (sched_setaffinity(tid,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
725 return unexpected(std::error_code(errno, std::generic_category()));
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 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.