ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
control.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "../../result.hpp"
4#include "../../scheduling.hpp"
5#include "../../thread_affinity.hpp"
6#include "../../thread_config.hpp"
7#include "../scheduling/native.hpp"
8#include "../try_result.hpp"
9
10#include <condition_variable>
11#include <exception>
12#include <memory>
13#include <mutex>
14#include <new>
15#include <system_error>
16#include <tuple>
17#include <type_traits>
18#include <utility>
19#include <vector>
20
21namespace threadschedule
22{
23
24namespace detail
25{
26struct native_thread_access;
27}
28
29namespace detail
30{
31[[nodiscard]] constexpr auto
33{
34 if (nice_value <= -10)
36 if (nice_value < 0)
38 if (nice_value == 0)
40 if (nice_value < 10)
43}
44
46{
47public:
48 [[nodiscard]] auto
49 wait() -> bool
50 {
51 std::unique_lock<std::mutex> lock(mutex_);
52 ready_.wait(lock, [this] { return ready_to_start_; });
53 return run_;
54 }
55
56 void
57 release(bool run)
58 {
59 {
60 std::lock_guard<std::mutex> lock(mutex_);
61 run_ = run;
62 ready_to_start_ = true;
63 }
64 ready_.notify_all();
65 }
66
67private:
68 std::mutex mutex_;
69 std::condition_variable ready_;
70 bool ready_to_start_{ false };
71 bool run_{ false };
72};
73
74[[nodiscard]] constexpr auto
76{
77 switch (config.intent())
78 {
86 return native_schedule::realtime_fifo(config.priority_value());
88 return native_schedule::realtime_rr(config.priority_value());
90 return native_schedule::posix_nice(config.priority_value());
92 default:
94 }
95}
96
97[[nodiscard]] inline auto
99{
100 std::vector<int> cpus;
101 cpus.reserve(affinity.cpus().size());
102 for (auto cpu : affinity.cpus())
103 cpus.push_back(static_cast<int>(cpu.value()));
104 native_thread_affinity native(cpus);
105 if (native.get_cpus() != cpus)
106 throw std::system_error(std::make_error_code(std::errc::invalid_argument), "thread affinity is not representable");
107 return native;
108}
109
110[[nodiscard]] inline auto
112{
114 native.name = config.get_name();
115 if (config.get_scheduling())
116 native.scheduling = to_native(*config.get_scheduling());
117 if (config.get_affinity())
118 native.affinity = to_native(*config.get_affinity());
119 return native;
120}
121
122[[nodiscard]] inline auto
123has_thread_configuration(thread_config const& config) noexcept -> bool
124{
125 return !config.empty();
126}
127
128[[nodiscard]] inline auto
130{
131 std::vector<cpu_id> cpus;
132 cpus.reserve(affinity.get_cpus().size());
133 for (auto cpu : affinity.get_cpus())
134 cpus.emplace_back(cpu);
135 return thread_affinity(std::move(cpus));
136}
137
138namespace thread_lifecycle
139{
140template <typename ThreadLike>
141auto
142join(ThreadLike& value) -> result<void>
143{
144 if (!value.joinable())
145 return unexpected(std::make_error_code(std::errc::invalid_argument));
146 return try_result(
147 [&value]() -> result<void>
148 {
149 value.join();
150 return {};
151 });
152}
153
154template <typename ThreadLike>
155void
156join_or_throw(ThreadLike& value, char const* operation)
157{
158 if (!value.joinable())
159 throw std::system_error(std::make_error_code(std::errc::invalid_argument), operation);
160 value.join();
161}
162
163template <typename ThreadLike>
164auto
165detach(ThreadLike& value) -> result<void>
166{
167 if (!value.joinable())
168 return unexpected(std::make_error_code(std::errc::invalid_argument));
169 return try_result(
170 [&value]() -> result<void>
171 {
172 value.detach();
173 return {};
174 });
175}
176
177template <typename ThreadLike>
178void
179detach_or_throw(ThreadLike& value, char const* operation)
180{
181 if (!value.joinable())
182 throw std::system_error(std::make_error_code(std::errc::invalid_argument), operation);
183 value.detach();
184}
185} // namespace thread_lifecycle
186
187namespace portable_thread_control
188{
189template <typename Control>
190auto
191configure(Control& control, thread_config const& config) -> result<void>
192{
193 return try_result([&control, &config]() -> result<void> { return control.configure(to_native(config)); });
194}
195
196template <typename Control>
197auto
198set_priority(Control& control, priority_level level) -> result<void>
199{
200 return control.configure(native_schedule::posix_nice(static_cast<int>(level)));
201}
202
203template <typename Control>
204auto
205set_nice(Control& control, nice_value value) -> result<void>
206{
207 return control.configure(native_schedule::posix_nice(value.value()));
208}
209
210[[nodiscard]] inline auto
211get_priority(result<int> value) -> result<priority_level>
212{
213 if (!value)
214 return unexpected(value.error());
215 return to_priority_level(value.value());
216}
217
218[[nodiscard]] inline auto
219get_nice(result<int> value) -> result<nice_value>
220{
221 if (!value)
222 return unexpected(value.error());
223 return nice_value::create(value.value());
224}
225
226template <typename Control>
227auto
228set_affinity(Control& control, thread_affinity const& affinity) -> result<void>
229{
230 return try_result([&control, &affinity]() -> result<void> { return control.set_affinity(to_native(affinity)); });
231}
232
233[[nodiscard]] inline auto
235{
236 if (!affinity)
237 return unexpected(affinity.error());
238 return from_native(affinity.value());
239}
240} // namespace portable_thread_control
241
242#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L
243template <typename Function, typename Tuple>
244void
245invoke_jthread_callable(Function& callable, Tuple&& arguments, std::stop_token token)
246{
247 using function_type = std::remove_reference_t<Function>;
248 std::apply(
249 [&callable, &token](auto&&... stored)
250 {
251 if constexpr (std::is_invocable_v<function_type, std::stop_token, decltype(stored)...>)
252 std::invoke(std::move(callable), std::move(token), std::forward<decltype(stored)>(stored)...);
253 else
254 std::invoke(std::move(callable), std::forward<decltype(stored)>(stored)...);
255 },
256 std::forward<Tuple>(arguments));
257}
258#endif
259} // namespace detail
260
261} // namespace threadschedule
Manages a set of CPU indices to which a thread may be bound.
Definition native.hpp:433
Strongly-typed nice value in the POSIX range $[-20, 19]$.
constexpr auto value() const noexcept -> int
Return the wrapped nice value.
static auto create(int value) noexcept -> result< nice_value >
Construct a nice value without exceptions.
Immutable scheduling request passed to thread creation/configuration APIs.
Portable thread configuration bundle.
constexpr auto posix_nice(int nice_value) noexcept -> native_scheduling_config
Definition native.hpp:313
constexpr auto interactive() noexcept -> native_scheduling_config
Definition native.hpp:285
constexpr auto low_latency() noexcept -> native_scheduling_config
Definition native.hpp:292
constexpr auto normal() noexcept -> native_scheduling_config
Definition native.hpp:278
constexpr auto realtime_fifo(int priority=80) noexcept -> native_scheduling_config
Definition native.hpp:299
constexpr auto realtime_rr(int priority=80) noexcept -> native_scheduling_config
Definition native.hpp:306
constexpr auto background() noexcept -> native_scheduling_config
Definition native.hpp:271
auto from_native(native_thread_affinity const &affinity) -> thread_affinity
Definition control.hpp:129
constexpr auto to_native(shutdown_policy policy) noexcept -> shutdown_policy_backend
Definition shutdown.hpp:10
constexpr auto to_priority_level(int nice_value) noexcept -> priority_level
Definition control.hpp:32
auto has_thread_configuration(thread_config const &config) noexcept -> bool
Definition control.hpp:123
auto try_result(Function &&function) -> decltype(std::forward< Function >(function)())
priority_level
Portable non-realtime priority levels.
@ nice
Apply an explicit nice-level priority value.
@ low_latency
Favor low-latency execution where possible.
@ background
Prefer throughput and lower CPU contention over responsiveness.
@ realtime_fifo
Request POSIX FIFO realtime scheduling.
@ realtime_round_robin
Request POSIX round-robin realtime scheduling.
@ interactive
Favor responsiveness for user-facing work.
@ normal
Default scheduler behavior with balanced responsiveness.