NUMA++ 0.10.0
Loading...
Searching...
No Matches
thread.hpp
Go to the documentation of this file.
1/**
2 * @file
3 * @ingroup numapp
4 * @copyright ESO 2024 - European Southern Observatory
5 *
6 * @brief Contains declarations for numapp thread utilities
7 *
8 * @defgroup numapp_thread Thread APIs
9 * @ingroup numapp
10 * @brief NUMA++ thread APIs
11 */
12#ifndef NUMAPP_THREAD_HPP_
13#define NUMAPP_THREAD_HPP_
14#include <numapp/config.hpp>
15
16#include <string>
17#include <string_view>
18#include <system_error>
19#include <thread>
20#include <type_traits>
21
24
25namespace numapp {
26namespace thisThread {
27
28/**
29 * Query the thread id "TID" of the current thread.
30 *
31 * @note This is the Kernel task identifier and is unrelated to any pthread id.
32 *
33 * It is provided in NUMA++ as @c gettid() is not provided by glibc.
34 *
35 * @return callers thread ID.
36 *
37 * @manpages
38 * @manpage{gettid,2}
39 *
40 * @ingroup numapp_thread
41 */
42[[nodiscard]] pid_t GetThreadId() noexcept;
43
44/**
45 * Set thread name for current thread.
46 *
47 * @param thread_name Name of thread. Maximum length (`thread_name.length()`) is 15.
48 * @param[out] ec out-parameter for error reporting.
49 * - std::errc::result_out_of_range if thread_name is longer than 16 characters.
50 * - other errors propagated from underlying prctl call.
51 *
52 * @sa GetThreadName()
53 * @ingroup numapp_thread
54 */
55void SetThreadName(std::string_view thread_name, std::error_code& ec) noexcept;
56
57/**
58 * Set thread name for current thread (throwing version).
59 *
60 * @param thread_name Name of thread. Maximum length (`thread_name.length()`) is 15.
61 * @throws std::system_error on errors.
62 *
63 * @sa GetThreadName()
64 * @ingroup numapp_thread
65 */
66void SetThreadName(std::string_view thread_name);
67
68/**
69 * Get name of current thread.
70 *
71 * @param[out] ec out-parameter for error reporting.
72 *
73 * @return string containing current thread name.
74 * @return empty string if error occurs (error is reported in @a ec).
75 * @throws std::bad_alloc if allocation fails.
76 * @sa SetThreadName()
77 * @ingroup numapp_thread
78 */
79[[nodiscard]] std::string GetThreadName(std::error_code& ec) noexcept;
80
81/**
82 * Get name of current thread (throwing version).
83 *
84 * @return string containing current thread name.
85 * @return empty string if error occurs (error is reported in @a ec).
86 * @throws std::bad_alloc if allocation fails.
87 * @sa SetThreadName()
88 * @ingroup numapp_thread
89 */
90[[nodiscard]] std::string GetThreadName();
91
92} // namespace thisThread
93
94/**
95 * @name Makes a std::thread or std::jthread with provided NUMA policies
96 * @anchor make_thread
97 * Create a named thread with optional CPU affinity, scheduler and memory policies.
98 *
99 * Function has the following effects in this thread `(P)arent` and new thread `(C)hild`:
100 *
101 * - <sup>(P)</sup>Create std::thread with an unspecified *thread-setup* function as target.
102 * - <sup>(P)</sup>Wait for child to be created and complete setup.
103 * - <sup>(C)</sup>Function will set thread name and apply policies.
104 * - <sup>(C)</sup>Function will apply memory policy to thread stack memory which will move
105 * physical pages if necessary. This is done with strict MemPolicyFlag such that if moving pages
106 * fails the thread creation will fail.
107 * - <sup>(C)</sup>Function signals parent thread with success/failure.
108 * - <sup>(C)</sup>If setup was successful invoke @c func with @a args, and in case of std::jthread
109 * the associated `std::stop_token` if func is invocable with it, otherwise return.
110 * - <sup>(P)</sup>Wake on signal from child:
111 * - If setup of new thread failed it will join with thread and throw @c std:system_error
112 * containing error.
113 * - On success it returns thread.
114 *
115 * @param thread_name Name of thread, Must be maximum 16 characters (15 + '\0').
116 * @param policies The NUMA policies to apply.
117 * @param func Callable invoked in new thread.
118 * @param args Arguments for func which will be decay-copied. Use `std::reference_wrapper` to pass
119 * references (remember: caller must ensure the life-time of references objects).
120 * @throws std::system_error if new thread failed to apply policies.
121 *
122 * Minimum application example with a `main()` function:
123 * @include makeThreadExample.cpp
124 *
125 * Example function that create pinned thread with local NUMA node memory policy:
126 * @include makeThreadExample2.cpp
127 * @ingroup numapp_thread
128 */
129/// @{
130/**
131 * Primary overload accepting string-view for @c thread_name.
132 *
133 * See @ref make_thread "MakeThread group" for detailed documentation.
134 * @ingroup numapp_thread
135 */
136template <class Func, class... Args>
137[[nodiscard]] std::thread MakeThread(std::string_view thread_name,
138 NumaPolicies const& policies,
139 Func&& func,
140 Args&&... args) {
141 auto trampoline = detail::ThreadInitializer(thread_name, policies);
142 auto thr = std::thread(
143 [&, func = std::bind(std::forward<Func>(func), std::forward<Args>(args)...)]() mutable {
144 if (trampoline.Initialize() != std::error_code()) {
145 // Abort
146 return;
147 }
148 // Finally run
149 func();
150 });
151 // Wait for thread to start to complete
152 auto result = trampoline.Wait();
153 if (result.code()) {
154 // Thread failed to set name or apply policy
155 thr.join();
156 throw std::system_error(result);
157 }
158 return thr;
159}
160
161/**
162 * Compatibility overload accepting null terminated C string for thread_name.
163 *
164 * See @ref make_thread "MakeThread group" for detailed documentation.
165 * @ingroup numapp_thread
166 */
167template <class Func, class... Args>
168[[nodiscard]] std::thread
169MakeThread(char const* thread_name, NumaPolicies const& policies, Func&& func, Args&&... args) {
170 return MakeThread(std::string_view(thread_name),
171 policies,
172 std::forward<Func>(func),
173 std::forward<Args>(args)...);
174}
175
176#ifdef __cpp_lib_jthread // C++20
177
178/**
179 * Create std::jthread with specified named and NUMA policies.
180 *
181 * @note Like `std::jthread` it can optionally accept a `std::stop_token` as the first
182 * argument.
183 *
184 * See @ref make_thread "MakeThread group" for detailed documentation.
185 * @ingroup numapp_thread
186 */
187template <class Func, class... Args>
188[[nodiscard]] std::jthread MakeJthread(std::string_view thread_name,
189 NumaPolicies const& policies,
190 Func&& func,
191 Args&&... args) {
192 constexpr bool wants_stop_token = std::is_invocable_v<Func, std::stop_token const&, Args...>;
193
194 auto trampoline = detail::ThreadInitializer(thread_name, policies);
195 auto thr = std::jthread([&,
196 func = detail::TokenPrepender<wants_stop_token>::Bind(
197 std::forward<Func>(func), std::forward<Args>(args)...)](
198 std::stop_token const& token) mutable {
199 if (trampoline.Initialize() != std::error_code()) {
200 // Abort
201 return;
202 }
203 // Finally run, with or without std::stop_token
204 if constexpr (wants_stop_token) {
205 func(token);
206 } else {
207 func();
208 }
209 });
210 // Wait for thread to start to complete
211 auto result = trampoline.Wait();
212 if (result.code()) {
213 // Thread failed to set name or apply policy
214 thr.join();
215 throw std::system_error(result);
216 }
217
218 return thr;
219}
220
221#endif
222/// @}
223
224} // namespace numapp
225#endif // NUMAPP_THREAD_HPP_
Combines the the available NUMA policy types in one object.
NUMA++ configuration.
pid_t GetThreadId() noexcept
Query the thread id "TID" of the current thread.
Definition thread.cpp:27
std::thread MakeThread(std::string_view thread_name, NumaPolicies const &policies, Func &&func, Args &&... args)
Primary overload accepting string-view for thread_name.
Definition thread.hpp:137
void SetThreadName(std::string_view thread_name, std::error_code &ec) noexcept
Set thread name for current thread.
Definition thread.cpp:32
std::string GetThreadName()
Get name of current thread (throwing version).
Definition thread.cpp:75
Contains declarations for NumaPolicies.