aboutsummaryrefslogtreecommitdiff
path: root/src/threading.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/threading.cpp')
-rw-r--r--src/threading.cpp146
1 files changed, 88 insertions, 58 deletions
diff --git a/src/threading.cpp b/src/threading.cpp
index 483a228..efca7eb 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -49,6 +49,7 @@ THE SOFTWARE.
49 49
50#endif // __linux__ 50#endif // __linux__
51 51
52#include "compat.hpp"
52#include "threading.hpp" 53#include "threading.hpp"
53 54
54#if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC) 55#if !defined(PLATFORM_XBOX) && !defined(PLATFORM_WIN32) && !defined(PLATFORM_POCKETPC)
@@ -82,25 +83,42 @@ THE SOFTWARE.
82#pragma warning(disable : 4054) 83#pragma warning(disable : 4054)
83#endif 84#endif
84 85
86static constexpr std::string_view StripFuncName(std::string_view const& where_)
87{
88 std::string_view funcname_{ where_ };
89
90 auto _args_pos{ funcname_.find_first_of('(') };
91 funcname_ = funcname_.substr(0, _args_pos);
92 auto _name_pos{ funcname_.find_last_of(' ') };
93 funcname_.remove_prefix(_name_pos + 1);
94 return funcname_;
95}
96
85/* 97/*
86 * FAIL is for unexpected API return values - essentially programming 98 * FAIL is for unexpected API return values - essentially programming
87 * error in _this_ code. 99 * error in _this_ code.
88 */ 100 */
89#if HAVE_WIN32 101#if HAVE_WIN32
90static void FAIL(char const* funcname_, DWORD const rc_) 102
103template <typename F, typename... ARGS>
104void Win32Invoke(lua_State* const L_, std::string_view const& where_, F& f_, ARGS... args_)
91{ 105{
106 auto const _ret{ std::invoke(f_, std::forward<ARGS>(args_)...) };
107 if (!_ret) {
108 auto const _rc{ GetLastError() };
109 std::string_view const _funcname{ StripFuncName(where_) };
110
92#if defined(PLATFORM_XBOX) 111#if defined(PLATFORM_XBOX)
93 fprintf(stderr, "%s() failed! (%d)\n", funcname_, rc_); 112 luaG_pushstring(L_, "%s() failed with code %d", _funcname.data(), _rc);
94#else // PLATFORM_XBOX 113#else // PLATFORM_XBOX
95 char buf[256]; 114 char _buf[256];
96 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, rc_, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, nullptr); 115 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, _rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), _buf, 256, nullptr);
97 fprintf(stderr, "%s() failed! [GetLastError() -> %lu] '%s'", funcname_, rc_, buf); 116 luaG_pushstring(L_, "%s() failed with code %d '%s'", _funcname.data(), _rc, _buf);
98#endif // PLATFORM_XBOX 117#endif // PLATFORM_XBOX
99#ifdef _MSC_VER 118 raise_lua_error(L_);
100 __debugbreak(); // give a chance to the debugger! 119 }
101#endif // _MSC_VER
102 abort();
103} 120}
121
104#endif // HAVE_WIN32 122#endif // HAVE_WIN32
105 123
106/*---=== Threading ===---*/ 124/*---=== Threading ===---*/
@@ -121,33 +139,35 @@ static int const gs_prio_remap[] = {
121 139
122// ################################################################################################# 140// #################################################################################################
123 141
124void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 142std::pair<int, int> THREAD_NATIVE_PRIOS()
125{ 143{
126 // prio range [-3,+3] was checked by the caller 144 return std::make_pair(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL);
127 if (!SetThreadPriority(GetCurrentThread(), gs_prio_remap[prio_ + 3])) {
128 FAIL("THREAD_SET_PRIORITY", GetLastError());
129 }
130} 145}
131 146
132// ################################################################################################# 147// #################################################################################################
133 148
134void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) 149[[nodiscard]]
150void THREAD_SET_PRIORITY(lua_State* const L_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
135{ 151{
136 // prio range [-3,+3] was checked by the caller 152 // mapped prio range [-3,+3] was checked by the caller
137 // for some reason when building for mingw, native_handle() is an unsigned long long, but HANDLE is a void* 153 return Win32Invoke(L_, std::source_location::current().function_name(), SetThreadPriority, GetCurrentThread(), native_ ? prio_ : gs_prio_remap[prio_ + 3]);
138 // -> need a strong cast to make g++ happy
139 if (!SetThreadPriority(thread_.native_handle(), gs_prio_remap[prio_ + 3])) {
140 FAIL("THREAD_SET_PRIORITY", GetLastError());
141 }
142} 154}
143 155
144// ################################################################################################# 156// #################################################################################################
145 157
146void THREAD_SET_AFFINITY(unsigned int aff_) 158[[nodiscard]]
159void THREAD_SET_PRIORITY(lua_State* const L_, std::thread& thread_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
147{ 160{
148 if (!SetThreadAffinityMask(GetCurrentThread(), aff_)) { 161 // mapped prio range [-3,+3] was checked by the caller
149 FAIL("THREAD_SET_AFFINITY", GetLastError()); 162 return Win32Invoke(L_, std::source_location::current().function_name(), SetThreadPriority, thread_.native_handle(), native_ ? prio_ : gs_prio_remap[prio_ + 3]);
150 } 163}
164
165// #################################################################################################
166
167[[nodiscard]]
168void THREAD_SET_AFFINITY(lua_State* const L_, unsigned int aff_)
169{
170 return Win32Invoke(L_, std::source_location::current().function_name(), SetThreadAffinityMask, GetCurrentThread(), aff_);
151} 171}
152 172
153// ################################################################################################# 173// #################################################################################################
@@ -215,24 +235,24 @@ static int pthread_attr_setschedpolicy(pthread_attr_t* attr, int policy)
215#endif // pthread_attr_setschedpolicy() 235#endif // pthread_attr_setschedpolicy()
216#endif // defined(__MINGW32__) || defined(__MINGW64__) 236#endif // defined(__MINGW32__) || defined(__MINGW64__)
217 237
218static void _PT_FAIL(int rc, const char* name, const char* file, int line) 238template <typename F, typename... ARGS>
239void PthreadInvoke(lua_State* const L_, std::string_view const& where_, F& f_, ARGS... args_)
219{ 240{
220 const char* why = (rc == EINVAL) ? "EINVAL" 241 auto const _rc{ std::invoke(f_, std::forward<ARGS>(args_)...) };
221 : (rc == EBUSY) ? "EBUSY" 242 if (_rc) {
222 : (rc == EPERM) ? "EPERM" 243 std::string_view const _funcname{ StripFuncName(where_) };
223 : (rc == ENOMEM) ? "ENOMEM" 244
224 : (rc == ESRCH) ? "ESRCH" 245 char const* _why = (_rc == EINVAL) ? "EINVAL"
225 : (rc == ENOTSUP) ? "ENOTSUP" 246 : (_rc == EBUSY) ? "EBUSY"
226 : "<UNKNOWN>"; 247 : (_rc == EPERM) ? "EPERM"
227 fprintf(stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why); 248 : (_rc == ENOMEM) ? "ENOMEM"
228 abort(); 249 : (_rc == ESRCH) ? "ESRCH"
229} 250 : (_rc == ENOTSUP) ? "ENOTSUP"
230#define PT_CALL(call) \ 251 : "<UNKNOWN>";
231 { \ 252
232 int rc = call; \ 253 raise_luaL_error(L_, "%s() failed with code %s", _funcname.data(), _why);
233 if (rc != 0) \
234 _PT_FAIL(rc, #call, __FILE__, __LINE__); \
235 } 254 }
255}
236 256
237// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range 257// array of 7 thread priority values, hand-tuned by platform so that we offer a uniform [-3,+3] public priority range
238static int const gs_prio_remap[] = { 258static int const gs_prio_remap[] = {
@@ -357,7 +377,18 @@ static int const gs_prio_remap[] = {
357#endif // _PRIO_0 377#endif // _PRIO_0
358}; 378};
359 379
360void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 380// #################################################################################################
381
382std::pair<int, int> THREAD_NATIVE_PRIOS()
383{
384 int const _prio_min{ sched_get_priority_min(_PRIO_MODE) };
385 int const _prio_max{ sched_get_priority_max(_PRIO_MODE) };
386 return std::make_pair(_prio_min, _prio_max);
387}
388
389// #################################################################################################
390
391void THREAD_SET_PRIORITY(lua_State* const L_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
361{ 392{
362#ifdef PLATFORM_LINUX 393#ifdef PLATFORM_LINUX
363 if (!sudo_) // only root-privileged process can change priorities 394 if (!sudo_) // only root-privileged process can change priorities
@@ -366,13 +397,13 @@ void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_)
366 397
367 struct sched_param sp; 398 struct sched_param sp;
368 // prio range [-3,+3] was checked by the caller 399 // prio range [-3,+3] was checked by the caller
369 sp.sched_priority = gs_prio_remap[prio_ + 3]; 400 sp.sched_priority = native_ ? prio_ : gs_prio_remap[prio_ + 3];
370 PT_CALL(pthread_setschedparam(pthread_self(), _PRIO_MODE, &sp)); 401 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setschedparam, pthread_self(), _PRIO_MODE, &sp);
371} 402}
372 403
373// ################################################################################################# 404// #################################################################################################
374 405
375void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) 406void THREAD_SET_PRIORITY(lua_State* const L_, std::thread& thread_, int const prio_, NativePrioFlag const native_, [[maybe_unused]] SudoFlag const sudo_)
376{ 407{
377#ifdef PLATFORM_LINUX 408#ifdef PLATFORM_LINUX
378 if (!sudo_) // only root-privileged process can change priorities 409 if (!sudo_) // only root-privileged process can change priorities
@@ -381,28 +412,26 @@ void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool
381 412
382 struct sched_param sp; 413 struct sched_param sp;
383 // prio range [-3,+3] was checked by the caller 414 // prio range [-3,+3] was checked by the caller
384 sp.sched_priority = gs_prio_remap[prio_ + 3]; 415 sp.sched_priority = native_ ? prio_ : gs_prio_remap[prio_ + 3];
385 PT_CALL(pthread_setschedparam(thread_.native_handle(), _PRIO_MODE, &sp)); 416 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setschedparam, thread_.native_handle(), _PRIO_MODE, &sp);
386} 417}
387 418
388// ################################################################################################# 419// #################################################################################################
389 420
390#ifdef __PROSPERO__ 421#ifdef __PROSPERO__
391 422
392void THREAD_SET_AFFINITY(unsigned int aff_) 423void THREAD_SET_AFFINITY(lua_State* const L_, unsigned int aff_)
393{ 424{
394 scePthreadSetaffinity(scePthreadSelf(), aff_); 425 PthreadInvoke(L_, std::source_location::current().function_name(), scePthreadSetaffinity, scePthreadSelf(), aff_);
395} 426}
396 427
397#else // __PROSPERO__ 428#else // __PROSPERO__
398 429
399void THREAD_SET_AFFINITY(unsigned int aff_) 430void THREAD_SET_AFFINITY(lua_State* const L_, unsigned int aff_)
400{ 431{
401#if HAVE_WIN32 // "hybrid": Win32 API is available, and pthread too 432#if HAVE_WIN32 // "hybrid": Win32 API is available, and pthread too
402 // since pthread_setaffinity_np can be missing (for example mingw), use win32 api instead 433 // since pthread_setaffinity_np can be missing (for example mingw), use win32 api instead
403 if (!SetThreadAffinityMask(GetCurrentThread(), aff_)) { 434 Win32Invoke(L_, std::source_location::current().function_name(), SetThreadAffinityMask, GetCurrentThread(), aff_);
404 FAIL("THREAD_SET_AFFINITY", GetLastError());
405 }
406#else // pure pthread 435#else // pure pthread
407 int bit = 0; 436 int bit = 0;
408#ifdef __NetBSD__ 437#ifdef __NetBSD__
@@ -422,12 +451,13 @@ void THREAD_SET_AFFINITY(unsigned int aff_)
422 aff_ >>= 1; 451 aff_ >>= 1;
423 } 452 }
424#ifdef __ANDROID__ 453#ifdef __ANDROID__
425 PT_CALL(sched_setaffinity(pthread_self(), sizeof(cpu_set_t), &cpuset)); 454
455 PthreadInvoke(L_, std::source_location::current().function_name(), sched_setaffinity, pthread_self(), sizeof(cpu_set_t), &cpuset);
426#elif defined(__NetBSD__) 456#elif defined(__NetBSD__)
427 PT_CALL(pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset)); 457 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setaffinity_np, pthread_self(), cpuset_size(cpuset), cpuset);
428 cpuset_destroy(cpuset); 458 cpuset_destroy(cpuset);
429#else 459#else
430 PT_CALL(pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)); 460 PthreadInvoke(L_, std::source_location::current().function_name(), pthread_setaffinity_np, pthread_self(), sizeof(cpu_set_t), &cpuset);
431#endif 461#endif
432#endif // PLATFORM_MINGW 462#endif // PLATFORM_MINGW
433} 463}
@@ -447,7 +477,7 @@ void THREAD_SETNAME(std::string_view const& name_)
447 477
448void THREAD_SETNAME(std::string_view const& name_) 478void THREAD_SETNAME(std::string_view const& name_)
449{ 479{
450 // exact API to set the thread name is platform-dependant 480 // exact API to set the thread name is platform-dependent
451 // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github. 481 // if you need to fix the build, or if you know how to fill a hole, tell me (bnt.germain@gmail.com) so that I can submit the fix in github.
452#if defined PLATFORM_MINGW 482#if defined PLATFORM_MINGW
453 pthread_setname_np(pthread_self(), name_.data()); 483 pthread_setname_np(pthread_self(), name_.data());