ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
jthread.hpp
Go to the documentation of this file.
1#pragma once
2
11#include "thread_config.hpp"
12
13#include <cstdint>
14#include <memory>
15#include <string>
16#include <thread>
17#include <tuple>
18#include <type_traits>
19#include <utility>
20
21namespace threadschedule
22{
23#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L
24class thread_view;
25
29class jthread
30{
31public:
33 jthread() noexcept = default;
40 explicit jthread(std::jthread&& value) noexcept : impl_(std::move(value)) {}
46 template <
47 typename F, typename... Args,
48 std::enable_if_t<!std::is_same_v<std::decay_t<F>, jthread> && !std::is_same_v<std::decay_t<F>, thread_config>
49 && std::is_constructible_v<std::jthread, F, Args...>,
50 int> = 0>
51 explicit jthread(F&& function, Args&&... args)
52 : impl_(make_impl(native_id_, std::forward<F>(function), std::forward<Args>(args)...))
53 {
54 }
55
62 template <typename F, typename... Args>
63 jthread(thread_config const& config, F&& function, Args&&... args)
64 : impl_(make_configured_impl(config, native_id_, std::forward<F>(function), std::forward<Args>(args)...))
65 {
66 }
67
68 jthread(jthread&&) noexcept = default;
69 auto operator=(jthread&&) noexcept -> jthread& = default;
70 jthread(jthread const&) = delete;
71 auto operator=(jthread const&) -> jthread& = delete;
72
78 template <typename F, typename... Args>
79 static auto
80 create(F&& function, Args&&... args)
81 -> std::enable_if_t<!std::is_same_v<std::decay_t<F>, thread_config>, result<jthread>>
82 {
83 return detail::try_result([&]() -> result<jthread>
84 { return jthread(std::forward<F>(function), std::forward<Args>(args)...); });
85 }
86
93 template <typename F, typename... Args>
94 static auto
95 create(thread_config const& config, F&& function, Args&&... args) -> result<jthread>
96 {
97 return detail::try_result([&]() -> result<jthread>
98 { return jthread(config, std::forward<F>(function), std::forward<Args>(args)...); });
99 }
100
102 auto
103 join() -> result<void>
104 {
105 return detail::thread_lifecycle::join(impl_);
106 }
107
109 void
110 join_or_throw()
111 {
112 detail::thread_lifecycle::join_or_throw(impl_, "jthread::join");
113 }
114
116 auto
117 detach() -> result<void>
118 {
119 return detail::thread_lifecycle::detach(impl_);
120 }
121
123 void
124 detach_or_throw()
125 {
126 detail::thread_lifecycle::detach_or_throw(impl_, "jthread::detach");
127 }
128
130 [[nodiscard]] auto
131 joinable() const noexcept -> bool
132 {
133 return impl_.joinable();
134 }
135
137 [[nodiscard]] auto
138 get_id() const noexcept -> std::jthread::id
139 {
140 return impl_.get_id();
141 }
142
144 [[nodiscard]] static auto
145 hardware_concurrency() noexcept -> unsigned
146 {
147 return std::thread::hardware_concurrency();
148 }
149
151 [[nodiscard]] auto
152 request_stop() noexcept -> bool
153 {
154 return impl_.request_stop();
155 }
156
158 [[nodiscard]] auto
159 stop_requested() const noexcept -> bool
160 {
161 return impl_.get_stop_token().stop_requested();
162 }
163
165 [[nodiscard]] auto
166 get_stop_token() const noexcept -> std::stop_token
167 {
168 return impl_.get_stop_token();
169 }
170
172 [[nodiscard]] auto
173 get_stop_source() noexcept -> std::stop_source
174 {
175 return impl_.get_stop_source();
176 }
177
179 auto
180 configure(thread_config const& config) -> result<void>
181 {
182 auto view = native_view();
183 return detail::portable_thread_control::configure(view, config);
184 }
185
187 auto
188 set_priority(priority_level level) -> result<void>
189 {
190 auto view = native_view();
191 return detail::portable_thread_control::set_priority(view, level);
192 }
193
195 auto
196 set_nice(nice_value value) -> result<void>
197 {
198 auto view = native_view();
199 return detail::portable_thread_control::set_nice(view, value);
200 }
201
203 [[nodiscard]] auto
204 get_priority() const -> result<priority_level>
205 {
206 auto view = native_view();
207 return detail::portable_thread_control::get_priority(view.get_nice_value());
208 }
209
211 [[nodiscard]] auto
212 get_nice() const -> result<nice_value>
213 {
214 auto view = native_view();
215 return detail::portable_thread_control::get_nice(view.get_nice_value());
216 }
217
219 auto
220 set_name(std::string const& name) -> result<void>
221 {
222 auto view = native_view();
223 return view.set_name(name);
224 }
225
227 [[nodiscard]] auto
228 get_name() const -> result<std::string>
229 {
230 auto view = native_view();
231 return view.get_name();
232 }
233
235 auto
236 set_affinity(thread_affinity const& affinity) -> result<void>
237 {
238 auto view = native_view();
239 return detail::portable_thread_control::set_affinity(view, affinity);
240 }
241
243 [[nodiscard]] auto
244 get_affinity() const -> result<thread_affinity>
245 {
246 auto view = native_view();
247 return detail::portable_thread_control::get_affinity(view.get_affinity());
248 }
249
251 [[nodiscard]] auto
252 release() noexcept -> std::jthread
253 {
254 auto value = std::move(impl_);
255 native_id_ = {};
256 return value;
257 }
258
259private:
260 friend struct detail::native_thread_access;
261 friend class thread_view;
262
263 using native_view_type = detail::basic_thread_backend<std::jthread, detail::non_owning_tag>;
264
265 [[nodiscard]] auto
266 native_view() const -> native_view_type
267 {
268 return native_view_type(const_cast<std::jthread&>(impl_), native_id_);
269 }
270
271 template <typename F, typename... Args>
272 static auto
273 make_impl(detail::native_thread_id& native_id, F&& function, Args&&... args) -> std::jthread
274 {
275 using function_type = std::decay_t<F>;
276 auto identity = std::make_shared<detail::thread_identity_state>();
277 std::jthread value(
278 [identity, callable = function_type(std::forward<F>(function)),
279 arguments = std::make_tuple(std::forward<Args>(args)...)](std::stop_token token) mutable
280 {
281 identity->publish(detail::current_native_thread_id());
282 detail::invoke_jthread_callable(callable, std::move(arguments), std::move(token));
283 });
284 native_id = identity->wait();
285 return value;
286 }
287
288 template <typename F, typename... Args>
289 static auto
290 make_configured_impl(thread_config const& config, detail::native_thread_id& native_id, F&& function, Args&&... args)
291 -> std::jthread
292 {
293 using function_type = std::decay_t<F>;
294 auto gate = std::make_shared<detail::thread_start_gate>();
295 auto identity = std::make_shared<detail::thread_identity_state>();
296 std::jthread value(
297 [gate, identity, callable = function_type(std::forward<F>(function)),
298 arguments = std::make_tuple(std::forward<Args>(args)...)](std::stop_token token) mutable
299 {
300 identity->publish(detail::current_native_thread_id());
301 if (!gate->wait())
302 return;
303 detail::invoke_jthread_callable(callable, std::move(arguments), std::move(token));
304 });
305
306 native_id = identity->wait();
307 auto rollback = detail::make_scope_exit([&]() noexcept { gate->release(false); });
308 native_view_type view(value, native_id);
309 auto configured = view.configure(detail::to_native(config));
310 if (!configured)
311 {
312 throw std::system_error(configured.error(), "jthread configuration");
313 }
314 gate->release(true);
315 rollback.release();
316 return value;
317 }
318
319 detail::native_thread_id native_id_{};
320 std::jthread impl_;
321};
322#endif
323
324} // namespace threadschedule
auto native_id(thread_id id) noexcept -> result< native_thread_id >
auto set_name(std::string const &name) -> result< void >
Set the calling thread name.
auto get_name() -> result< std::string >
Query the calling thread name.
C++17 thread wrappers and non-owning views.
Portable thread startup and runtime configuration.