ThreadSchedule 3.0.0
Modern C++ thread management library
Loading...
Searching...
No Matches
windows.hpp
Go to the documentation of this file.
1#pragma once
2
11inline auto last_win32_error() -> std::error_code;
12
13inline auto
14apply_priority(HANDLE handle, native_thread_priority priority) -> expected<void, std::error_code>
15{
16 if (!handle)
17 return unexpected(std::make_error_code(std::errc::no_such_process));
18 if (SetThreadPriority(handle, map_priority_to_win32(priority.value())) != 0)
19 return {};
20 return unexpected(std::error_code(static_cast<int>(GetLastError()), std::system_category()));
21}
22
23inline auto
24apply_windows_thread_priority(HANDLE handle, int priority) -> expected<void, std::error_code>
25{
26 if (!handle)
27 return unexpected(std::make_error_code(std::errc::no_such_process));
28 if (SetThreadPriority(handle, priority) != 0)
29 return {};
30 return unexpected(last_win32_error());
31}
32
33inline auto
34apply_nice_value(HANDLE handle, int nice_value) -> expected<void, std::error_code>
35{
36 if (nice_value < -20 || nice_value > 19)
37 return unexpected(std::make_error_code(std::errc::invalid_argument));
38 return apply_priority(handle, native_thread_priority{ nice_value });
39}
40
41inline auto
42apply_scheduling_policy(HANDLE handle, native_scheduling_policy policy, native_thread_priority priority)
43 -> expected<void, std::error_code>
44{
45 if (!handle)
46 return unexpected(std::make_error_code(std::errc::no_such_process));
47 auto params_result = scheduler_parameters::create_for_policy(policy, priority);
48 if (!params_result.has_value())
49 return unexpected(params_result.error());
50 if (SetThreadPriority(handle, params_result.value().sched_priority) != 0)
51 return {};
52 return unexpected(std::error_code(static_cast<int>(GetLastError()), std::system_category()));
53}
54
55inline auto
56apply_affinity(HANDLE handle, native_thread_affinity const& affinity) -> expected<void, std::error_code>
57{
58 if (!handle)
59 return unexpected(std::make_error_code(std::errc::no_such_process));
60 if (!affinity.has_any())
61 return unexpected(std::make_error_code(std::errc::invalid_argument));
62 using set_thread_group_affinity_fn = BOOL(WINAPI*)(HANDLE, const GROUP_AFFINITY*, PGROUP_AFFINITY);
63 HMODULE module = GetModuleHandleW(L"kernel32.dll");
64 if (module)
65 {
66 auto set_group_affinity = reinterpret_cast<set_thread_group_affinity_fn>(
67 reinterpret_cast<void*>(GetProcAddress(module, "SetThreadGroupAffinity")));
68 if (set_group_affinity)
69 {
70 GROUP_AFFINITY ga{};
71 ga.Mask = static_cast<KAFFINITY>(affinity.get_mask());
72 ga.Group = affinity.get_group();
73 if (set_group_affinity(handle, &ga, nullptr) != 0)
74 return {};
75 return unexpected(last_win32_error());
76 }
77 }
78 if (affinity.get_group() != 0)
79 return unexpected(std::make_error_code(std::errc::function_not_supported));
80 DWORD_PTR mask = static_cast<DWORD_PTR>(affinity.get_mask());
81 if (SetThreadAffinityMask(handle, mask) != 0)
82 return {};
83 return unexpected(last_win32_error());
84}
85
86using set_thread_description_fn = HRESULT(WINAPI*)(HANDLE, PCWSTR);
87using get_thread_description_fn = HRESULT(WINAPI*)(HANDLE, PWSTR*);
88
89class hresult_error_category final : public std::error_category
90{
91public:
92 [[nodiscard]] auto
93 name() const noexcept -> char const* override
94 {
95 return "HRESULT";
96 }
97
98 [[nodiscard]] auto
99 message(int value) const -> std::string override
100 {
101 std::ostringstream stream;
102 stream << "HRESULT 0x" << std::hex << static_cast<std::uint32_t>(value);
103 return stream.str();
104 }
105};
106
107inline auto
108hresult_category() -> std::error_category const&
109{
110 static hresult_error_category const category;
111 return category;
112}
113
114inline auto
115error_from_hresult(HRESULT result) -> std::error_code
116{
117 if (HRESULT_FACILITY(result) == FACILITY_WIN32)
118 return { static_cast<int>(HRESULT_CODE(result)), std::system_category() };
119 return { static_cast<int>(result), hresult_category() };
120}
121
122inline auto
123last_win32_error() -> std::error_code
124{
125 return { static_cast<int>(GetLastError()), std::system_category() };
126}
127
135
136using module_lookup_fn = HMODULE(WINAPI*)(LPCWSTR);
137using proc_lookup_fn = FARPROC(WINAPI*)(HMODULE, LPCSTR);
138
139inline auto
140resolve_thread_description_api(module_lookup_fn module_lookup = GetModuleHandleW,
141 proc_lookup_fn proc_lookup = GetProcAddress) -> thread_description_api
142{
144 DWORD last_error = ERROR_SUCCESS;
145 constexpr wchar_t const* modules[] = { L"kernel32.dll", L"kernelbase.dll" };
146 for (auto const* module_name : modules)
147 {
148 SetLastError(ERROR_SUCCESS);
149 HMODULE const module = module_lookup(module_name);
150 if (!module)
151 {
152 DWORD const error = GetLastError();
153 if (error != ERROR_SUCCESS)
154 last_error = error;
155 continue;
156 }
157 result.found_module = true;
158 if (!result.set)
159 result.set = reinterpret_cast<set_thread_description_fn>(
160 reinterpret_cast<void*>(proc_lookup(module, "SetThreadDescription")));
161 if (!result.get)
162 result.get = reinterpret_cast<get_thread_description_fn>(
163 reinterpret_cast<void*>(proc_lookup(module, "GetThreadDescription")));
164 }
165 if (!result.found_module)
166 result.lookup_error
167 = { static_cast<int>(last_error == ERROR_SUCCESS ? ERROR_MOD_NOT_FOUND : last_error), std::system_category() };
168 return result;
169}
170
171inline auto
172resolved_set_thread_description() -> expected<set_thread_description_fn, std::error_code>
173{
174 static std::atomic<set_thread_description_fn> cached{ nullptr };
175 if (auto const function = cached.load(std::memory_order_acquire))
176 return function;
177 auto const resolved = resolve_thread_description_api();
178 if (!resolved.set)
179 {
180 std::error_code const error
181 = resolved.found_module ? std::make_error_code(std::errc::function_not_supported) : resolved.lookup_error;
182 return unexpected(error);
183 }
184 set_thread_description_fn expected_null = nullptr;
185 cached.compare_exchange_strong(expected_null, resolved.set, std::memory_order_release, std::memory_order_acquire);
186 return expected_null ? expected_null : resolved.set;
187}
188
189inline auto
190resolved_get_thread_description() -> expected<get_thread_description_fn, std::error_code>
191{
192 static std::atomic<get_thread_description_fn> cached{ nullptr };
193 if (auto const function = cached.load(std::memory_order_acquire))
194 return function;
195 auto const resolved = resolve_thread_description_api();
196 if (!resolved.get)
197 {
198 std::error_code const error
199 = resolved.found_module ? std::make_error_code(std::errc::function_not_supported) : resolved.lookup_error;
200 return unexpected(error);
201 }
202 get_thread_description_fn expected_null = nullptr;
203 cached.compare_exchange_strong(expected_null, resolved.get, std::memory_order_release, std::memory_order_acquire);
204 return expected_null ? expected_null : resolved.get;
205}
206
207inline auto
208utf8_to_utf16(std::string const& value) -> expected<std::wstring, std::error_code>
209{
210 if (value.empty())
211 return std::wstring{};
212 int const size
213 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.data(), static_cast<int>(value.size()), nullptr, 0);
214 if (size == 0)
215 return unexpected(last_win32_error());
216 std::wstring result(static_cast<std::size_t>(size), L'\0');
217 int const written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.data(), static_cast<int>(value.size()),
218 result.data(), size);
219 if (written == 0)
220 return unexpected(last_win32_error());
221 result.resize(static_cast<std::size_t>(written));
222 return result;
223}
224
226{
227 void
228 operator()(wchar_t* value) const noexcept
229 {
230 if (value)
231 LocalFree(value);
232 }
233};
234
235inline auto
236utf16_to_utf8(PCWSTR value) -> expected<std::string, std::error_code>
237{
238 int const size = WideCharToMultiByte(CP_UTF8, 0, value, -1, nullptr, 0, nullptr, nullptr);
239 if (size == 0)
240 return unexpected(last_win32_error());
241 std::string result(static_cast<std::size_t>(size), '\0');
242 int const written = WideCharToMultiByte(CP_UTF8, 0, value, -1, result.data(), size, nullptr, nullptr);
243 if (written == 0)
244 return unexpected(last_win32_error());
245 result.resize(static_cast<std::size_t>(written - 1));
246 return result;
247}
248
249inline auto
250apply_name(HANDLE handle, std::string const& name) -> expected<void, std::error_code>
251{
252 if (!handle)
253 return unexpected(std::make_error_code(std::errc::no_such_process));
254 auto const wide = utf8_to_utf16(name);
255 if (!wide.has_value())
256 return unexpected(wide.error());
257 auto const set_description = resolved_set_thread_description();
258 if (!set_description.has_value())
259 return unexpected(set_description.error());
260 HRESULT const result = set_description.value()(handle, wide.value().c_str());
261 if (SUCCEEDED(result))
262 return {};
263 return unexpected(error_from_hresult(result));
264}
265
266inline auto
267read_name(HANDLE handle) -> expected<std::string, std::error_code>
268{
269 if (!handle)
270 return unexpected(std::make_error_code(std::errc::no_such_process));
271 auto const get_description = resolved_get_thread_description();
272 if (!get_description.has_value())
273 return unexpected(get_description.error());
274 PWSTR raw_name = nullptr;
275 HRESULT const result = get_description.value()(handle, &raw_name);
276 std::unique_ptr<wchar_t, local_free_deleter> name(raw_name);
277 if (FAILED(result))
278 return unexpected(error_from_hresult(result));
279 if (!name)
280 return unexpected(std::make_error_code(std::errc::io_error));
281 return utf16_to_utf8(name.get());
282}
283
284inline auto
285read_affinity(HANDLE handle) -> expected<native_thread_affinity, std::error_code>
286{
287 if (!handle)
288 return unexpected(std::make_error_code(std::errc::no_such_process));
289 using get_thread_group_affinity_fn = BOOL(WINAPI*)(HANDLE, PGROUP_AFFINITY);
290 HMODULE module = GetModuleHandleW(L"kernel32.dll");
291 if (!module)
292 return unexpected(last_win32_error());
293 auto get_group_affinity = reinterpret_cast<get_thread_group_affinity_fn>(
294 reinterpret_cast<void*>(GetProcAddress(module, "GetThreadGroupAffinity")));
295 if (!get_group_affinity)
296 return unexpected(std::make_error_code(std::errc::function_not_supported));
297 GROUP_AFFINITY ga{};
298 if (get_group_affinity(handle, &ga) == 0)
299 return unexpected(last_win32_error());
300
301 native_thread_affinity affinity;
302 for (int i = 0; i < 64; ++i)
303 {
304 if ((ga.Mask & (static_cast<KAFFINITY>(1) << i)) != 0)
305 affinity.add_cpu(static_cast<int>(ga.Group) * 64 + i);
306 }
307 return affinity;
308}
309
310inline auto
311read_priority(HANDLE handle) -> std::optional<int>
312{
313 if (!handle)
314 return std::nullopt;
315 int const priority = GetThreadPriority(handle);
316 if (priority == THREAD_PRIORITY_ERROR_RETURN)
317 return std::nullopt;
318 return priority;
319}
320
321inline auto
322read_nice_value(HANDLE handle) -> expected<int, std::error_code>
323{
324 if (!handle)
325 return unexpected(std::make_error_code(std::errc::no_such_process));
326 int const priority = GetThreadPriority(handle);
327 if (priority == THREAD_PRIORITY_ERROR_RETURN)
328 return unexpected(last_win32_error());
329 if (priority >= THREAD_PRIORITY_HIGHEST)
330 return -20;
331 if (priority == THREAD_PRIORITY_ABOVE_NORMAL)
332 return -5;
333 if (priority == THREAD_PRIORITY_NORMAL)
334 return 0;
335 if (priority == THREAD_PRIORITY_BELOW_NORMAL)
336 return 5;
337 if (priority == THREAD_PRIORITY_LOWEST)
338 return 10;
339 return 19;
340}
341
342inline auto
343read_scheduling_policy(HANDLE handle) -> std::optional<native_scheduling_policy>
344{
345 if (!handle)
346 return std::nullopt;
347 return native_scheduling_policy::other;
348}
349
350inline auto
351apply_priority(native_thread_id tid, native_thread_priority priority) -> expected<void, std::error_code>
352{
353 unique_handle handle(OpenThread(THREAD_SET_INFORMATION, FALSE, tid));
354 if (!handle)
355 return unexpected(last_win32_error());
356
357 return apply_priority(handle.get(), priority);
358}
359
360inline auto
361apply_windows_thread_priority(native_thread_id tid, int priority) -> expected<void, std::error_code>
362{
363 unique_handle handle(OpenThread(THREAD_SET_INFORMATION, FALSE, tid));
364 if (!handle)
365 return unexpected(last_win32_error());
366 return apply_windows_thread_priority(handle.get(), priority);
367}
368
369inline auto
370apply_nice_value(native_thread_id tid, int nice_value) -> expected<void, std::error_code>
371{
372 unique_handle handle(OpenThread(THREAD_SET_INFORMATION, FALSE, tid));
373 if (!handle)
374 return unexpected(last_win32_error());
375 return apply_nice_value(handle.get(), nice_value);
376}
377
378inline auto
379apply_scheduling_policy(native_thread_id tid, native_scheduling_policy policy, native_thread_priority priority)
380 -> expected<void, std::error_code>
381{
382 unique_handle handle(OpenThread(THREAD_SET_INFORMATION, FALSE, tid));
383 if (!handle)
384 return unexpected(last_win32_error());
385
386 return apply_scheduling_policy(handle.get(), policy, priority);
387}
388
389inline auto
390apply_affinity(native_thread_id tid, native_thread_affinity const& affinity) -> expected<void, std::error_code>
391{
392 unique_handle handle(OpenThread(THREAD_SET_INFORMATION, FALSE, tid));
393 if (!handle)
394 return unexpected(last_win32_error());
395
396 return apply_affinity(handle.get(), affinity);
397}
398
399inline auto
400apply_name(native_thread_id tid, std::string const& name) -> expected<void, std::error_code>
401{
402 unique_handle handle(OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, tid));
403 if (!handle)
404 return unexpected(std::make_error_code(std::errc::no_such_process));
405
406 return apply_name(handle.get(), name);
407}
408
409inline auto
410read_name(native_thread_id tid) -> expected<std::string, std::error_code>
411{
412 unique_handle handle(OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, tid));
413 if (!handle)
414 return unexpected(last_win32_error());
415
416 return read_name(handle.get());
417}
418
419inline auto
420read_affinity(native_thread_id tid) -> expected<native_thread_affinity, std::error_code>
421{
422 unique_handle handle(OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid));
423 if (!handle)
424 return unexpected(last_win32_error());
425
426 return read_affinity(handle.get());
427}
428
429inline auto
430read_priority(native_thread_id tid) -> std::optional<int>
431{
432 unique_handle handle(OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid));
433 if (!handle)
434 return std::nullopt;
435
436 return read_priority(handle.get());
437}
438
439inline auto
440read_nice_value(native_thread_id tid) -> expected<int, std::error_code>
441{
442 unique_handle handle(OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid));
443 if (!handle)
444 return unexpected(last_win32_error());
445 return read_nice_value(handle.get());
446}
447
448inline auto
449read_scheduling_policy(native_thread_id tid) -> std::optional<native_scheduling_policy>
450{
451 unique_handle handle(OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid));
452 if (!handle)
453 return std::nullopt;
454
455 return read_scheduling_policy(handle.get());
456}
457
458#if defined(__MINGW32__)
459// winpthreads' pthread_t is an opaque identifier. Keep conversion to a Win32
460// handle in this adapter; it must never participate in the native_thread_id
461// overload set.
462inline auto
463win32_handle_from_pthread(pthread_t thread) -> expected<HANDLE, std::error_code>
464{
465 HANDLE const handle = pthread_gethandle(thread);
466 if (!handle)
467 return unexpected(last_win32_error());
468 return handle;
469}
470
471inline auto
472apply_scheduling_policy(pthread_t thread, native_scheduling_policy policy, native_thread_priority priority)
473 -> expected<void, std::error_code>
474{
475 auto const handle = win32_handle_from_pthread(thread);
476 if (!handle.has_value())
477 return unexpected(handle.error());
478 return apply_scheduling_policy(handle.value(), policy, priority);
479}
480
481inline auto
482apply_priority(pthread_t thread, native_thread_priority priority) -> expected<void, std::error_code>
483{
484 auto const handle = win32_handle_from_pthread(thread);
485 if (!handle.has_value())
486 return unexpected(handle.error());
487 return apply_priority(handle.value(), priority);
488}
489
490inline auto
491apply_windows_thread_priority(pthread_t thread, int priority) -> expected<void, std::error_code>
492{
493 auto const handle = win32_handle_from_pthread(thread);
494 if (!handle.has_value())
495 return unexpected(handle.error());
496 return apply_windows_thread_priority(handle.value(), priority);
497}
498
499inline auto
500apply_nice_value(pthread_t thread, int nice_value) -> expected<void, std::error_code>
501{
502 auto const handle = win32_handle_from_pthread(thread);
503 if (!handle.has_value())
504 return unexpected(handle.error());
505 return apply_nice_value(handle.value(), nice_value);
506}
507
508inline auto
509apply_affinity(pthread_t thread, native_thread_affinity const& affinity) -> expected<void, std::error_code>
510{
511 auto const handle = win32_handle_from_pthread(thread);
512 if (!handle.has_value())
513 return unexpected(handle.error());
514 return apply_affinity(handle.value(), affinity);
515}
516
517inline auto
518apply_name(pthread_t thread, std::string const& name) -> expected<void, std::error_code>
519{
520 // Validate before handing UTF-8 to winpthreads, whose API accepts char*.
521 auto const wide = utf8_to_utf16(name);
522 if (!wide.has_value())
523 return unexpected(wide.error());
524 int const result = pthread_setname_np(thread, name.c_str());
525 if (result == 0)
526 return {};
527 return unexpected(std::error_code(result, std::generic_category()));
528}
529
530inline auto
531read_name(pthread_t thread) -> expected<std::string, std::error_code>
532{
533 std::array<char, 256> name{};
534 int const result = pthread_getname_np(thread, name.data(), name.size());
535 if (result != 0)
536 return unexpected(std::error_code(result, std::generic_category()));
537 return std::string(name.data());
538}
539
540inline auto
541read_affinity(pthread_t thread) -> expected<native_thread_affinity, std::error_code>
542{
543 auto const handle = win32_handle_from_pthread(thread);
544 if (!handle.has_value())
545 return unexpected(handle.error());
546 return read_affinity(handle.value());
547}
548
549inline auto
550read_priority(pthread_t thread) -> std::optional<int>
551{
552 auto const handle = win32_handle_from_pthread(thread);
553 return handle.has_value() ? read_priority(handle.value()) : std::nullopt;
554}
555
556inline auto
557read_nice_value(pthread_t thread) -> expected<int, std::error_code>
558{
559 auto const handle = win32_handle_from_pthread(thread);
560 if (!handle.has_value())
561 return unexpected(handle.error());
562 return read_nice_value(handle.value());
563}
564
565inline auto
566read_scheduling_policy(pthread_t thread) -> std::optional<native_scheduling_policy>
567{
568 auto const handle = win32_handle_from_pthread(thread);
569 return handle.has_value() ? read_scheduling_policy(handle.value()) : std::nullopt;
570}
571#endif
auto name() const noexcept -> char const *override
Definition windows.hpp:93
auto message(int value) const -> std::string override
Definition windows.hpp:99
expected< T, std::error_code > result
Standard result type used by public APIs.
Definition result.hpp:22
void operator()(wchar_t *value) const noexcept
Definition windows.hpp:228
set_thread_description_fn set
Definition windows.hpp:130
std::error_code lookup_error
Definition windows.hpp:133
get_thread_description_fn get
Definition windows.hpp:131
HRESULT(WINAPI *)(HANDLE, PCWSTR) set_thread_description_fn
Definition windows.hpp:86
auto read_affinity(HANDLE handle) -> expected< native_thread_affinity, std::error_code >
Definition windows.hpp:285
auto resolve_thread_description_api(module_lookup_fn module_lookup=GetModuleHandleW, proc_lookup_fn proc_lookup=GetProcAddress) -> thread_description_api
Definition windows.hpp:140
auto hresult_category() -> std::error_category const &
Definition windows.hpp:108
auto error_from_hresult(HRESULT result) -> std::error_code
Definition windows.hpp:115
auto apply_nice_value(HANDLE handle, int nice_value) -> expected< void, std::error_code >
Definition windows.hpp:34
HRESULT(WINAPI *)(HANDLE, PWSTR *) get_thread_description_fn
Definition windows.hpp:87
auto read_nice_value(HANDLE handle) -> expected< int, std::error_code >
Definition windows.hpp:322
auto resolved_set_thread_description() -> expected< set_thread_description_fn, std::error_code >
Definition windows.hpp:172
auto apply_affinity(HANDLE handle, native_thread_affinity const &affinity) -> expected< void, std::error_code >
Definition windows.hpp:56
auto apply_windows_thread_priority(HANDLE handle, int priority) -> expected< void, std::error_code >
Definition windows.hpp:24
auto utf8_to_utf16(std::string const &value) -> expected< std::wstring, std::error_code >
Definition windows.hpp:208
auto resolved_get_thread_description() -> expected< get_thread_description_fn, std::error_code >
Definition windows.hpp:190
auto read_priority(HANDLE handle) -> std::optional< int >
Definition windows.hpp:311
auto last_win32_error() -> std::error_code
Definition windows.hpp:123
HMODULE(WINAPI *)(LPCWSTR) module_lookup_fn
Definition windows.hpp:136
auto read_name(HANDLE handle) -> expected< std::string, std::error_code >
Definition windows.hpp:267
FARPROC(WINAPI *)(HMODULE, LPCSTR) proc_lookup_fn
Definition windows.hpp:137
auto apply_priority(HANDLE handle, native_thread_priority priority) -> expected< void, std::error_code >
Definition windows.hpp:14
auto utf16_to_utf8(PCWSTR value) -> expected< std::string, std::error_code >
Definition windows.hpp:236
auto read_scheduling_policy(HANDLE handle) -> std::optional< native_scheduling_policy >
Definition windows.hpp:343
auto apply_scheduling_policy(HANDLE handle, native_scheduling_policy policy, native_thread_priority priority) -> expected< void, std::error_code >
Definition windows.hpp:42
auto apply_name(HANDLE handle, std::string const &name) -> expected< void, std::error_code >
Definition windows.hpp:250