ThreadSchedule 2.2.0
Modern C++ thread management library
Loading...
Searching...
No Matches
scheduler_policy.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include "expected.hpp"
9#include <algorithm>
10#include <cstdint>
11#include <fstream>
12#include <optional>
13#include <sstream>
14#include <string>
15#include <system_error>
16#include <vector>
17
18#ifdef _WIN32
19#include <windows.h>
20#else
21#include <pthread.h>
22#include <sched.h>
23#include <sys/resource.h>
24#include <sys/types.h>
25#endif
26
27namespace threadschedule
28{
29// expected/result are provided by expected.hpp
30
31#ifdef _WIN32
32using Tid = unsigned long; // DWORD thread id
33#else
34using Tid = pid_t; // Linux TID via gettid()
35#endif
36
69enum class SchedulingPolicy : std::uint_fast8_t
70{
71#ifdef _WIN32
72 // Windows doesn't have the same scheduling policies as Linux
73 // We'll use generic values
74 OTHER = 0,
75 FIFO = 1,
76 RR = 2,
77 BATCH = 3,
78 IDLE = 4
79#else
80 OTHER = SCHED_OTHER,
81 FIFO = SCHED_FIFO,
82 RR = SCHED_RR,
83 BATCH = SCHED_BATCH,
84 IDLE = SCHED_IDLE,
85#ifdef SCHED_DEADLINE
86 DEADLINE = SCHED_DEADLINE
87#endif
88#endif
89};
90
122{
123 public:
124 constexpr explicit ThreadPriority(int priority = 0) : priority_(std::clamp(priority, min_priority, max_priority))
125 {
126 }
127
128 [[nodiscard]] constexpr auto value() const noexcept -> int
129 {
130 return priority_;
131 }
132 [[nodiscard]] constexpr auto is_valid() const noexcept -> bool
133 {
134 return priority_ >= min_priority && priority_ <= max_priority;
135 }
136
137 [[nodiscard]] static constexpr auto lowest() noexcept -> ThreadPriority
138 {
139 return ThreadPriority(min_priority);
140 }
141 [[nodiscard]] static constexpr auto normal() noexcept -> ThreadPriority
142 {
143 return ThreadPriority(0);
144 }
145 [[nodiscard]] static constexpr auto highest() noexcept -> ThreadPriority
146 {
147 return ThreadPriority(max_priority);
148 }
149
150 [[nodiscard]] constexpr auto operator==(ThreadPriority const& other) const noexcept -> bool
151 {
152 return priority_ == other.priority_;
153 }
154 [[nodiscard]] constexpr auto operator!=(ThreadPriority const& other) const noexcept -> bool
155 {
156 return priority_ != other.priority_;
157 }
158 [[nodiscard]] constexpr auto operator<(ThreadPriority const& other) const noexcept -> bool
159 {
160 return priority_ < other.priority_;
161 }
162 [[nodiscard]] constexpr auto operator<=(ThreadPriority const& other) const noexcept -> bool
163 {
164 return priority_ <= other.priority_;
165 }
166 [[nodiscard]] constexpr auto operator>(ThreadPriority const& other) const noexcept -> bool
167 {
168 return priority_ > other.priority_;
169 }
170 [[nodiscard]] constexpr auto operator>=(ThreadPriority const& other) const noexcept -> bool
171 {
172 return priority_ >= other.priority_;
173 }
174
175 [[nodiscard]] auto to_string() const -> std::string
176 {
177 std::ostringstream oss;
178 oss << "ThreadPriority(" << priority_ << ")";
179 return oss.str();
180 }
181
182 private:
183 static constexpr int min_priority = -20;
184 static constexpr int max_priority = 19;
185 int priority_;
186};
187
222{
223 public:
225 {
226#ifdef _WIN32
227 group_ = 0;
228 mask_ = 0;
229#else
230 CPU_ZERO(&cpuset_);
231#endif
232 }
233
234 explicit ThreadAffinity(std::vector<int> const& cpus) : ThreadAffinity()
235 {
236 for (int cpu : cpus)
237 {
238 add_cpu(cpu);
239 }
240 }
241
242 // Adds a CPU index. On Windows, indices >= 64 select group = cpu/64 automatically.
243 void add_cpu(int cpu)
244 {
245#ifdef _WIN32
246 if (cpu < 0)
247 return;
248 WORD g = static_cast<WORD>(cpu / 64);
249 int bit = cpu % 64;
250 if (!has_any())
251 {
252 group_ = g;
253 }
254 if (g != group_)
255 {
256 // Single-group affinity object: ignore CPUs from other groups
257 return;
258 }
259 mask_ |= (static_cast<unsigned long long>(1) << bit);
260#else
261 if (cpu >= 0 && cpu < CPU_SETSIZE)
262 {
263 CPU_SET(cpu, &cpuset_);
264 }
265#endif
266 }
267
268 void remove_cpu(int cpu)
269 {
270#ifdef _WIN32
271 if (cpu < 0)
272 return;
273 WORD g = static_cast<WORD>(cpu / 64);
274 int bit = cpu % 64;
275 if (g == group_)
276 {
277 mask_ &= ~(static_cast<unsigned long long>(1) << bit);
278 }
279#else
280 if (cpu >= 0 && cpu < CPU_SETSIZE)
281 {
282 CPU_CLR(cpu, &cpuset_);
283 }
284#endif
285 }
286
287 [[nodiscard]] auto is_set(int cpu) const -> bool
288 {
289#ifdef _WIN32
290 if (cpu < 0)
291 return false;
292 WORD g = static_cast<WORD>(cpu / 64);
293 int bit = cpu % 64;
294 return g == group_ && (mask_ & (static_cast<unsigned long long>(1) << bit)) != 0;
295#else
296 return cpu >= 0 && cpu < CPU_SETSIZE && CPU_ISSET(cpu, &cpuset_);
297#endif
298 }
299
300 [[nodiscard]] auto has_cpu(int cpu) const -> bool
301 {
302 return is_set(cpu);
303 }
304
305 void clear()
306 {
307#ifdef _WIN32
308 mask_ = 0;
309#else
310 CPU_ZERO(&cpuset_);
311#endif
312 }
313
314 [[nodiscard]] auto get_cpus() const -> std::vector<int>
315 {
316 std::vector<int> cpus;
317#ifdef _WIN32
318 for (int i = 0; i < 64; ++i)
319 {
320 if (mask_ & (static_cast<unsigned long long>(1) << i))
321 {
322 cpus.push_back(static_cast<int>(group_) * 64 + i);
323 }
324 }
325#else
326 for (int i = 0; i < CPU_SETSIZE; ++i)
327 {
328 if (CPU_ISSET(i, &cpuset_))
329 {
330 cpus.push_back(i);
331 }
332 }
333#endif
334 return cpus;
335 }
336
337#ifdef _WIN32
338 [[nodiscard]] unsigned long long get_mask() const
339 {
340 return mask_;
341 }
342 [[nodiscard]] WORD get_group() const
343 {
344 return group_;
345 }
346 [[nodiscard]] bool has_any() const
347 {
348 return mask_ != 0;
349 }
350#else
351 [[nodiscard]] auto native_handle() const -> cpu_set_t const&
352 {
353 return cpuset_;
354 }
355#endif
356
357 [[nodiscard]] auto to_string() const -> std::string
358 {
359 auto cpus = get_cpus();
360 std::ostringstream oss;
361 oss << "ThreadAffinity({";
362 for (size_t i = 0; i < cpus.size(); ++i)
363 {
364 if (i > 0)
365 oss << ", ";
366 oss << cpus[i];
367 }
368 oss << "})";
369 return oss.str();
370 }
371
372 private:
373#ifdef _WIN32
374 WORD group_;
375 unsigned long long mask_;
376#else
377 cpu_set_t cpuset_;
378#endif
379};
380
416{
417 public:
418#ifdef _WIN32
419 // Windows doesn't use sched_param, but we'll define a compatible type
420 struct sched_param_win
421 {
422 int sched_priority;
423 };
424
426 ThreadPriority priority)
427 {
428 sched_param_win param{};
429 // On Windows, priority is directly used
430 param.sched_priority = priority.value();
431 return param;
432 }
433
435 {
436 // Windows thread priorities range from -15 to +15
437 return 30;
438 }
439#else
442 {
443 sched_param param{};
444
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);
448
449 if (min_prio == -1 || max_prio == -1)
450 {
451 return unexpected(std::make_error_code(std::errc::invalid_argument));
452 }
453
454 param.sched_priority = std::clamp(priority.value(), min_prio, max_prio);
455 return param;
456 }
457
459 {
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);
463
464 if (min_prio == -1 || max_prio == -1)
465 {
466 return unexpected(std::make_error_code(std::errc::invalid_argument));
467 }
468
469 return max_prio - min_prio;
470 }
471#endif
472};
473
477inline auto to_string(SchedulingPolicy policy) -> std::string
478{
479 switch (policy)
480 {
482 return "OTHER";
484 return "FIFO";
486 return "RR";
488 return "BATCH";
490 return "IDLE";
491#if defined(SCHED_DEADLINE) && !defined(_WIN32)
492 case SchedulingPolicy::DEADLINE:
493 return "DEADLINE";
494#endif
495 default:
496 return "UNKNOWN";
497 }
498}
499
500// ---------------------------------------------------------------------------
501// detail:: free functions for thread configuration (priority, policy, affinity)
502//
503// Overloaded by handle type so that every wrapper class can delegate with a
504// single call: detail::apply_priority(handle, priority).
505// ---------------------------------------------------------------------------
506
507namespace detail
508{
509
510#ifdef _WIN32
511
512inline auto map_priority_to_win32(int prio_val) -> int
513{
514 if (prio_val <= -10)
515 return THREAD_PRIORITY_IDLE;
516 if (prio_val <= -5)
517 return THREAD_PRIORITY_LOWEST;
518 if (prio_val < 0)
519 return THREAD_PRIORITY_BELOW_NORMAL;
520 if (prio_val == 0)
521 return THREAD_PRIORITY_NORMAL;
522 if (prio_val <= 5)
523 return THREAD_PRIORITY_ABOVE_NORMAL;
524 if (prio_val <= 10)
525 return THREAD_PRIORITY_HIGHEST;
526 return THREAD_PRIORITY_TIME_CRITICAL;
527}
528
529inline auto apply_priority(HANDLE handle, ThreadPriority priority) -> expected<void, std::error_code>
530{
531 if (!handle)
532 return unexpected(std::make_error_code(std::errc::no_such_process));
533 if (SetThreadPriority(handle, map_priority_to_win32(priority.value())) != 0)
534 return {};
535 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
536}
537
538inline auto apply_scheduling_policy(HANDLE handle, SchedulingPolicy /*policy*/, ThreadPriority priority)
539 -> expected<void, std::error_code>
540{
541 return apply_priority(handle, priority);
542}
543
544inline auto apply_affinity(HANDLE handle, ThreadAffinity const& affinity) -> expected<void, std::error_code>
545{
546 if (!handle)
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");
550 if (hMod)
551 {
552 auto set_group_affinity = reinterpret_cast<SetThreadGroupAffinityFn>(
553 reinterpret_cast<void*>(GetProcAddress(hMod, "SetThreadGroupAffinity")));
554 if (set_group_affinity && affinity.has_any())
555 {
556 GROUP_AFFINITY ga{};
557 ga.Mask = static_cast<KAFFINITY>(affinity.get_mask());
558 ga.Group = affinity.get_group();
559 if (set_group_affinity(handle, &ga, nullptr) != 0)
560 return {};
561 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
562 }
563 }
564 DWORD_PTR mask = static_cast<DWORD_PTR>(affinity.get_mask());
565 if (SetThreadAffinityMask(handle, mask) != 0)
566 return {};
567 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
568}
569
570inline auto apply_name(HANDLE handle, std::string const& name) -> expected<void, std::error_code>
571{
572 if (!handle)
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");
576 if (!hMod)
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")));
580 if (!set_desc)
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())))
584 return {};
585 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
586}
587
588inline auto read_name(HANDLE handle) -> std::optional<std::string>
589{
590 if (!handle)
591 return std::nullopt;
592 using GetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PWSTR*);
593 HMODULE hMod = GetModuleHandleW(L"kernel32.dll");
594 if (!hMod)
595 return std::nullopt;
596 auto get_desc = reinterpret_cast<GetThreadDescriptionFn>(
597 reinterpret_cast<void*>(GetProcAddress(hMod, "GetThreadDescription")));
598 if (!get_desc)
599 return std::nullopt;
600 PWSTR thread_name = nullptr;
601 if (SUCCEEDED(get_desc(handle, &thread_name)) && thread_name)
602 {
603 int size = WideCharToMultiByte(CP_UTF8, 0, thread_name, -1, nullptr, 0, nullptr, nullptr);
604 if (size > 0)
605 {
606 std::string result(size - 1, '\0');
607 WideCharToMultiByte(CP_UTF8, 0, thread_name, -1, &result[0], size, nullptr, nullptr);
608 LocalFree(thread_name);
609 return result;
610 }
611 LocalFree(thread_name);
612 }
613 return std::nullopt;
614}
615
616inline auto read_affinity(HANDLE handle) -> std::optional<ThreadAffinity>
617{
618 if (!handle)
619 return std::nullopt;
620 using GetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE, PGROUP_AFFINITY);
621 HMODULE hMod = GetModuleHandleW(L"kernel32.dll");
622 if (!hMod)
623 return std::nullopt;
624 auto get_group_affinity = reinterpret_cast<GetThreadGroupAffinityFn>(
625 reinterpret_cast<void*>(GetProcAddress(hMod, "GetThreadGroupAffinity")));
626 if (!get_group_affinity)
627 return std::nullopt;
628 GROUP_AFFINITY ga{};
629 if (get_group_affinity(handle, &ga) != 0)
630 {
631 ThreadAffinity affinity;
632 for (int i = 0; i < 64; ++i)
633 {
634 if ((ga.Mask & (static_cast<KAFFINITY>(1) << i)) != 0)
635 affinity.add_cpu(static_cast<int>(ga.Group) * 64 + i);
636 }
637 if (affinity.has_any())
638 return affinity;
639 }
640 return std::nullopt;
641}
642
643inline auto read_priority(HANDLE handle) -> std::optional<int>
644{
645 if (!handle)
646 return std::nullopt;
647 int const priority = GetThreadPriority(handle);
648 if (priority == THREAD_PRIORITY_ERROR_RETURN)
649 return std::nullopt;
650 return priority;
651}
652
653inline auto read_scheduling_policy(HANDLE handle) -> std::optional<SchedulingPolicy>
654{
655 if (!handle)
656 return std::nullopt;
658}
659
660inline auto apply_priority(Tid tid, ThreadPriority priority) -> expected<void, std::error_code>
661{
662 HANDLE handle = OpenThread(THREAD_SET_INFORMATION, FALSE, tid);
663 if (!handle)
664 return unexpected(std::make_error_code(std::errc::no_such_process));
665
666 auto result = apply_priority(handle, priority);
667 CloseHandle(handle);
668 return result;
669}
670
671inline auto apply_scheduling_policy(Tid tid, SchedulingPolicy policy, ThreadPriority priority)
672 -> expected<void, std::error_code>
673{
674 HANDLE handle = OpenThread(THREAD_SET_INFORMATION, FALSE, tid);
675 if (!handle)
676 return unexpected(std::make_error_code(std::errc::no_such_process));
677
678 auto result = apply_scheduling_policy(handle, policy, priority);
679 CloseHandle(handle);
680 return result;
681}
682
683inline auto apply_affinity(Tid tid, ThreadAffinity const& affinity) -> expected<void, std::error_code>
684{
685 HANDLE handle = OpenThread(THREAD_SET_INFORMATION, FALSE, tid);
686 if (!handle)
687 return unexpected(std::make_error_code(std::errc::no_such_process));
688
689 auto result = apply_affinity(handle, affinity);
690 CloseHandle(handle);
691 return result;
692}
693
694inline auto apply_name(Tid tid, std::string const& name) -> expected<void, std::error_code>
695{
696 HANDLE handle = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, tid);
697 if (!handle)
698 return unexpected(std::make_error_code(std::errc::no_such_process));
699
700 auto result = apply_name(handle, name);
701 CloseHandle(handle);
702 return result;
703}
704
705inline auto read_name(Tid tid) -> std::optional<std::string>
706{
707 HANDLE handle = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, tid);
708 if (!handle)
709 return std::nullopt;
710
711 auto result = read_name(handle);
712 CloseHandle(handle);
713 return result;
714}
715
716inline auto read_affinity(Tid tid) -> std::optional<ThreadAffinity>
717{
718 HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
719 if (!handle)
720 return std::nullopt;
721
722 auto result = read_affinity(handle);
723 CloseHandle(handle);
724 return result;
725}
726
727inline auto read_priority(Tid tid) -> std::optional<int>
728{
729 HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
730 if (!handle)
731 return std::nullopt;
732
733 auto result = read_priority(handle);
734 CloseHandle(handle);
735 return result;
736}
737
738inline auto read_scheduling_policy(Tid tid) -> std::optional<SchedulingPolicy>
739{
740 HANDLE handle = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid);
741 if (!handle)
742 return std::nullopt;
743
744 auto result = read_scheduling_policy(handle);
745 CloseHandle(handle);
746 return result;
747}
748
749#else // POSIX
750
751// --- shared implementation for pthread_t and pid_t scheduling ---
752
753template <typename SetSchedFn>
754inline auto apply_sched_params(SchedulingPolicy policy, ThreadPriority priority, SetSchedFn&& set_sched)
756{
757 int const policy_int = static_cast<int>(policy);
758 auto params_result = SchedulerParams::create_for_policy(policy, priority);
759 if (!params_result.has_value())
760 return unexpected(params_result.error());
761 if (set_sched(policy_int, &params_result.value()) == 0)
762 return {};
763 return unexpected(std::error_code(errno, std::generic_category()));
764}
765
766// --- pthread_t overloads (BaseThreadWrapper, ThreadControlBlock, PThreadWrapper) ---
767
768inline auto apply_scheduling_policy(pthread_t handle, SchedulingPolicy policy, ThreadPriority priority)
770{
771 return apply_sched_params(policy, priority,
772 [handle](int p, sched_param* sp) { return pthread_setschedparam(handle, p, sp); });
773}
774
775inline auto apply_priority(pthread_t handle, ThreadPriority priority) -> expected<void, std::error_code>
776{
777 return apply_scheduling_policy(handle, SchedulingPolicy::OTHER, priority);
778}
779
780inline auto apply_affinity(pthread_t handle, ThreadAffinity const& affinity) -> expected<void, std::error_code>
781{
782 if (pthread_setaffinity_np(handle, sizeof(cpu_set_t), &affinity.native_handle()) == 0)
783 return {};
784 return unexpected(std::error_code(errno, std::generic_category()));
785}
786
787inline auto apply_name(pthread_t handle, std::string const& name) -> expected<void, std::error_code>
788{
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)
792 return {};
793 return unexpected(std::error_code(errno, std::generic_category()));
794}
795
796inline auto read_name(pthread_t handle) -> std::optional<std::string>
797{
798 char name[16];
799 if (pthread_getname_np(handle, name, sizeof(name)) == 0)
800 return std::string(name);
801 return std::nullopt;
802}
803
804inline auto read_affinity(pthread_t handle) -> std::optional<ThreadAffinity>
805{
806 cpu_set_t cpuset;
807 CPU_ZERO(&cpuset);
808 if (pthread_getaffinity_np(handle, sizeof(cpu_set_t), &cpuset) == 0)
809 {
810 std::vector<int> cpus;
811 for (int i = 0; i < CPU_SETSIZE; ++i)
812 {
813 if (CPU_ISSET(i, &cpuset))
814 cpus.push_back(i);
815 }
816 return ThreadAffinity(cpus);
817 }
818 return std::nullopt;
819}
820
821inline auto read_priority(pthread_t handle) -> std::optional<int>
822{
823 int policy = 0;
824 sched_param param{};
825 if (pthread_getschedparam(handle, &policy, &param) == 0)
826 return param.sched_priority;
827 return std::nullopt;
828}
829
830inline auto read_scheduling_policy(pthread_t handle) -> std::optional<SchedulingPolicy>
831{
832 int policy = 0;
833 sched_param param{};
834 if (pthread_getschedparam(handle, &policy, &param) == 0)
835 return static_cast<SchedulingPolicy>(policy);
836 return std::nullopt;
837}
838
839// --- pid_t / TID overloads (ThreadByNameView) ---
840
841inline auto apply_scheduling_policy(pid_t tid, SchedulingPolicy policy, ThreadPriority priority)
843{
844 return apply_sched_params(policy, priority,
845 [tid](int p, sched_param* sp) { return sched_setscheduler(tid, p, sp); });
846}
847
849{
851}
852
853inline auto apply_affinity(pid_t tid, ThreadAffinity const& affinity) -> expected<void, std::error_code>
854{
855 if (sched_setaffinity(tid, sizeof(cpu_set_t), &affinity.native_handle()) == 0)
856 return {};
857 return unexpected(std::error_code(errno, std::generic_category()));
858}
859
860inline auto apply_name(pid_t tid, std::string const& name) -> expected<void, std::error_code>
861{
862 if (name.length() > 15)
863 return unexpected(std::make_error_code(std::errc::invalid_argument));
864
865 std::string const path = std::string("/proc/self/task/") + std::to_string(tid) + "/comm";
866 std::ofstream out(path);
867 if (!out)
868 return unexpected(std::error_code(errno, std::generic_category()));
869
870 out << name;
871 out.flush();
872 if (!out)
873 return unexpected(std::error_code(errno, std::generic_category()));
874 return {};
875}
876
877inline auto read_name(pid_t tid) -> std::optional<std::string>
878{
879 std::string const path = std::string("/proc/self/task/") + std::to_string(tid) + "/comm";
880 std::ifstream in(path);
881 if (!in)
882 return std::nullopt;
883
884 std::string current;
885 std::getline(in, current);
886 if (!current.empty() && current.back() == '\n')
887 current.pop_back();
888 return current;
889}
890
891inline auto read_affinity(pid_t tid) -> std::optional<ThreadAffinity>
892{
893 cpu_set_t cpuset;
894 CPU_ZERO(&cpuset);
895 if (sched_getaffinity(tid, sizeof(cpu_set_t), &cpuset) != 0)
896 return std::nullopt;
897
898 std::vector<int> cpus;
899 for (int i = 0; i < CPU_SETSIZE; ++i)
900 {
901 if (CPU_ISSET(i, &cpuset))
902 cpus.push_back(i);
903 }
904 return ThreadAffinity(cpus);
905}
906
907inline auto read_priority(pid_t tid) -> std::optional<int>
908{
909 sched_param param{};
910 if (sched_getparam(tid, &param) == 0)
911 return param.sched_priority;
912 return std::nullopt;
913}
914
915inline auto read_scheduling_policy(pid_t tid) -> std::optional<SchedulingPolicy>
916{
917 int const policy = sched_getscheduler(tid);
918 if (policy == -1)
919 return std::nullopt;
920 return static_cast<SchedulingPolicy>(policy);
921}
922
923#endif
924
925} // namespace detail
926
927} // namespace threadschedule
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.
Definition expected.hpp:215
Exception thrown by expected::value() when the object is in the error state.
Definition expected.hpp:162
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.
auto to_string(SchedulingPolicy policy) -> std::string
String conversion utilities.