ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
thread_control_block.hpp
Go to the documentation of this file.
1#pragma once
2
7#include "../../expected.hpp"
8#include "../callable/copyable_function.hpp"
9#include "../scheduling/native.hpp"
10#include "../thread/identity.hpp"
11#include "../thread_backend.hpp"
12
13#ifdef _WIN32
14# include "../unique_handle.hpp"
15#endif
16
17#include <algorithm>
18#include <cstdint>
19#include <memory>
20#include <optional>
21#include <shared_mutex>
22#include <string>
23#include <system_error>
24#include <thread>
25#include <vector>
26
28{
29
30class thread_registry_backend;
31class registration_guard_backend;
32
70{
72 std::thread::id std_id;
73 std::string name;
74 std::string component;
75 bool alive{ true };
76 std::shared_ptr<class thread_control_block> control;
77};
78
80
122{
123public:
129
131
132 [[nodiscard]] auto
133 tid() const noexcept -> native_thread_id
134 {
135 return tid_;
136 }
137 [[nodiscard]] auto
138 std_id() const noexcept -> std::thread::id
139 {
140 return std_id_;
141 }
142
143private:
144 [[nodiscard]] auto
145 native_handle() const
146 {
147#ifdef _WIN32
148 return handle_.get();
149#else
150 return pthread_handle_;
151#endif
152 }
153
154public:
155 [[nodiscard]] auto
157 {
158 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
159 if (!target_is_active())
160 return inactive_error();
161#ifdef _WIN32
162 return detail::apply_affinity_checked(native_handle(), affinity);
163#else
164 return detail::apply_affinity_checked(tid_, affinity);
165#endif
166 }
167
168 [[nodiscard]] auto
170 {
171 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
172 if (!target_is_active())
173 return inactive_error();
174#ifdef _WIN32
175 return detail::apply_priority(native_handle(), priority);
176#else
177 return detail::apply_scheduling_config(tid_, tid_, detail::native_schedule::posix_nice(priority.value()));
178#endif
179 }
180
181 [[nodiscard]] auto
183 {
184 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
185 if (!target_is_active())
186 return inactive_error();
187#ifdef _WIN32
188 return detail::apply_nice_value(native_handle(), nice_value);
189#else
190 return detail::apply_nice_value(tid_, nice_value);
191#endif
192 }
193
194 [[nodiscard]] auto
195 get_nice_value() const -> expected<int, std::error_code>
196 {
197 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
198 if (!target_is_active())
199 return inactive_error();
200#ifdef _WIN32
201 return detail::read_effective_nice(native_handle(), tid_);
202#else
203 return detail::read_effective_nice(tid_, tid_);
204#endif
205 }
206
207 [[nodiscard]] auto
210 {
211 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
212 if (!target_is_active())
213 return inactive_error();
214#ifdef _WIN32
215 return detail::apply_scheduling_policy(native_handle(), policy, priority);
216#else
217 return detail::apply_scheduling_policy(tid_, policy, priority);
218#endif
219 }
220
221 [[nodiscard]] auto
223 {
224 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
225 if (!target_is_active())
226 return inactive_error();
227#ifdef _WIN32
228 return detail::apply_scheduling_config(native_handle(), tid_, config);
229#else
230 return detail::apply_scheduling_config(tid_, tid_, config);
231#endif
232 }
233
234 [[nodiscard]] auto
235 set_name(std::string const& name) const -> expected<void, std::error_code>
236 {
237 std::shared_lock<std::shared_mutex> lock(lifecycle_mutex_);
238 if (!target_is_active())
239 return inactive_error();
240#ifdef _WIN32
241 return detail::apply_name(native_handle(), name);
242#else
243 return detail::apply_name(tid_, name);
244#endif
245 }
246
247 static auto
248 create_for_current_thread() -> std::shared_ptr<thread_control_block>
249 {
250 auto block = std::make_shared<thread_control_block>();
251 block->tid_ = thread_info::get_thread_id();
252 block->std_id_ = std::this_thread::get_id();
253#ifdef _WIN32
254 HANDLE realHandle = nullptr;
255 if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &realHandle,
256 THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, 0)
257 == 0)
258 throw std::system_error(detail::last_win32_error(), "DuplicateHandle");
259 block->handle_.reset(realHandle);
260#else
261 block->pthread_handle_ = pthread_self();
262 block->start_time_ = read_thread_start_time(block->tid_);
263#endif
264 current_thread_controls().add(block);
265 return block;
266 }
267
268private:
269 struct thread_exit_controls
270 {
271 void
272 add(std::shared_ptr<thread_control_block> const& control)
273 {
274 controls_.erase(
275 std::remove_if(controls_.begin(), controls_.end(), [](auto const& item) { return item.expired(); }),
276 controls_.end());
277 controls_.push_back(control);
278 }
279
280 ~thread_exit_controls()
281 {
282 for (auto const& item : controls_)
283 if (auto control = item.lock())
284 control->deactivate();
285 }
286
287 std::vector<std::weak_ptr<thread_control_block>> controls_;
288 };
289
290 [[nodiscard]] static auto
291 current_thread_controls() -> thread_exit_controls&
292 {
293 thread_local thread_exit_controls controls;
294 return controls;
295 }
296
297 [[nodiscard]] static auto
298 inactive_error() -> unexpected<std::error_code>
299 {
300 return unexpected(std::make_error_code(std::errc::no_such_process));
301 }
302
303 [[nodiscard]] auto
304 target_is_active() const noexcept -> bool
305 {
306 if (!active_)
307 return false;
308#ifdef _WIN32
309 return static_cast<bool>(handle_);
310#else
311 // Without a generation value a recycled TID cannot be distinguished
312 // safely from the original target. Fail closed instead of risking that
313 // native controls are applied to an unrelated thread.
314 if (!start_time_.has_value())
315 return false;
316 return native_thread_is_alive({ tid_, start_time_.value() });
317#endif
318 }
319
320 void
321 deactivate() noexcept
322 {
323 std::unique_lock<std::shared_mutex> lock(lifecycle_mutex_);
324 active_ = false;
325 }
326
328
329 mutable std::shared_mutex lifecycle_mutex_;
330 bool active_{ true };
331 native_thread_id tid_{};
332 std::thread::id std_id_;
333#ifdef _WIN32
334 detail::unique_handle handle_;
335#else
336 pthread_t pthread_handle_{};
337 std::optional<std::uint64_t> start_time_;
338#endif
339};
340
341} // namespace threadschedule::detail
Manages a set of CPU indices to which a thread may be bound.
Definition native.hpp:433
Value-semantic wrapper for a thread scheduling priority.
Definition native.hpp:140
Per-thread control handle for OS-level scheduling operations.
auto tid() const noexcept -> native_thread_id
auto set_name(std::string const &name) const -> expected< void, std::error_code >
thread_control_block(thread_control_block const &)=delete
auto set_scheduling_policy(native_scheduling_policy policy, native_thread_priority priority) const -> expected< void, std::error_code >
thread_control_block(thread_control_block &&)=delete
auto set_affinity(native_thread_affinity const &affinity) const -> expected< void, std::error_code >
auto operator=(thread_control_block &&) -> thread_control_block &=delete
auto configure(native_scheduling_config const &config) const -> expected< void, std::error_code >
auto std_id() const noexcept -> std::thread::id
auto set_nice_value(int nice_value) const -> expected< void, std::error_code >
auto set_priority(native_thread_priority priority) const -> expected< void, std::error_code >
static auto create_for_current_thread() -> std::shared_ptr< thread_control_block >
auto operator=(thread_control_block const &) -> thread_control_block &=delete
auto get_nice_value() const -> expected< int, std::error_code >
static auto get_thread_id() -> native_thread_id
Central registry of threads indexed by OS-level thread ID (native_thread_id).
Strongly-typed nice value in the POSIX range $[-20, 19]$.
Owning thread wrapper with result-based lifecycle/configuration API.
Definition thread.hpp:29
constexpr auto posix_nice(int nice_value) noexcept -> native_scheduling_config
Definition native.hpp:313
Aggregates multiple thread_registry_backend instances into a single queryable view.
auto read_effective_nice(NativeHandle handle, native_thread_id tid) -> expected< int, std::error_code >
Definition native.hpp:847
auto read_thread_start_time(native_thread_id id) noexcept -> std::optional< std::uint64_t >
Definition identity.hpp:41
native_scheduling_policy
Enumeration of available thread scheduling policies.
Definition native.hpp:85
detail::copyable_function< void(registered_thread_info_backend const &)> registry_callback
auto apply_affinity_checked(NativeHandle handle, native_thread_affinity const &affinity) -> expected< void, std::error_code >
Definition native.hpp:790
auto native_thread_is_alive(native_thread_identity const &identity) noexcept -> bool
Definition identity.hpp:74
auto apply_scheduling_config(NativeHandle handle, native_thread_id tid, native_scheduling_config const &config) -> expected< void, std::error_code >
Definition native.hpp:814
std::function< Signature > copyable_function
Snapshot of metadata for a single registered thread.
std::shared_ptr< class thread_control_block > control