16# include <libloaderapi.h>
21# include <sys/prctl.h>
22# include <sys/resource.h>
23# include <sys/syscall.h>
41template <
typename ThreadType,
typename OwnershipTag>
59template <
typename ThreadType>
69 [[nodiscard]]
auto underlying() const noexcept -> ThreadType const&
94template <
typename ThreadType>
107 [[nodiscard]]
auto underlying() const noexcept -> ThreadType const&
115template <
typename ThreadLike>
120 if (!thread.set_name(name).has_value())
122 if (!thread.set_scheduling_policy(policy, priority).has_value())
126 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
182template <
typename ThreadType,
typename OwnershipTag = detail::OwningTag>
187 using id =
typename ThreadType::id;
208 underlying().detach();
212 [[nodiscard]]
auto joinable() const noexcept ->
bool
214 return underlying().joinable();
216 [[nodiscard]]
auto get_id() const noexcept ->
id
218 return underlying().get_id();
222 return underlying().native_handle();
230 [[nodiscard]]
auto get_name() const -> std::optional<std::
string>
262 DWORD priority_class;
263 if (nice_value <= -15)
265 priority_class = HIGH_PRIORITY_CLASS;
267 else if (nice_value <= -10)
269 priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
271 else if (nice_value < 10)
273 priority_class = NORMAL_PRIORITY_CLASS;
275 else if (nice_value < 19)
277 priority_class = BELOW_NORMAL_PRIORITY_CLASS;
281 priority_class = IDLE_PRIORITY_CLASS;
283 return SetPriorityClass(GetCurrentProcess(), priority_class) != 0;
285 return setpriority(PRIO_PROCESS, 0, nice_value) == 0;
293 DWORD priority_class = GetPriorityClass(GetCurrentProcess());
294 if (priority_class == 0)
300 switch (priority_class)
302 case HIGH_PRIORITY_CLASS:
304 case ABOVE_NORMAL_PRIORITY_CLASS:
306 case NORMAL_PRIORITY_CLASS:
308 case BELOW_NORMAL_PRIORITY_CLASS:
310 case IDLE_PRIORITY_CLASS:
317 int const nice = getpriority(PRIO_PROCESS, 0);
375 this->underlying() = std::move(t);
378 template <
typename F,
typename... Args>
381 this->underlying() = std::thread(std::forward<F>(f), std::forward<Args>(args)...);
389 this->underlying() = std::move(other.underlying());
398 this->underlying().join();
400 this->underlying() = std::move(other.underlying());
409 this->underlying().join();
416 return std::move(this->underlying());
419 explicit operator std::thread() &&
noexcept
421 return std::move(this->underlying());
425 template <
typename F,
typename... Args>
429 ThreadWrapper wrapper(std::forward<F>(f), std::forward<Args>(args)...);
463 auto get() noexcept -> std::thread&
465 return this->underlying();
467 [[nodiscard]]
auto get() const noexcept -> std::thread const&
469 return this->underlying();
510#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
511class JThreadWrapper :
public BaseThreadWrapper<std::jthread, detail::OwningTag>
519 this->underlying() = std::move(t);
523 auto release() noexcept -> std::jthread
525 return std::move(this->underlying());
528 explicit operator std::jthread() &&
noexcept
530 return std::move(this->underlying());
533 template <
typename F,
typename... Args>
536 this->underlying() = std::jthread(std::forward<F>(f), std::forward<Args>(args)...);
544 this->underlying() = std::move(other.underlying());
551 this->underlying() = std::move(other.underlying());
559 this->underlying().request_stop();
561 [[nodiscard]]
auto stop_requested() const noexcept ->
bool
563 return this->underlying().get_stop_token().stop_requested();
565 [[nodiscard]]
auto get_stop_token() const noexcept -> std::stop_token
567 return this->underlying().get_stop_token();
569 [[nodiscard]]
auto get_stop_source() noexcept -> std::stop_source
571 return this->underlying().get_stop_source();
575 template <
typename F,
typename... Args>
579 JThreadWrapper wrapper(std::forward<F>(f), std::forward<Args>(args)...);
580 (void)wrapper.set_name(name);
581 (void)wrapper.set_scheduling_policy(policy, priority);
619 this->underlying().request_stop();
621 [[nodiscard]]
auto stop_requested() const noexcept ->
bool
623 return this->underlying().get_stop_token().stop_requested();
625 [[nodiscard]]
auto get_stop_token() const noexcept -> std::stop_token
627 return this->underlying().get_stop_token();
629 [[nodiscard]]
auto get_stop_source() noexcept -> std::stop_source
631 return this->underlying().get_stop_source();
635 auto get() noexcept -> std::jthread&
637 return this->underlying();
639 [[nodiscard]]
auto get() const noexcept -> std::jthread const&
641 return this->underlying();
698 DIR* dir = opendir(
"/proc/self/task");
701 struct dirent* entry =
nullptr;
702 while ((entry = readdir(dir)) !=
nullptr)
704 if (entry->d_name[0] ==
'.')
706 std::string tid_str(entry->d_name);
707 std::string path = std::string(
"/proc/self/task/") + tid_str +
"/comm";
708 std::ifstream in(path);
712 std::getline(in, current);
713 if (!current.empty() && current.back() ==
'\n')
717 handle_ =
static_cast<pid_t
>(std::stoi(tid_str));
725 [[nodiscard]]
auto found() const noexcept ->
bool
737 return unexpected(std::make_error_code(std::errc::function_not_supported));
740 return unexpected(std::make_error_code(std::errc::no_such_process));
745 [[nodiscard]]
auto get_name() const -> std::optional<std::
string>
764 return unexpected(std::make_error_code(std::errc::function_not_supported));
767 return unexpected(std::make_error_code(std::errc::no_such_process));
776 return unexpected(std::make_error_code(std::errc::function_not_supported));
779 return unexpected(std::make_error_code(std::errc::no_such_process));
787 return unexpected(std::make_error_code(std::errc::function_not_supported));
790 return unexpected(std::make_error_code(std::errc::no_such_process));
837 bind_current_thread_handle();
851 if (has_native_handle())
856 [[nodiscard]]
auto get_name() const -> std::optional<std::
string>
858 if (has_native_handle())
865 if (has_native_handle())
873 if (has_native_handle())
880 if (has_native_handle())
887 if (has_native_handle())
894 if (has_native_handle())
901 if (has_native_handle())
908 return std::thread::hardware_concurrency();
914 return GetCurrentThreadId();
916 return static_cast<pid_t
>(syscall(SYS_gettid));
931 void bind_current_thread_handle()
934 HANDLE real_handle =
nullptr;
935 if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &real_handle,
936 THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, 0) != 0)
938 nativeHandle_ = real_handle;
939 nativeHandleOwner_ = std::shared_ptr<void>(real_handle, [](
void* handle) {
941 CloseHandle(
static_cast<HANDLE
>(handle));
943 hasNativeHandle_ =
true;
946 nativeHandle_ = pthread_self();
947 hasNativeHandle_ =
true;
951 [[nodiscard]]
auto has_native_handle() const noexcept ->
bool
953 return hasNativeHandle_;
958 return nativeHandle_;
964 std::shared_ptr<void> nativeHandleOwner_;
968 bool hasNativeHandle_{
false};
Polymorphic base providing common thread management operations.
auto native_handle() noexcept -> native_handle_type
static auto get_nice_value() -> std::optional< int >
static auto set_nice_value(int nice_value) -> bool
typename ThreadType::native_handle_type native_handle_type
typename ThreadType::id id
auto get_affinity() const -> std::optional< ThreadAffinity >
auto get_name() const -> std::optional< std::string >
auto set_scheduling_policy(SchedulingPolicy policy, ThreadPriority priority) -> expected< void, std::error_code >
auto get_id() const noexcept -> id
auto set_name(std::string const &name) -> expected< void, std::error_code >
auto set_priority(ThreadPriority priority) -> expected< void, std::error_code >
BaseThreadWrapper(ThreadType &t)
BaseThreadWrapper()=default
virtual ~BaseThreadWrapper()=default
auto joinable() const noexcept -> bool
auto set_affinity(ThreadAffinity const &affinity) -> expected< void, std::error_code >
Manages a set of CPU indices to which a thread may be bound.
auto set_name(std::string const &name) const -> expected< void, std::error_code >
auto set_affinity(ThreadAffinity const &affinity) const -> expected< void, std::error_code >
auto native_handle() const noexcept -> native_handle_type
auto set_priority(ThreadPriority priority) const -> expected< void, std::error_code >
auto set_scheduling_policy(SchedulingPolicy policy, ThreadPriority priority) const -> expected< void, std::error_code >
auto get_name() const -> std::optional< std::string >
ThreadByNameView(const std::string &name)
auto found() const noexcept -> bool
auto set_scheduling_policy(SchedulingPolicy policy, ThreadPriority priority) const -> expected< void, std::error_code >
static auto get_thread_id() -> Tid
auto get_name() const -> std::optional< std::string >
static auto get_current_priority() -> std::optional< int >
auto get_policy() const -> std::optional< SchedulingPolicy >
static auto get_current_policy() -> std::optional< SchedulingPolicy >
auto set_name(std::string const &name) const -> expected< void, std::error_code >
auto get_priority() const -> std::optional< int >
pthread_t native_handle_type
auto set_affinity(ThreadAffinity const &affinity) const -> expected< void, std::error_code >
auto get_affinity() const -> std::optional< ThreadAffinity >
auto thread_id() const noexcept -> Tid
auto set_priority(ThreadPriority priority) const -> expected< void, std::error_code >
static auto hardware_concurrency() -> unsigned int
Value-semantic wrapper for a thread scheduling priority.
Non-owning view over an externally managed std::thread.
ThreadWrapperView(std::thread &t)
auto get() const noexcept -> std::thread const &
auto get() noexcept -> std::thread &
Owning wrapper around std::thread with RAII join-on-destroy semantics.
auto operator=(ThreadWrapper const &) -> ThreadWrapper &=delete
auto release() noexcept -> std::thread
auto operator=(ThreadWrapper &&other) noexcept -> ThreadWrapper &
operator std::thread() &&noexcept
~ThreadWrapper() override
static auto create_with_config(std::string const &name, SchedulingPolicy policy, ThreadPriority priority, F &&f, Args &&... args) -> ThreadWrapper
ThreadWrapper(std::thread &&t) noexcept
ThreadWrapper(ThreadWrapper const &)=delete
ThreadWrapper(ThreadWrapper &&other) noexcept
ThreadWrapper(F &&f, Args &&... args)
auto underlying() const noexcept -> ThreadType const &
ThreadType * external_thread_
ThreadStorage(ThreadType &t)
auto underlying() noexcept -> ThreadType &
auto underlying() noexcept -> ThreadType &
auto underlying() const noexcept -> ThreadType const &
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 configure_thread(ThreadLike &thread, std::string const &name, SchedulingPolicy policy, ThreadPriority priority) -> expected< void, std::error_code >
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 >
SchedulingPolicy
Enumeration of available thread scheduling policies.
ThreadWrapperView JThreadWrapperView
ThreadWrapper JThreadWrapper
Owning wrapper around std::jthread with cooperative cancellation (C++20).
Scheduling policies, thread priority, and CPU affinity types.
Tag type selecting non-owning (pointer) storage in ThreadStorage.
Tag type selecting owning (value) storage in ThreadStorage.