8#include "../expected.hpp"
13#include <condition_variable>
24# include <libloaderapi.h>
26# include <sys/prctl.h>
27# include <sys/resource.h>
28# include <sys/syscall.h>
41 std::lock_guard<std::mutex> lock(mutex_);
45 ready_condition_.notify_all();
51 std::unique_lock<std::mutex> lock(mutex_);
52 ready_condition_.wait(lock, [
this] {
return ready_; });
58 std::condition_variable ready_condition_;
63[[nodiscard]]
inline auto
67 return GetCurrentThreadId();
69 return static_cast<pid_t
>(syscall(SYS_gettid));
83template <
typename ThreadType,
typename OwnershipTag>
102template <
typename ThreadType>
142template <
typename ThreadType>
152 return *external_thread_;
157 return *external_thread_;
160 ThreadType* external_thread_ =
nullptr;
163template <
typename ThreadLike>
171 return thread.set_scheduling_policy(policy, priority);
174template <
typename ThreadLike>
184 if (config.scheduling)
190 if (config.affinity.has_value())
254template <
typename ThreadType,
typename OwnershipTag = detail::owning_tag>
259 using id =
typename ThreadType::id;
285 underlying().detach();
293 return underlying().joinable();
298 return underlying().get_id();
305 return underlying().native_handle();
343 return detail::apply_scheduling_policy(
native_handle(), policy, priority);
363 return unexpected(std::make_error_code(std::errc::operation_not_supported));
405 [[nodiscard]]
static auto
408 return unexpected(std::make_error_code(std::errc::no_such_process));
468 return this->underlying();
474 return this->underlying();
480 this->underlying() = std::move(t);
485 this->underlying() = std::move(t);
489 template <
typename F,
typename... Args>
492 auto identity = std::make_shared<thread_identity_state>();
493 using function_type = std::decay_t<F>;
494 auto arguments = std::make_tuple(std::forward<Args>(args)...);
495 this->underlying() = std::thread(
496 [identity, function = function_type(std::forward<F>(f)), arguments = std::move(arguments)]()
mutable
499 std::apply([&function](
auto&&... stored)
500 { std::invoke(std::move(function), std::forward<
decltype(stored)>(stored)...); },
501 std::move(arguments));
511 this->underlying() = std::move(
other.underlying());
513 other.set_native_id({});
523 this->underlying().
join();
525 this->underlying() = std::move(
other.underlying());
527 other.set_native_id({});
536 this->underlying().join();
544 auto result = std::move(this->underlying());
550 operator std::thread() &&
noexcept
556 template <
typename F,
typename... Args>
561 thread_backend wrapper(std::forward<F>(f), std::forward<Args>(args)...);
567 template <
typename F,
typename... Args>
571 thread_backend wrapper(std::forward<F>(f), std::forward<Args>(args)...);
611 return this->underlying();
616 return this->underlying();
656 bind_current_thread_handle();
670 if (has_native_handle())
671 return detail::apply_name(native_handle(), name);
672 return detail::apply_name(tid_, name);
678 if (has_native_handle())
679 return detail::read_name(native_handle());
680 return detail::read_name(tid_);
687 if (has_native_handle())
688 return detail::apply_priority(native_handle(), priority);
689 return detail::apply_priority(tid_, priority);
691 if (has_native_handle())
702 if (has_native_handle())
703 return detail::apply_scheduling_policy(native_handle(), policy, priority);
704 return detail::apply_scheduling_policy(tid_, policy, priority);
710 if (has_native_handle())
724 if (has_native_handle())
732 if (has_native_handle())
733 return detail::read_affinity(native_handle());
734 return detail::read_affinity(tid_);
740 if (has_native_handle())
741 return detail::read_scheduling_policy(native_handle());
742 return detail::read_scheduling_policy(tid_);
748 if (has_native_handle())
749 return detail::read_priority(native_handle());
750 return detail::read_priority(tid_);
756 return std::thread::hardware_concurrency();
763 return GetCurrentThreadId();
765 return static_cast<pid_t
>(syscall(SYS_gettid));
783 bind_current_thread_handle()
786 HANDLE real_handle =
nullptr;
787 if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &real_handle,
788 THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, 0)
791 native_handle_owner_ = std::make_shared<unique_handle>(real_handle);
792 native_handle_ = native_handle_owner_->get();
793 has_native_handle_ =
true;
796 native_handle_ = pthread_self();
797 has_native_handle_ =
true;
802 has_native_handle() const noexcept ->
bool
804 return has_native_handle_;
810 return native_handle_;
816 std::shared_ptr<unique_handle> native_handle_owner_;
820 bool has_native_handle_{
false };
Polymorphic base providing common thread management operations.
auto get_id() const noexcept -> id
auto set_nice_value(int nice_value) -> expected< void, std::error_code >
static auto no_such_thread() -> unexpected< std::error_code >
auto native_handle() noexcept -> native_handle_type
auto configure(native_thread_config const &config) -> expected< void, std::error_code >
virtual ~basic_thread_backend()=default
auto set_name(std::string const &name) -> expected< void, std::error_code >
native_thread_id native_id_
auto native_id() const noexcept -> native_thread_id
auto get_affinity() const -> expected< native_thread_affinity, std::error_code >
auto get_nice_value() const -> expected< int, std::error_code >
auto joinable() const noexcept -> bool
basic_thread_backend(ThreadType &t)
auto configure(native_scheduling_config const &config) -> expected< void, std::error_code >
auto set_affinity(native_thread_affinity const &affinity) -> expected< void, std::error_code >
typename ThreadType::native_handle_type native_handle_type
auto set_scheduling_policy(native_scheduling_policy policy, native_thread_priority priority) -> expected< void, std::error_code >
basic_thread_backend()=default
typename ThreadType::id id
basic_thread_backend(ThreadType &t, native_thread_id tid)
auto set_priority(native_thread_priority priority) -> expected< void, std::error_code >
auto get_name() const -> expected< std::string, std::error_code >
void set_native_id(native_thread_id tid) noexcept
Manages a set of CPU indices to which a thread may be bound.
Value-semantic wrapper for a thread scheduling priority.
Owning wrapper around std::thread with RAII join-on-destroy semantics.
auto get() const noexcept -> std::thread const &
static auto create_with_config(std::string const &name, native_scheduling_policy policy, native_thread_priority priority, F &&f, Args &&... args) -> thread_backend
static auto create_with_config(native_thread_config const &config, F &&f, Args &&... args) -> thread_backend
auto get() noexcept -> std::thread &
auto operator=(thread_backend &&other) noexcept -> thread_backend &
thread_backend(std::thread &&t, native_thread_id tid) noexcept
~thread_backend() override
thread_backend(thread_backend &&other) noexcept
thread_backend(std::thread &&t) noexcept
thread_backend(F &&f, Args &&... args)
auto operator=(thread_backend const &) -> thread_backend &=delete
thread_backend(thread_backend const &)=delete
auto release() noexcept -> std::thread
auto wait() -> native_thread_id
void publish(native_thread_id tid)
Lightweight handle for querying and controlling a specific OS thread.
static auto get_thread_id() -> native_thread_id
auto set_affinity(native_thread_affinity const &affinity) const -> expected< void, std::error_code >
static auto hardware_concurrency() -> unsigned int
static auto get_current_priority() -> std::optional< int >
auto set_scheduling_policy(native_scheduling_policy policy, native_thread_priority priority) const -> expected< void, std::error_code >
auto get_name() const -> expected< std::string, std::error_code >
auto get_policy() const -> std::optional< native_scheduling_policy >
auto configure(native_scheduling_config const &config) const -> expected< void, std::error_code >
auto set_priority(native_thread_priority priority) const -> expected< void, std::error_code >
auto configure(native_thread_config const &config) const -> expected< void, std::error_code >
auto get_priority() const -> std::optional< int >
auto get_affinity() const -> expected< native_thread_affinity, std::error_code >
thread_info(native_thread_id tid)
static auto get_current_policy() -> std::optional< native_scheduling_policy >
pthread_t native_handle_type
auto set_name(std::string const &name) const -> expected< void, std::error_code >
auto thread_id() const noexcept -> native_thread_id
thread_storage(ThreadType &t)
auto underlying() const noexcept -> ThreadType const &
auto underlying() noexcept -> ThreadType &
auto underlying() const noexcept -> ThreadType const &
auto underlying() noexcept -> ThreadType &
Non-owning view over an externally managed std::thread.
thread_view_backend(std::thread &t)
thread_view_backend(std::thread &t, native_thread_id tid)
auto get() const noexcept -> std::thread const &
auto get() noexcept -> std::thread &
Strongly-typed nice value in the POSIX range $[-20, 19]$.
Owning thread wrapper with result-based lifecycle/configuration API.
auto set_name(std::string const &name) -> result< void >
Set thread name.
auto configure(thread_config const &config) -> result< void >
Apply a full portable thread configuration to the running thread.
auto set_affinity(thread_affinity const &affinity) -> result< void >
Apply CPU affinity set.
constexpr auto posix_nice(int nice_value) noexcept -> native_scheduling_config
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 >
native_scheduling_policy
Enumeration of available thread scheduling policies.
@ other
Standard round-robin time-sharing.
auto configure_thread(ThreadLike &thread, std::string const &name, native_scheduling_policy policy, native_thread_priority priority) -> expected< void, std::error_code >
auto apply_affinity_checked(NativeHandle handle, native_thread_affinity const &affinity) -> expected< void, std::error_code >
auto current_native_thread_id() noexcept -> native_thread_id
auto apply_scheduling_config(NativeHandle handle, native_thread_id tid, native_scheduling_config const &config) -> expected< void, std::error_code >
expected< T, std::error_code > result
Standard result type used by public APIs.
Scheduling policies, thread priority, and CPU affinity types.
Tag type selecting non-owning (pointer) storage in thread_storage.
Tag type selecting owning (value) storage in thread_storage.