7#include "../../expected.hpp"
8#include "../../export.hpp"
9#include "../callable/copyable_function.hpp"
15#include <shared_mutex>
17#include <system_error>
20#include <unordered_map>
26class auto_register_current_thread;
85 std::unique_lock<std::shared_mutex>
lock(mutex_);
86 for (
auto&
entry : threads_)
87 if (
entry.second.control)
88 entry.second.control->deactivate();
98 info.std_id = std::this_thread::get_id();
99 info.name = std::move(name);
100 info.component = std::move(component);
102 (
void)try_register(std::move(
info));
107 std::string component = std::string())
114 info.name = std::move(name);
115 info.component = std::move(component);
118 (
void)try_register(std::move(
info));
133 std::unique_lock<std::shared_mutex>
lock(mutex_);
134 auto it = threads_.find(tid);
138 auto info = std::move(
it->second);
141 info.control->deactivate();
177 std::shared_lock<std::shared_mutex>
lock(mutex_);
178 auto it = threads_.find(tid);
179 if (
it == threads_.end())
225 template <
typename Predicate>
229 std::vector<registered_thread_info_backend>
filtered;
231 for (
auto const&
entry : entries_)
239 template <
typename Fn>
243 for (
auto const&
entry : entries_)
252 return entries_.size();
258 return entries_.empty();
268 template <
typename Fn>
270 map(
Fn&&
fn)
const -> std::vector<std::invoke_result_t<Fn, registered_thread_info_backend const&>>
272 std::vector<std::invoke_result_t<Fn, registered_thread_info_backend const&>>
result;
273 result.reserve(entries_.size());
274 for (
auto const&
entry : entries_)
282 template <
typename Predicate>
286 for (
auto const&
entry : entries_)
294 template <
typename Predicate>
298 for (
auto const&
entry : entries_)
306 template <
typename Predicate>
310 for (
auto const&
entry : entries_)
318 template <
typename Predicate>
322 return !
any(std::forward<Predicate>(
pred));
337 std::vector<registered_thread_info_backend>
result;
338 if (
n < entries_.size())
340 result.assign(entries_.begin() +
n, entries_.end());
346 std::vector<registered_thread_info_backend> entries_;
353 std::vector<registered_thread_info_backend> snapshot;
354 std::shared_lock<std::shared_mutex>
lock(mutex_);
355 snapshot.reserve(threads_.size());
356 for (
auto const&
kv : threads_)
358 snapshot.push_back(
kv.second);
366 auto blk = lock_block(tid);
368 return unexpected(std::make_error_code(std::errc::no_such_process));
369 return blk->set_affinity(affinity);
375 auto blk = lock_block(tid);
377 return unexpected(std::make_error_code(std::errc::no_such_process));
378 return blk->set_priority(priority);
384 auto blk = lock_block(tid);
386 return unexpected(std::make_error_code(std::errc::no_such_process));
393 auto blk = lock_block(tid);
395 return unexpected(std::make_error_code(std::errc::no_such_process));
396 return blk->get_nice_value();
403 auto blk = lock_block(tid);
405 return unexpected(std::make_error_code(std::errc::no_such_process));
406 return blk->set_scheduling_policy(policy, priority);
412 auto blk = lock_block(tid);
414 return unexpected(std::make_error_code(std::errc::no_such_process));
433 if (
config.affinity.has_value())
441 auto blk = lock_block(tid);
443 return unexpected(std::make_error_code(std::errc::no_such_process));
444 return blk->set_name(name);
450 std::unique_lock<std::shared_mutex>
lock(mutex_);
451 auto const found = threads_.find(tid);
452 if (
found != threads_.end())
453 found->second.name = name;
460 std::unique_lock<std::shared_mutex>
lock(mutex_);
461 on_register_ = std::move(
cb);
464 template <
typename Callback, std::enable_if_t<!std::is_same_v<std::decay_t<Callback>, registry_callback>,
int> = 0>
468 static_assert(std::is_invocable_r_v<void, Callback&, registered_thread_info_backend const&>,
469 "Register callback must be invocable with "
470 "registered_thread_info_backend "
472 std::unique_lock<std::shared_mutex>
lock(mutex_);
474 = detail::make_copyable_function<void(registered_thread_info_backend const&)>(std::forward<Callback>(
cb));
480 std::unique_lock<std::shared_mutex>
lock(mutex_);
481 on_unregister_ = std::move(
cb);
484 template <
typename Callback, std::enable_if_t<!std::is_same_v<std::decay_t<Callback>, registry_callback>,
int> = 0>
488 static_assert(std::is_invocable_r_v<void, Callback&, registered_thread_info_backend const&>,
489 "Unregister callback must be invocable with "
490 "registered_thread_info_backend "
492 std::unique_lock<std::shared_mutex>
lock(mutex_);
494 = detail::make_copyable_function<void(registered_thread_info_backend const&)>(std::forward<Callback>(
cb));
501 std::unique_lock<std::shared_mutex>
lock(mutex_);
502 auto it = threads_.find(
info.tid);
503 if (
it != threads_.end())
506 threads_.emplace(
info.tid, std::move(
info));
533 register_guard(std::shared_ptr<thread_control_block>
const&
control_block, std::string
const& name,
534 std::string
const& component) ->
bool
538 registered_thread_info_backend
info;
542 info.component = component;
545 return try_register(std::move(
info));
549 lock_block(
native_thread_id tid)
const -> std::shared_ptr<thread_control_block>
551 std::shared_lock<std::shared_mutex>
lock(mutex_);
552 auto it = threads_.find(tid);
553 if (
it == threads_.end())
555 return it->second.control;
557 mutable std::shared_mutex mutex_;
558 std::unordered_map<native_thread_id, registered_thread_info_backend> threads_;
564 friend class ::threadschedule::auto_register_current_thread;
596 replace(std::shared_ptr<thread_registry_backend> value)
598 owner = std::move(value);
603 std::shared_ptr<thread_registry_backend>
owner;
604 std::shared_ptr<external_registry_binding_state>
previous;
608#if defined(THREADSCHEDULE_RUNTIME)
614 -> std::shared_ptr<external_registry_binding_state>;
618struct runtime_registry_storage
620 std::shared_ptr<external_registry_binding_state> external;
624registry_storage() -> runtime_registry_storage&
626 static runtime_registry_storage storage;
634 auto const& storage = registry_storage();
635 if (storage.external && storage.external->registry !=
nullptr)
636 return *storage.external->registry;
644 auto& storage = registry_storage();
645 storage.external = reg ==
nullptr ? nullptr : std::make_shared<external_registry_binding_state>(reg);
651 auto& storage = registry_storage();
652 auto*
const previous = storage.external ? storage.external->registry :
nullptr;
660 registry_storage().external = std::move(state);
665 -> std::shared_ptr<external_registry_binding_state>
667 auto& storage = registry_storage();
668 auto previous = std::move(storage.external);
669 storage.external = std::move(state);
676 return registry_storage().external;
Manages a set of CPU indices to which a thread may be bound.
Value-semantic wrapper for a thread scheduling priority.
CRTP mixin that provides functional-style query facade methods.
auto map(Fn &&fn) const -> std::vector< std::invoke_result_t< Fn, registered_thread_info_backend const & > >
Internal movable RAII guard for worker registration.
Per-thread control handle for OS-level scheduling operations.
static auto get_thread_id() -> native_thread_id
Lazy, functional-style query/filter view over a snapshot of registered threads.
auto filter(Predicate &&pred) const -> query_view
query_view(std::vector< registered_thread_info_backend > entries)
auto take(size_t n) const -> query_view
auto all(Predicate &&pred) const -> bool
auto empty() const -> bool
auto count() const -> size_t
auto skip(size_t n) const -> query_view
void for_each(Fn &&fn) const
auto any(Predicate &&pred) const -> bool
auto map(Fn &&fn) const -> std::vector< std::invoke_result_t< Fn, registered_thread_info_backend const & > >
auto none(Predicate &&pred) const -> bool
auto find_if(Predicate &&pred) const -> std::optional< registered_thread_info_backend >
auto entries() const -> std::vector< registered_thread_info_backend > const &
Central registry of threads indexed by OS-level thread ID (native_thread_id).
auto get(native_thread_id tid) const -> std::optional< registered_thread_info_backend >
auto get_nice_value(native_thread_id tid) const -> expected< int, std::error_code >
auto set_nice_value(native_thread_id tid, int nice_value) const -> expected< void, std::error_code >
thread_registry_backend()=default
void set_on_unregister(registry_callback cb)
void register_current_thread(std::shared_ptr< thread_control_block > const &control_block, std::string name=std::string(), std::string component=std::string())
auto configure(native_thread_id tid, native_thread_config const &config) const -> expected< void, std::error_code >
auto set_priority(native_thread_id tid, native_thread_priority priority) const -> expected< void, std::error_code >
auto operator=(thread_registry_backend const &) -> thread_registry_backend &=delete
auto configure(native_thread_id tid, native_scheduling_config const &config) const -> expected< void, std::error_code >
void set_on_register(Callback &&cb)
void set_on_unregister(Callback &&cb)
auto query() const -> query_view
auto set_affinity(native_thread_id tid, native_thread_affinity const &affinity) const -> expected< void, std::error_code >
void unregister_current_thread()
auto set_scheduling_policy(native_thread_id tid, native_scheduling_policy policy, native_thread_priority priority) const -> expected< void, std::error_code >
void update_registered_name(native_thread_id tid, std::string const &name)
auto set_name(native_thread_id tid, std::string const &name) const -> expected< void, std::error_code >
~thread_registry_backend()
thread_registry_backend(thread_registry_backend const &)=delete
void set_on_register(registry_callback cb)
void register_current_thread(std::string name=std::string(), std::string component=std::string())
Strongly-typed nice value in the POSIX range $[-20, 19]$.
#define THREADSCHEDULE_API
Symbol visibility/import-export marker for runtime builds.
Aggregates multiple thread_registry_backend instances into a single queryable view.
native_scheduling_policy
Enumeration of available thread scheduling policies.
detail::copyable_function< void(registered_thread_info_backend const &)> registry_callback
auto runtime_exchange_external_registry_state(std::shared_ptr< external_registry_binding_state > state) -> std::shared_ptr< external_registry_binding_state >
auto runtime_external_registry_state() -> std::shared_ptr< external_registry_binding_state >
void runtime_set_external_registry_state(std::shared_ptr< external_registry_binding_state > state)
void runtime_set_external_registry(thread_registry_backend *reg)
auto runtime_exchange_external_registry(thread_registry_backend *reg) -> thread_registry_backend *
auto runtime_registry() -> thread_registry_backend &
expected< T, std::error_code > result
Standard result type used by public APIs.
Snapshot query facade shared by registry implementations.
std::shared_ptr< external_registry_binding_state > previous
external_registry_binding_state(std::shared_ptr< thread_registry_backend > value)
external_registry_binding_state(thread_registry_backend *value) noexcept
std::shared_ptr< thread_registry_backend > owner
void replace(std::shared_ptr< thread_registry_backend > value)
thread_registry_backend * registry
Snapshot of metadata for a single registered thread.
Native thread metadata and lifecycle-safe control blocks.