56class ThreadControlBlock
59 ThreadControlBlock() =
default;
60 ThreadControlBlock(ThreadControlBlock
const&) =
delete;
61 auto operator=(ThreadControlBlock
const&) -> ThreadControlBlock& =
delete;
62 ThreadControlBlock(ThreadControlBlock&&) =
delete;
63 auto operator=(ThreadControlBlock&&) -> ThreadControlBlock& =
delete;
76 [[nodiscard]]
auto tid()
const noexcept -> Tid
80 [[nodiscard]]
auto std_id()
const noexcept -> std::thread::id
90 return unexpected(std::make_error_code(std::errc::no_such_process));
91 using SetThreadGroupAffinityFn = BOOL(WINAPI*)(HANDLE,
const GROUP_AFFINITY*, PGROUP_AFFINITY);
92 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
95 auto set_group_affinity =
reinterpret_cast<SetThreadGroupAffinityFn
>(
96 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadGroupAffinity")));
97 if (set_group_affinity && affinity.has_any())
100 ga.Mask =
static_cast<KAFFINITY
>(affinity.get_mask());
101 ga.Group = affinity.get_group();
102 if (set_group_affinity(handle_, &ga,
nullptr) != 0)
104 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
107 DWORD_PTR mask =
static_cast<DWORD_PTR
>(affinity.get_mask());
108 if (SetThreadAffinityMask(handle_, mask) != 0)
110 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
112 if (pthread_setaffinity_np(pthreadHandle_,
sizeof(cpu_set_t), &affinity.native_handle()) == 0)
114 return unexpected(std::error_code(errno, std::generic_category()));
122 return unexpected(std::make_error_code(std::errc::no_such_process));
124 int prio_val = priority.value();
126 win_priority = THREAD_PRIORITY_IDLE;
127 else if (prio_val <= -5)
128 win_priority = THREAD_PRIORITY_LOWEST;
129 else if (prio_val < 0)
130 win_priority = THREAD_PRIORITY_BELOW_NORMAL;
131 else if (prio_val == 0)
132 win_priority = THREAD_PRIORITY_NORMAL;
133 else if (prio_val <= 5)
134 win_priority = THREAD_PRIORITY_ABOVE_NORMAL;
135 else if (prio_val <= 10)
136 win_priority = THREAD_PRIORITY_HIGHEST;
138 win_priority = THREAD_PRIORITY_TIME_CRITICAL;
139 if (SetThreadPriority(handle_, win_priority) != 0)
141 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
143 const int policy = SCHED_OTHER;
144 auto params_result = SchedulerParams::create_for_policy(SchedulingPolicy::OTHER, priority);
145 if (!params_result.has_value())
147 if (pthread_setschedparam(pthreadHandle_, policy, ¶ms_result.value()) == 0)
149 return unexpected(std::error_code(errno, std::generic_category()));
153 [[nodiscard]]
auto set_scheduling_policy(SchedulingPolicy policy,
ThreadPriority priority)
const
157 return set_priority(priority);
159 const int policy_int =
static_cast<int>(policy);
160 auto params_result = SchedulerParams::create_for_policy(policy, priority);
161 if (!params_result.has_value())
163 if (pthread_setschedparam(pthreadHandle_, policy_int, ¶ms_result.value()) == 0)
165 return unexpected(std::error_code(errno, std::generic_category()));
173 return unexpected(std::make_error_code(std::errc::no_such_process));
174 using SetThreadDescriptionFn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
175 HMODULE hMod = GetModuleHandleW(L
"kernel32.dll");
177 return unexpected(std::make_error_code(std::errc::function_not_supported));
178 auto set_desc =
reinterpret_cast<SetThreadDescriptionFn
>(
179 reinterpret_cast<void*
>(GetProcAddress(hMod,
"SetThreadDescription")));
181 return unexpected(std::make_error_code(std::errc::function_not_supported));
182 std::wstring wide(name.begin(), name.end());
183 if (SUCCEEDED(set_desc(handle_, wide.c_str())))
185 return unexpected(std::make_error_code(std::errc::operation_not_permitted));
187 if (name.length() > 15)
188 return unexpected(std::make_error_code(std::errc::invalid_argument));
189 if (pthread_setname_np(pthreadHandle_, name.c_str()) == 0)
191 return unexpected(std::error_code(errno, std::generic_category()));
195 static auto create_for_current_thread() -> std::shared_ptr<ThreadControlBlock>
197 auto block = std::make_shared<ThreadControlBlock>();
198 block->tid_ = ThreadInfo::get_thread_id();
199 block->stdId_ = std::this_thread::get_id();
201 HANDLE realHandle =
nullptr;
202 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &realHandle,
203 THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION, FALSE, 0);
204 block->handle_ = realHandle;
206 block->pthreadHandle_ = pthread_self();
213 std::thread::id stdId_;
215 HANDLE handle_ =
nullptr;
217 pthread_t pthreadHandle_{};
224 ThreadRegistry() =
default;
225 ThreadRegistry(ThreadRegistry
const&) =
delete;
226 auto operator=(ThreadRegistry
const&) -> ThreadRegistry& =
delete;
229 void register_current_thread(std::string name = std::string(), std::string componentTag = std::string())
231 Tid
const tid = ThreadInfo::get_thread_id();
234 info.stdId = std::this_thread::get_id();
235 info.name = std::move(name);
236 info.componentTag = std::move(componentTag);
240 std::unique_lock<std::shared_mutex> lock(mutex_);
241 auto it = threads_.find(tid);
242 if (it == threads_.end())
245 threads_.emplace(tid, std::move(info));
248 auto cb = onRegister_;
260 void register_current_thread(std::shared_ptr<ThreadControlBlock>
const& controlBlock,
261 std::string name = std::string(), std::string componentTag = std::string())
266 info.tid = controlBlock->tid();
267 info.stdId = controlBlock->std_id();
268 info.name = std::move(name);
269 info.componentTag = std::move(componentTag);
271 info.control = controlBlock;
272 std::unique_lock<std::shared_mutex> lock(mutex_);
273 auto it = threads_.find(info.tid);
274 if (it == threads_.end())
277 threads_.emplace(info.tid, std::move(info));
280 auto cb = onRegister_;
291 void unregister_current_thread()
293 Tid
const tid = ThreadInfo::get_thread_id();
294 std::unique_lock<std::shared_mutex> lock(mutex_);
295 auto it = threads_.find(tid);
296 if (it != threads_.end())
298 it->second.alive =
false;
299 auto info = it->second;
303 auto cb = onUnregister_;
311 [[nodiscard]]
auto get(Tid tid)
const -> std::optional<RegisteredThreadInfo>
313 std::shared_lock<std::shared_mutex> lock(mutex_);
314 auto it = threads_.find(tid);
315 if (it == threads_.end())
324 explicit QueryView(std::vector<RegisteredThreadInfo> entries) : entries_(std::move(entries))
328 template <
typename Predicate>
329 auto filter(Predicate&& pred)
const -> QueryView
331 std::vector<RegisteredThreadInfo> filtered;
332 filtered.reserve(entries_.size());
333 for (
auto const& entry : entries_)
336 filtered.push_back(entry);
338 return QueryView(std::move(filtered));
341 template <
typename Fn>
342 void for_each(Fn&& fn)
const
344 for (
auto const& entry : entries_)
350 [[nodiscard]]
auto count()
const ->
size_t
352 return entries_.size();
355 [[nodiscard]]
auto empty()
const ->
bool
357 return entries_.empty();
360 [[nodiscard]]
auto entries()
const -> std::vector<RegisteredThreadInfo>
const&
366 template <
typename Fn>
367 [[nodiscard]]
auto map(Fn&& fn)
const -> std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>>
369 std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>> result;
370 result.reserve(entries_.size());
371 for (
auto const& entry : entries_)
373 result.push_back(fn(entry));
379 template <
typename Predicate>
380 [[nodiscard]]
auto find_if(Predicate&& pred)
const -> std::optional<RegisteredThreadInfo>
382 for (
auto const& entry : entries_)
390 template <
typename Predicate>
391 [[nodiscard]]
auto any(Predicate&& pred)
const ->
bool
393 for (
auto const& entry : entries_)
401 template <
typename Predicate>
402 [[nodiscard]]
auto all(Predicate&& pred)
const ->
bool
404 for (
auto const& entry : entries_)
412 template <
typename Predicate>
413 [[nodiscard]]
auto none(Predicate&& pred)
const ->
bool
415 return !any(std::forward<Predicate>(pred));
418 [[nodiscard]]
auto take(
size_t n)
const -> QueryView
420 auto result = entries_;
421 if (result.size() > n)
423 return QueryView(std::move(result));
426 [[nodiscard]]
auto skip(
size_t n)
const -> QueryView
428 std::vector<RegisteredThreadInfo> result;
429 if (n < entries_.size())
431 result.assign(entries_.begin() + n, entries_.end());
433 return QueryView(std::move(result));
437 std::vector<RegisteredThreadInfo> entries_;
441 [[nodiscard]]
auto query() const ->
QueryView
443 std::vector<RegisteredThreadInfo> snapshot;
444 std::shared_lock<std::shared_mutex> lock(mutex_);
445 snapshot.reserve(threads_.size());
446 for (
auto const& kv : threads_)
448 snapshot.push_back(kv.second);
450 return QueryView(std::move(snapshot));
453 template <
typename Predicate>
454 [[nodiscard]]
auto filter(Predicate&& pred)
const -> QueryView
456 return query().filter(std::forward<Predicate>(pred));
459 [[nodiscard]]
auto count() const ->
size_t
461 return query().count();
464 [[nodiscard]]
auto empty() const ->
bool
466 return query().empty();
469 template <
typename Fn>
470 void for_each(Fn&& fn)
const
472 query().for_each(std::forward<Fn>(fn));
475 template <
typename Predicate,
typename Fn>
476 void apply(Predicate&& pred, Fn&& fn)
const
478 query().filter(std::forward<Predicate>(pred)).for_each(std::forward<Fn>(fn));
481 template <
typename Fn>
482 [[nodiscard]]
auto map(Fn&& fn)
const -> std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>>
484 return query().map(std::forward<Fn>(fn));
487 template <
typename Predicate>
488 [[nodiscard]]
auto find_if(Predicate&& pred)
const -> std::optional<RegisteredThreadInfo>
490 return query().find_if(std::forward<Predicate>(pred));
493 template <
typename Predicate>
494 [[nodiscard]]
auto any(Predicate&& pred)
const ->
bool
496 return query().any(std::forward<Predicate>(pred));
499 template <
typename Predicate>
500 [[nodiscard]]
auto all(Predicate&& pred)
const ->
bool
502 return query().all(std::forward<Predicate>(pred));
505 template <
typename Predicate>
506 [[nodiscard]]
auto none(Predicate&& pred)
const ->
bool
508 return query().none(std::forward<Predicate>(pred));
511 [[nodiscard]]
auto take(
size_t n)
const ->
QueryView
513 return query().take(n);
516 [[nodiscard]]
auto skip(
size_t n)
const ->
QueryView
518 return query().skip(n);
521 [[nodiscard]]
auto set_affinity(Tid tid, ThreadAffinity
const& affinity)
const -> expected<void, std::error_code>
523 auto blk = lock_block(tid);
525 return unexpected(std::make_error_code(std::errc::no_such_process));
526 return blk->set_affinity(affinity);
529 [[nodiscard]]
auto set_priority(Tid tid, ThreadPriority priority)
const -> expected<void, std::error_code>
531 auto blk = lock_block(tid);
533 return unexpected(std::make_error_code(std::errc::no_such_process));
534 return blk->set_priority(priority);
537 [[nodiscard]]
auto set_scheduling_policy(Tid tid, SchedulingPolicy policy, ThreadPriority priority)
const
538 -> expected<void, std::error_code>
540 auto blk = lock_block(tid);
542 return unexpected(std::make_error_code(std::errc::no_such_process));
543 return blk->set_scheduling_policy(policy, priority);
546 [[nodiscard]]
auto set_name(Tid tid, std::string
const& name)
const -> expected<void, std::error_code>
548 auto blk = lock_block(tid);
550 return unexpected(std::make_error_code(std::errc::no_such_process));
551 return blk->set_name(name);
555 void set_on_register(std::function<
void(RegisteredThreadInfo
const&)> cb)
557 std::unique_lock<std::shared_mutex> lock(mutex_);
558 onRegister_ = std::move(cb);
561 void set_on_unregister(std::function<
void(RegisteredThreadInfo
const&)> cb)
563 std::unique_lock<std::shared_mutex> lock(mutex_);
564 onUnregister_ = std::move(cb);
568 [[nodiscard]]
auto lock_block(Tid tid)
const -> std::shared_ptr<ThreadControlBlock>
570 std::shared_lock<std::shared_mutex> lock(mutex_);
571 auto it = threads_.find(tid);
572 if (it == threads_.end())
574 return it->second.control;
576 mutable std::shared_mutex mutex_;
577 std::unordered_map<Tid, RegisteredThreadInfo> threads_;
580 std::function<void(RegisteredThreadInfo
const&)> onRegister_;
581 std::function<void(RegisteredThreadInfo
const&)> onUnregister_;
619 std::lock_guard<std::mutex> lock(mutex_);
620 registries_.push_back(reg);
626 std::vector<RegisteredThreadInfo> merged;
627 std::vector<ThreadRegistry*> regs;
629 std::lock_guard<std::mutex> lock(mutex_);
634 auto view = r->query();
635 auto const& entries = view.entries();
636 merged.insert(merged.end(), entries.begin(), entries.end());
641 template <
typename Predicate>
644 return query().filter(std::forward<Predicate>(pred));
647 [[nodiscard]]
auto count()
const ->
size_t
649 return query().count();
652 [[nodiscard]]
auto empty()
const ->
bool
654 return query().empty();
657 template <
typename Fn>
658 void for_each(Fn&& fn)
const
660 query().for_each(std::forward<Fn>(fn));
663 template <
typename Predicate,
typename Fn>
664 void apply(Predicate&& pred, Fn&& fn)
const
666 query().filter(std::forward<Predicate>(pred)).for_each(std::forward<Fn>(fn));
669 template <
typename Fn>
670 [[nodiscard]]
auto map(Fn&& fn)
const -> std::vector<std::invoke_result_t<Fn, RegisteredThreadInfo const&>>
672 return query().map(std::forward<Fn>(fn));
675 template <
typename Predicate>
676 [[nodiscard]]
auto find_if(Predicate&& pred)
const -> std::optional<RegisteredThreadInfo>
678 return query().find_if(std::forward<Predicate>(pred));
681 template <
typename Predicate>
682 [[nodiscard]]
auto any(Predicate&& pred)
const ->
bool
684 return query().any(std::forward<Predicate>(pred));
687 template <
typename Predicate>
688 [[nodiscard]]
auto all(Predicate&& pred)
const ->
bool
690 return query().all(std::forward<Predicate>(pred));
693 template <
typename Predicate>
694 [[nodiscard]]
auto none(Predicate&& pred)
const ->
bool
696 return query().none(std::forward<Predicate>(pred));
701 return query().take(n);
706 return query().skip(n);
710 mutable std::mutex mutex_;
711 std::vector<ThreadRegistry*> registries_;
715class AutoRegisterCurrentThread
718 explicit AutoRegisterCurrentThread(std::string
const& name = std::string(),
719 std::string
const& componentTag = std::string())
720 : active_(
true), externalReg_(
nullptr)
722 auto block = ThreadControlBlock::create_for_current_thread();
723 (void)block->set_name(name);
724 registry().register_current_thread(block, name, componentTag);
727 explicit AutoRegisterCurrentThread(
ThreadRegistry& reg, std::string
const& name = std::string(),
728 std::string
const& componentTag = std::string())
729 : active_(
true), externalReg_(®)
731 auto block = ThreadControlBlock::create_for_current_thread();
732 (void)block->set_name(name);
733 externalReg_->register_current_thread(block, name, componentTag);
735 ~AutoRegisterCurrentThread()
739 if (externalReg_ !=
nullptr)
740 externalReg_->unregister_current_thread();
742 registry().unregister_current_thread();
745 AutoRegisterCurrentThread(AutoRegisterCurrentThread
const&) =
delete;
746 auto operator=(AutoRegisterCurrentThread
const&) -> AutoRegisterCurrentThread& =
delete;
747 AutoRegisterCurrentThread(AutoRegisterCurrentThread&& other) noexcept : active_(other.active_)
749 other.active_ =
false;
751 auto operator=(AutoRegisterCurrentThread&& other)
noexcept -> AutoRegisterCurrentThread&
755 active_ = other.active_;
756 other.active_ =
false;