129class ThreadControlBlock
132 ThreadControlBlock() =
default;
133 ThreadControlBlock(ThreadControlBlock
const&) =
delete;
134 auto operator=(ThreadControlBlock
const&) -> ThreadControlBlock& =
delete;
135 ThreadControlBlock(ThreadControlBlock&&) =
delete;
136 auto operator=(ThreadControlBlock&&) -> ThreadControlBlock& =
delete;
138 ~ThreadControlBlock()
143 CloseHandle(handle_);
149 [[nodiscard]]
auto tid()
const noexcept -> Tid
153 [[nodiscard]]
auto std_id()
const noexcept -> std::thread::id
163 return unexpected(std::make_error_code(std::errc::no_such_process));
164 using SetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE,
const GROUP_AFFINITY*, PGROUP_AFFINITY);
165 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
168 auto set_group_affinity =
reinterpret_cast<SetThreadGroupAffinityFn
>(
169 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadGroupAffinity")));
170 if (set_group_affinity && affinity.has_any())
173 ga.Mask =
static_cast<KAFFINITY
>(affinity.get_mask());
174 ga.Group = affinity.get_group();
175 if (set_group_affinity(handle_, &ga,
nullptr) != 0)
177 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
180 DWORD_PTR mask =
static_cast<DWORD_PTR
>(affinity.get_mask());
181 if (SetThreadAffinityMask(handle_, mask) != 0)
183 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
185 if (pthread_setaffinity_np(pthreadHandle_,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
187 return unexpected(std::error_code(errno, std::generic_category()));
195 return unexpected(std::make_error_code(std::errc::no_such_process));
197 int prio_val = priority.value();
199 win_priority = THREAD_PRIORITY_IDLE;
200 else if (prio_val <= -5)
201 win_priority = THREAD_PRIORITY_LOWEST;
202 else if (prio_val < 0)
203 win_priority = THREAD_PRIORITY_BELOW_NORMAL;
204 else if (prio_val == 0)
205 win_priority = THREAD_PRIORITY_NORMAL;
206 else if (prio_val <= 5)
207 win_priority = THREAD_PRIORITY_ABOVE_NORMAL;
208 else if (prio_val <= 10)
209 win_priority = THREAD_PRIORITY_HIGHEST;
211 win_priority = THREAD_PRIORITY_TIME_CRITICAL;
212 if (SetThreadPriority(handle_, win_priority) != 0)
214 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
216 const int policy = SCHED_OTHER;
217 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
218 if (!params_result.has_value())
220 if (pthread_setschedparam(pthreadHandle_, policy, ¶ms_result.value()) == 0)
222 return unexpected(std::error_code(errno, std::generic_category()));
226 [[nodiscard]]
auto set_scheduling_policy(SchedulingPolicy policy,
ThreadPriority priority)
const
230 return set_priority(priority);
232 const int policy_int =
static_cast<int>(policy);
233 auto params_result = SchedulerParams::create_for_policy(policy, priority);
234 if (!params_result.has_value())
236 if (pthread_setschedparam(pthreadHandle_, policy_int, ¶ms_result.value()) == 0)
238 return unexpected(std::error_code(errno, std::generic_category()));
246 return unexpected(std::make_error_code(std::errc::no_such_process));
247 using SetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
248 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
250 return unexpected(std::make_error_code(std::errc::function_not_supported));
251 auto set_desc =
reinterpret_cast<SetThreadDescriptionFn
>(
252 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadDescription")));
254 return unexpected(std::make_error_code(std::errc::function_not_supported));
255 std::wstring wide(name.begin(), name.end());
256 if (SUCCEEDED(set_desc(handle_, wide.c_str())))
258 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
260 if (name.length() > 15)
261 return unexpected(std::make_error_code(std::errc::invalid_argument));
262 if (pthread_setname_np(pthreadHandle_, name.c_str()) == 0)
264 return unexpected(std::error_code(errno, std::generic_category()));
268 static auto create_for_current_thread() -> std::shared_ptr<ThreadControlBlock>
270 auto block = std::make_shared<ThreadControlBlock>();
271 block->tid_ = ThreadInfo::get_thread_id();
272 block->stdId_ = std::this_thread::get_id();
274 HANDLE realHandle =
nullptr;
275 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &realHandle,
276 THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, 0);
277 block->handle_ = realHandle;
279 block->pthreadHandle_ = pthread_self();
286 std::thread::id stdId_;
288 HANDLE handle_ =
nullptr;
290 pthread_t pthreadHandle_{};
339 ThreadRegistry() =
default;
340 ThreadRegistry(ThreadRegistry
const&) =
delete;
341 auto operator=(ThreadRegistry
const&) -> ThreadRegistry& =
delete;
344 void register_current_thread(std::string name = std::string(), std::string componentTag = std::string())
346 Tid
const tid = ThreadInfo::get_thread_id();
349 info.stdId = std::this_thread::get_id();
350 info.name = std::move(name);
351 info.componentTag = std::move(componentTag);
355 std::unique_lock<std::shared_mutex> lock(mutex_);
356 auto it = threads_.find(tid);
357 if (it == threads_.end())
360 threads_.emplace(tid, std::move(info));
363 auto cb = onRegister_;
375 void register_current_thread(std::shared_ptr<ThreadControlBlock>
const& controlBlock,
376 std::string name = std::string(), std::string componentTag = std::string())
381 info.tid = controlBlock->tid();
382 info.stdId = controlBlock->std_id();
383 info.name = std::move(name);
384 info.componentTag = std::move(componentTag);
386 info.control = controlBlock;
387 std::unique_lock<std::shared_mutex> lock(mutex_);
388 auto it = threads_.find(info.tid);
389 if (it == threads_.end())
392 threads_.emplace(info.tid, std::move(info));
395 auto cb = onRegister_;
406 void unregister_current_thread()
408 Tid
const tid = ThreadInfo::get_thread_id();
409 std::unique_lock<std::shared_mutex> lock(mutex_);
410 auto it = threads_.find(tid);
411 if (it != threads_.end())
413 it->second.alive =
false;
414 auto info = it->second;
418 auto cb = onUnregister_;
426 [[nodiscard]]
auto get(Tid tid)
const -> std::optional<RegisteredThreadInfo>
428 std::shared_lock<std::shared_mutex> lock(mutex_);
429 auto it = threads_.find(tid);
430 if (it == threads_.end())
474 explicit QueryView(std::vector<RegisteredThreadInfo> entries) : entries_(std::move(entries))
478 template <
typename Predicate>
479 auto filter(Predicate&& pred)
const -> QueryView
481 std::vector<RegisteredThreadInfo> filtered;
482 filtered.reserve(entries_.size());
483 for (
auto const& entry : entries_)
486 filtered.push_back(entry);
488 return QueryView(std::move(filtered));
491 template <
typename Fn>
492 void for_each(Fn&& fn)
const
494 for (
auto const& entry : entries_)
500 [[nodiscard]]
auto count()
const ->
size_t
502 return entries_.size();
505 [[nodiscard]]
auto empty()
const ->
bool
507 return entries_.empty();
510 [[nodiscard]]
auto entries()
const -> std::vector<RegisteredThreadInfo>
const&
516 template <
typename Fn>
517 [[nodiscard]]
auto map(Fn&& fn)
const -> std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>>
519 std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>> result;
520 result.reserve(entries_.size());
521 for (
auto const& entry : entries_)
523 result.push_back(fn(entry));
529 template <
typename Predicate>
530 [[nodiscard]]
auto find_if(Predicate&& pred)
const -> std::optional<RegisteredThreadInfo>
532 for (
auto const& entry : entries_)
540 template <
typename Predicate>
541 [[nodiscard]]
auto any(Predicate&& pred)
const ->
bool
543 for (
auto const& entry : entries_)
551 template <
typename Predicate>
552 [[nodiscard]]
auto all(Predicate&& pred)
const ->
bool
554 for (
auto const& entry : entries_)
562 template <
typename Predicate>
563 [[nodiscard]]
auto none(Predicate&& pred)
const ->
bool
565 return !any(std::forward<Predicate>(pred));
568 [[nodiscard]]
auto take(
size_t n)
const -> QueryView
570 auto result = entries_;
571 if (result.size() > n)
573 return QueryView(std::move(result));
576 [[nodiscard]]
auto skip(
size_t n)
const -> QueryView
578 std::vector<RegisteredThreadInfo> result;
579 if (n < entries_.size())
581 result.assign(entries_.begin() + n, entries_.end());
583 return QueryView(std::move(result));
587 std::vector<RegisteredThreadInfo> entries_;
591 [[nodiscard]]
auto query() const ->
QueryView
593 std::vector<RegisteredThreadInfo> snapshot;
594 std::shared_lock<std::shared_mutex> lock(mutex_);
595 snapshot.reserve(threads_.size());
596 for (
auto const& kv : threads_)
598 snapshot.push_back(kv.second);
600 return QueryView(std::move(snapshot));
603 template <
typename Predicate>
604 [[nodiscard]]
auto filter(Predicate&& pred)
const -> QueryView
606 return query().filter(std::forward<Predicate>(pred));
609 [[nodiscard]]
auto count() const ->
size_t
611 return query().count();
614 [[nodiscard]]
auto empty() const ->
bool
616 return query().empty();
619 template <
typename Fn>
620 void for_each(Fn&& fn)
const
622 query().for_each(std::forward<Fn>(fn));
625 template <
typename Predicate,
typename Fn>
626 void apply(Predicate&& pred, Fn&& fn)
const
628 query().filter(std::forward<Predicate>(pred)).for_each(std::forward<Fn>(fn));
631 template <
typename Fn>
632 [[nodiscard]]
auto map(Fn&& fn)
const -> std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>>
634 return query().map(std::forward<Fn>(fn));
637 template <
typename Predicate>
638 [[nodiscard]]
auto find_if(Predicate&& pred)
const -> std::optional<RegisteredThreadInfo>
640 return query().find_if(std::forward<Predicate>(pred));
643 template <
typename Predicate>
644 [[nodiscard]]
auto any(Predicate&& pred)
const ->
bool
646 return query().any(std::forward<Predicate>(pred));
649 template <
typename Predicate>
650 [[nodiscard]]
auto all(Predicate&& pred)
const ->
bool
652 return query().all(std::forward<Predicate>(pred));
655 template <
typename Predicate>
656 [[nodiscard]]
auto none(Predicate&& pred)
const ->
bool
658 return query().none(std::forward<Predicate>(pred));
661 [[nodiscard]]
auto take(
size_t n)
const ->
QueryView
663 return query().take(n);
666 [[nodiscard]]
auto skip(
size_t n)
const ->
QueryView
668 return query().skip(n);
671 [[nodiscard]]
auto set_affinity(Tid tid, ThreadAffinity
const& affinity)
const -> expected<void, std::error_code>
673 auto blk = lock_block(tid);
675 return unexpected(std::make_error_code(std::errc::no_such_process));
676 return blk->set_affinity(affinity);
679 [[nodiscard]]
auto set_priority(Tid tid, ThreadPriority priority)
const -> expected<void, std::error_code>
681 auto blk = lock_block(tid);
683 return unexpected(std::make_error_code(std::errc::no_such_process));
684 return blk->set_priority(priority);
687 [[nodiscard]]
auto set_scheduling_policy(Tid tid, SchedulingPolicy policy, ThreadPriority priority)
const
688 -> expected<void, std::error_code>
690 auto blk = lock_block(tid);
692 return unexpected(std::make_error_code(std::errc::no_such_process));
693 return blk->set_scheduling_policy(policy, priority);
696 [[nodiscard]]
auto set_name(Tid tid, std::string
const& name)
const -> expected<void, std::error_code>
698 auto blk = lock_block(tid);
700 return unexpected(std::make_error_code(std::errc::no_such_process));
701 return blk->set_name(name);
705 void set_on_register(std::function<
void(RegisteredThreadInfo
const&)> cb)
707 std::unique_lock<std::shared_mutex> lock(mutex_);
708 onRegister_ = std::move(cb);
711 void set_on_unregister(std::function<
void(RegisteredThreadInfo
const&)> cb)
713 std::unique_lock<std::shared_mutex> lock(mutex_);
714 onUnregister_ = std::move(cb);
718 [[nodiscard]]
auto lock_block(Tid tid)
const -> std::shared_ptr<ThreadControlBlock>
720 std::shared_lock<std::shared_mutex> lock(mutex_);
721 auto it = threads_.find(tid);
722 if (it == threads_.end())
724 return it->second.control;
726 mutable std::shared_mutex mutex_;
727 std::unordered_map<Tid, RegisteredThreadInfo> threads_;
730 std::function<void(RegisteredThreadInfo
const&)> onRegister_;
731 std::function<void(RegisteredThreadInfo
const&)> onUnregister_;
891 std::lock_guard<std::mutex> lock(mutex_);
892 registries_.push_back(reg);
898 std::vector<RegisteredThreadInfo> merged;
899 std::vector<ThreadRegistry*> regs;
901 std::lock_guard<std::mutex> lock(mutex_);
906 auto view = r->query();
907 auto const& entries = view.entries();
908 merged.insert(merged.end(), entries.begin(), entries.end());
913 template <
typename Predicate>
916 return query().filter(std::forward<Predicate>(pred));
919 [[nodiscard]]
auto count()
const ->
size_t
921 return query().count();
924 [[nodiscard]]
auto empty()
const ->
bool
926 return query().empty();
929 template <
typename Fn>
930 void for_each(Fn&& fn)
const
932 query().for_each(std::forward<Fn>(fn));
935 template <
typename Predicate,
typename Fn>
936 void apply(Predicate&& pred, Fn&& fn)
const
938 query().filter(std::forward<Predicate>(pred)).for_each(std::forward<Fn>(fn));
941 template <
typename Fn>
942 [[nodiscard]]
auto map(Fn&& fn)
const -> std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>>
944 return query().map(std::forward<Fn>(fn));
947 template <
typename Predicate>
948 [[nodiscard]]
auto find_if(Predicate&& pred)
const -> std::optional<RegisteredThreadInfo>
950 return query().find_if(std::forward<Predicate>(pred));
953 template <
typename Predicate>
954 [[nodiscard]]
auto any(Predicate&& pred)
const ->
bool
956 return query().any(std::forward<Predicate>(pred));
959 template <
typename Predicate>
960 [[nodiscard]]
auto all(Predicate&& pred)
const ->
bool
962 return query().all(std::forward<Predicate>(pred));
965 template <
typename Predicate>
966 [[nodiscard]]
auto none(Predicate&& pred)
const ->
bool
968 return query().none(std::forward<Predicate>(pred));
973 return query().take(n);
978 return query().skip(n);
982 mutable std::mutex mutex_;
983 std::vector<ThreadRegistry*> registries_;
1026class AutoRegisterCurrentThread
1029 explicit AutoRegisterCurrentThread(std::string
const& name = std::string(),
1030 std::string
const& componentTag = std::string())
1031 : active_(
true), externalReg_(
nullptr)
1033 auto block = ThreadControlBlock::create_for_current_thread();
1034 (void)block->set_name(name);
1035 registry().register_current_thread(block, name, componentTag);
1038 explicit AutoRegisterCurrentThread(
ThreadRegistry& reg, std::string
const& name = std::string(),
1039 std::string
const& componentTag = std::string())
1040 : active_(
true), externalReg_(®)
1042 auto block = ThreadControlBlock::create_for_current_thread();
1043 (void)block->set_name(name);
1044 externalReg_->register_current_thread(block, name, componentTag);
1046 ~AutoRegisterCurrentThread()
1050 if (externalReg_ !=
nullptr)
1051 externalReg_->unregister_current_thread();
1053 registry().unregister_current_thread();
1056 AutoRegisterCurrentThread(AutoRegisterCurrentThread
const&) =
delete;
1057 auto operator=(AutoRegisterCurrentThread
const&) -> AutoRegisterCurrentThread& =
delete;
1058 AutoRegisterCurrentThread(AutoRegisterCurrentThread&& other) noexcept
1059 : active_(other.active_), externalReg_(other.externalReg_)
1061 other.active_ =
false;
1062 other.externalReg_ =
nullptr;
1064 auto operator=(AutoRegisterCurrentThread&& other)
noexcept -> AutoRegisterCurrentThread&
1070 if (externalReg_ !=
nullptr)
1071 externalReg_->unregister_current_thread();
1073 registry().unregister_current_thread();
1075 active_ = other.active_;
1076 externalReg_ = other.externalReg_;
1077 other.active_ =
false;
1078 other.externalReg_ =
nullptr;