64 template <
typename F,
typename... Args>
65 explicit PThreadWrapper(F&& func, Args&&... args) : thread_(0), joined_(false)
69 std::make_unique<std::function<void()>>([fn = std::forward<F>(func),
70 tup = std::make_tuple(std::forward<Args>(args)...)]()
mutable {
71 std::apply(std::move(fn), std::move(tup));
74 int const result = pthread_create(&thread_,
nullptr, thread_function, callable.release());
78 throw std::runtime_error(
"Failed to create pthread: " + std::to_string(result));
90 other.joined_.store(
true);
101 thread_ = other.thread_;
102 joined_.store(other.joined_.load());
104 other.joined_.store(
true);
123 int const result = pthread_join(thread_, &retval);
135 int const result = pthread_detach(thread_);
145 return thread_ != 0 && !joined_;
162 [[nodiscard]]
auto get_name() const -> std::optional<std::
string>
191 if (pthread_cancel(thread_) == 0)
193 return unexpected(std::error_code(errno, std::generic_category()));
198 int const state = enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE;
200 if (pthread_setcancelstate(state, &old_state) == 0)
202 return unexpected(std::error_code(errno, std::generic_category()));
207 int const type = asynchronous ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED;
209 if (pthread_setcanceltype(type, &old_type) == 0)
211 return unexpected(std::error_code(errno, std::generic_category()));
215 template <
typename F,
typename... Args>
220 PThreadWrapper wrapper(std::forward<F>(f), std::forward<Args>(args)...);
226 template <
typename F,
typename... Args>
232 std::make_unique<std::function<void()>>([fn = std::forward<F>(func),
233 tup = std::make_tuple(std::forward<Args>(args)...)]()
mutable {
234 std::apply(std::move(fn), std::move(tup));
237 int const result = pthread_create(&wrapper.thread_, &attr, thread_function, callable.release());
241 throw std::runtime_error(
"Failed to create pthread with attributes: " + std::to_string(result));
249 std::atomic<bool> joined_;
251 static auto thread_function(
void* arg) ->
void*
253 std::unique_ptr<std::function<void()>> func(
static_cast<std::function<
void()
>*>(arg));
285 if (pthread_attr_init(&attr_) != 0)
287 throw std::runtime_error(
"Failed to initialize pthread attributes");
293 pthread_attr_destroy(&attr_);
303 if (pthread_attr_init(&other.attr_) != 0)
313 pthread_attr_destroy(&attr_);
315 if (pthread_attr_init(&other.attr_) != 0)
323 [[nodiscard]]
auto get() const -> pthread_attr_t const&
327 auto get() -> pthread_attr_t&
335 int const state = detached ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE;
336 return pthread_attr_setdetachstate(&attr_, state) == 0;
341 return pthread_attr_setstacksize(&attr_, stack_size) == 0;
346 return pthread_attr_setguardsize(&attr_, guard_size) == 0;
351 int const policy_int =
static_cast<int>(policy);
352 return pthread_attr_setschedpolicy(&attr_, policy_int) == 0;
358 param.sched_priority = priority.value();
359 return pthread_attr_setschedparam(&attr_, ¶m) == 0;
364 int const inherit_val = inherit ? PTHREAD_INHERIT_SCHED : PTHREAD_EXPLICIT_SCHED;
365 return pthread_attr_setinheritsched(&attr_, inherit_val) == 0;
370 int const scope = system_scope ? PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS;
371 return pthread_attr_setscope(&attr_, scope) == 0;
378 if (pthread_attr_getdetachstate(&attr_, &state) == 0)
380 return state == PTHREAD_CREATE_DETACHED;
388 if (pthread_attr_getstacksize(&attr_, &stack_size) == 0)
398 if (pthread_attr_getguardsize(&attr_, &guard_size) == 0)
406 pthread_attr_t attr_;
427 if (pthread_mutex_init(&mutex_,
nullptr) != 0)
429 throw std::runtime_error(
"Failed to initialize pthread mutex");
435 if (pthread_mutex_init(&mutex_, attr) != 0)
437 throw std::runtime_error(
"Failed to initialize pthread mutex with attributes");
443 pthread_mutex_destroy(&mutex_);
451 if (pthread_mutex_lock(&mutex_) != 0)
453 throw std::runtime_error(
"Failed to lock pthread mutex");
459 return pthread_mutex_trylock(&mutex_) == 0;
464 if (pthread_mutex_unlock(&mutex_) != 0)
466 throw std::runtime_error(
"Failed to unlock pthread mutex");
476 pthread_mutex_t mutex_;
auto set_scope(bool system_scope) -> bool
auto get_detach_state() const -> std::optional< bool >
auto set_stack_size(size_t stack_size) -> bool
auto operator=(PThreadAttributes const &) -> PThreadAttributes &=delete
PThreadAttributes(PThreadAttributes &&other) noexcept
auto get() -> pthread_attr_t &
auto set_guard_size(size_t guard_size) -> bool
auto set_scheduling_policy(SchedulingPolicy policy) -> bool
auto set_detach_state(bool detached) -> bool
auto operator=(PThreadAttributes &&other) noexcept -> PThreadAttributes &
auto get_stack_size() const -> std::optional< size_t >
auto get() const -> pthread_attr_t const &
auto set_inherit_sched(bool inherit) -> bool
auto get_guard_size() const -> std::optional< size_t >
auto set_scheduling_parameter(ThreadPriority priority) -> bool
PThreadAttributes(PThreadAttributes const &)=delete
PThreadMutex(PThreadMutex const &)=delete
PThreadMutex(pthread_mutexattr_t const *attr)
auto native_handle() -> pthread_mutex_t *
auto operator=(PThreadMutex const &) -> PThreadMutex &=delete
RAII wrapper around POSIX threads with a modern C++ interface.
auto native_handle() const -> native_handle_type
auto cancel() const -> expected< void, std::error_code >
pthread_t native_handle_type
auto operator=(PThreadWrapper &&other) noexcept -> PThreadWrapper &
auto get_affinity() const -> std::optional< ThreadAffinity >
auto set_affinity(ThreadAffinity const &affinity) const -> expected< void, std::error_code >
PThreadWrapper(PThreadWrapper &&other) noexcept
auto get_id() const -> id
static auto set_cancel_state(bool enabled) -> expected< void, std::error_code >
auto get_name() const -> std::optional< std::string >
PThreadWrapper(F &&func, Args &&... args)
auto joinable() const -> bool
auto operator=(PThreadWrapper const &) -> PThreadWrapper &=delete
PThreadWrapper(PThreadWrapper const &)=delete
auto set_scheduling_policy(SchedulingPolicy policy, ThreadPriority priority) const -> expected< void, std::error_code >
static auto set_cancel_type(bool asynchronous) -> expected< void, std::error_code >
static auto create_with_config(std::string const &name, SchedulingPolicy policy, ThreadPriority priority, F &&f, Args &&... args) -> PThreadWrapper
auto set_priority(ThreadPriority priority) const -> expected< void, std::error_code >
static auto create_with_attributes(pthread_attr_t const &attr, F &&func, Args &&... args) -> PThreadWrapper
auto set_name(std::string const &name) const -> expected< void, std::error_code >
Manages a set of CPU indices to which a thread may be bound.
Value-semantic wrapper for a thread scheduling priority.
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.
C++20 concepts, type traits, and SFINAE helpers for the threading library.
Polyfill for std::expected (C++23) for pre-C++23 compilers.
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.
Scheduling policies, thread priority, and CPU affinity types.
Type trait that identifies thread-like types.