diff options
Diffstat (limited to 'src/threading.cpp')
-rw-r--r-- | src/threading.cpp | 146 |
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 | ||
86 | static 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 |
90 | static void FAIL(char const* funcname_, DWORD const rc_) | 102 | |
103 | template <typename F, typename... ARGS> | ||
104 | void 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 | ||
124 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) | 142 | std::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 | ||
134 | void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) | 149 | [[nodiscard]] |
150 | void 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 | ||
146 | void THREAD_SET_AFFINITY(unsigned int aff_) | 158 | [[nodiscard]] |
159 | void 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]] | ||
168 | void 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 | ||
218 | static void _PT_FAIL(int rc, const char* name, const char* file, int line) | 238 | template <typename F, typename... ARGS> |
239 | void 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 |
238 | static int const gs_prio_remap[] = { | 258 | static 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 | ||
360 | void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) | 380 | // ################################################################################################# |
381 | |||
382 | std::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 | |||
391 | void 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 | ||
375 | void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, [[maybe_unused]] bool sudo_) | 406 | void 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 | ||
392 | void THREAD_SET_AFFINITY(unsigned int aff_) | 423 | void 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 | ||
399 | void THREAD_SET_AFFINITY(unsigned int aff_) | 430 | void 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 | ||
448 | void THREAD_SETNAME(std::string_view const& name_) | 478 | void 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()); |