diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lane.cpp | 4 | ||||
-rw-r--r-- | src/lane.hpp | 2 | ||||
-rw-r--r-- | src/lanes.cpp | 62 | ||||
-rw-r--r-- | src/lanes.lua | 42 | ||||
-rw-r--r-- | src/threading.cpp | 146 | ||||
-rw-r--r-- | src/threading.hpp | 14 | ||||
-rw-r--r-- | src/universe.hpp | 5 |
7 files changed, 175 insertions, 100 deletions
diff --git a/src/lane.cpp b/src/lane.cpp index 26ddebd..65a776e 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
@@ -1169,11 +1169,11 @@ void Lane::securizeDebugName(lua_State* const L_) | |||
1169 | 1169 | ||
1170 | // ################################################################################################# | 1170 | // ################################################################################################# |
1171 | 1171 | ||
1172 | void Lane::startThread(int const priority_) | 1172 | void Lane::startThread(lua_State* const L_, int const priority_, NativePrioFlag native_) |
1173 | { | 1173 | { |
1174 | thread = std::thread([this]() { lane_main(this); }); | 1174 | thread = std::thread([this]() { lane_main(this); }); |
1175 | if (priority_ != kThreadPrioDefault) { | 1175 | if (priority_ != kThreadPrioDefault) { |
1176 | THREAD_SET_PRIORITY(thread, priority_, U->sudo); | 1176 | THREAD_SET_PRIORITY(L_, thread, priority_, native_, U->sudo); |
1177 | } | 1177 | } |
1178 | } | 1178 | } |
1179 | 1179 | ||
diff --git a/src/lane.hpp b/src/lane.hpp index 5fe36b6..917606f 100644 --- a/src/lane.hpp +++ b/src/lane.hpp | |||
@@ -204,7 +204,7 @@ class Lane final | |||
204 | [[nodiscard]] | 204 | [[nodiscard]] |
205 | bool selfdestructRemove(); | 205 | bool selfdestructRemove(); |
206 | void securizeDebugName(lua_State* L_); | 206 | void securizeDebugName(lua_State* L_); |
207 | void startThread(int priority_); | 207 | void startThread(lua_State* L_, int priority_, NativePrioFlag native_); |
208 | void storeDebugName( std::string_view const& name_); | 208 | void storeDebugName( std::string_view const& name_); |
209 | [[nodiscard]] | 209 | [[nodiscard]] |
210 | int storeResults(lua_State* L_); | 210 | int storeResults(lua_State* L_); |
diff --git a/src/lanes.cpp b/src/lanes.cpp index d1a353b..0eaeb3e 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -137,13 +137,15 @@ LUAG_FUNC(set_singlethreaded) | |||
137 | LUAG_FUNC(set_thread_priority) | 137 | LUAG_FUNC(set_thread_priority) |
138 | { | 138 | { |
139 | lua_Integer const _prio{ luaL_checkinteger(L_, 1) }; | 139 | lua_Integer const _prio{ luaL_checkinteger(L_, 1) }; |
140 | NativePrioFlag const _native{ std::string_view{ "native" } == luaL_optstring(L_, 2, "mapped") }; | ||
140 | // public Lanes API accepts a generic range -3/+3 | 141 | // public Lanes API accepts a generic range -3/+3 |
141 | // that will be remapped into the platform-specific scheduler priority scheme | 142 | // that will be remapped into the platform-specific scheduler priority scheme |
142 | // On some platforms, -3 is equivalent to -2 and +3 to +2 | 143 | // On some platforms, -3 is equivalent to -2 and +3 to +2 |
143 | if (_prio < kThreadPrioMin || _prio > kThreadPrioMax) { | 144 | if (!_native && (_prio < kThreadPrioMin || _prio > kThreadPrioMax)) { |
144 | raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _prio); | 145 | raise_luaL_error(L_, "priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _prio); |
145 | } | 146 | } |
146 | THREAD_SET_PRIORITY(static_cast<int>(_prio), Universe::Get(L_)->sudo); | 147 | |
148 | THREAD_SET_PRIORITY(L_, static_cast<int>(_prio), _native, Universe::Get(L_)->sudo); | ||
147 | return 0; | 149 | return 0; |
148 | } | 150 | } |
149 | 151 | ||
@@ -155,7 +157,8 @@ LUAG_FUNC(set_thread_affinity) | |||
155 | if (_affinity <= 0) { | 157 | if (_affinity <= 0) { |
156 | raise_luaL_error(L_, "invalid affinity (%d)", _affinity); | 158 | raise_luaL_error(L_, "invalid affinity (%d)", _affinity); |
157 | } | 159 | } |
158 | THREAD_SET_AFFINITY(static_cast<unsigned int>(_affinity)); | 160 | |
161 | THREAD_SET_AFFINITY(L_, static_cast<unsigned int>(_affinity)); | ||
159 | return 0; | 162 | return 0; |
160 | } | 163 | } |
161 | 164 | ||
@@ -236,9 +239,26 @@ int lanes_register(lua_State* const L_) | |||
236 | 239 | ||
237 | // ################################################################################################# | 240 | // ################################################################################################# |
238 | 241 | ||
242 | LUAG_FUNC(thread_priority_range) | ||
243 | { | ||
244 | NativePrioFlag const _native{ std::string_view{ "native" } == luaL_optstring(L_, 1, "mapped") }; | ||
245 | if (_native) { | ||
246 | auto const [_prio_min, _prio_max] = THREAD_NATIVE_PRIOS(); | ||
247 | lua_pushinteger(L_, _prio_min); | ||
248 | lua_pushinteger(L_, _prio_max); | ||
249 | } else { | ||
250 | lua_pushinteger(L_, kThreadPrioMin); | ||
251 | lua_pushinteger(L_, kThreadPrioMax); | ||
252 | } | ||
253 | return 2; | ||
254 | } | ||
255 | |||
256 | // ################################################################################################# | ||
257 | |||
239 | //--- [] means can be nil | 258 | //--- [] means can be nil |
240 | // lane_ud = lane_new( function | 259 | // lane_ud = lane_new( function |
241 | // , [libs_str] | 260 | // , [libs_str] |
261 | // , [prio_is_native_bool] | ||
242 | // , [priority_int] | 262 | // , [priority_int] |
243 | // , [globals_tbl] | 263 | // , [globals_tbl] |
244 | // , [package_tbl] | 264 | // , [package_tbl] |
@@ -255,15 +275,16 @@ LUAG_FUNC(lane_new) | |||
255 | { | 275 | { |
256 | static constexpr StackIndex kFuncIdx{ 1 }; | 276 | static constexpr StackIndex kFuncIdx{ 1 }; |
257 | static constexpr StackIndex kLibsIdx{ 2 }; | 277 | static constexpr StackIndex kLibsIdx{ 2 }; |
258 | static constexpr StackIndex kPrioIdx{ 3 }; | 278 | static constexpr StackIndex kPrinIdx{ 3 }; |
259 | static constexpr StackIndex kGlobIdx{ 4 }; | 279 | static constexpr StackIndex kPrioIdx{ 4 }; |
260 | static constexpr StackIndex kPackIdx{ 5 }; | 280 | static constexpr StackIndex kGlobIdx{ 5 }; |
261 | static constexpr StackIndex kRequIdx{ 6 }; | 281 | static constexpr StackIndex kPackIdx{ 6 }; |
262 | static constexpr StackIndex kGcCbIdx{ 7 }; | 282 | static constexpr StackIndex kRequIdx{ 7 }; |
263 | static constexpr StackIndex kNameIdx{ 8 }; | 283 | static constexpr StackIndex kGcCbIdx{ 8 }; |
264 | static constexpr StackIndex kErTlIdx{ 9 }; | 284 | static constexpr StackIndex kNameIdx{ 9 }; |
265 | static constexpr StackIndex kAsCoro{ 10 }; | 285 | static constexpr StackIndex kErTlIdx{ 10 }; |
266 | static constexpr StackIndex kFixedArgsIdx{ 10 }; | 286 | static constexpr StackIndex kAsCoro{ 11 }; |
287 | static constexpr StackIndex kFixedArgsIdx{ 11 }; | ||
267 | 288 | ||
268 | int const _nargs{ lua_gettop(L_) - kFixedArgsIdx }; | 289 | int const _nargs{ lua_gettop(L_) - kFixedArgsIdx }; |
269 | LUA_ASSERT(L_, _nargs >= 0); | 290 | LUA_ASSERT(L_, _nargs >= 0); |
@@ -400,21 +421,22 @@ LUAG_FUNC(lane_new) | |||
400 | // public Lanes API accepts a generic range -3/+3 | 421 | // public Lanes API accepts a generic range -3/+3 |
401 | // that will be remapped into the platform-specific scheduler priority scheme | 422 | // that will be remapped into the platform-specific scheduler priority scheme |
402 | // On some platforms, -3 is equivalent to -2 and +3 to +2 | 423 | // On some platforms, -3 is equivalent to -2 and +3 to +2 |
403 | int const _priority{ | 424 | auto const [_priority, _native] { |
404 | std::invoke([L = L_]() { | 425 | std::invoke([L = L_]() { |
426 | NativePrioFlag const _native{ static_cast<bool>(lua_toboolean(L, kPrinIdx)) }; | ||
405 | StackIndex const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? kIdxNone : kPrioIdx }; | 427 | StackIndex const _prio_idx{ lua_isnoneornil(L, kPrioIdx) ? kIdxNone : kPrioIdx }; |
406 | if (_prio_idx == 0) { | 428 | if (_prio_idx == kIdxNone) { |
407 | return kThreadPrioDefault; | 429 | return std::make_pair(kThreadPrioDefault, _native); |
408 | } | 430 | } |
409 | int const _priority{ static_cast<int>(lua_tointeger(L, _prio_idx)) }; | 431 | int const _priority{ static_cast<int>(lua_tointeger(L, _prio_idx)) }; |
410 | if ((_priority < kThreadPrioMin || _priority > kThreadPrioMax)) { | 432 | if (!_native && (_priority < kThreadPrioMin || _priority > kThreadPrioMax)) { |
411 | raise_luaL_error(L, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority); | 433 | raise_luaL_error(L, "Priority out of range: %d..+%d (%d)", kThreadPrioMin, kThreadPrioMax, _priority); |
412 | } | 434 | } |
413 | return _priority; | 435 | return std::make_pair(_priority, _native); |
414 | }) | 436 | }) |
415 | }; | 437 | }; |
416 | 438 | ||
417 | _lane->startThread(_priority); | 439 | _lane->startThread(L_, _priority, _native); |
418 | 440 | ||
419 | STACK_GROW(_L2, _nargs + 3); | 441 | STACK_GROW(_L2, _nargs + 3); |
420 | STACK_GROW(L_, 3); | 442 | STACK_GROW(L_, 3); |
@@ -658,6 +680,7 @@ namespace { | |||
658 | { Universe::kFinally, Universe::InitializeFinalizer }, | 680 | { Universe::kFinally, Universe::InitializeFinalizer }, |
659 | { "linda", LG_linda }, | 681 | { "linda", LG_linda }, |
660 | { "nameof", LG_nameof }, | 682 | { "nameof", LG_nameof }, |
683 | { "thread_priority_range", LG_thread_priority_range }, | ||
661 | { "now_secs", LG_now_secs }, | 684 | { "now_secs", LG_now_secs }, |
662 | { "register", lanes_register }, | 685 | { "register", lanes_register }, |
663 | { "set_singlethreaded", LG_set_singlethreaded }, | 686 | { "set_singlethreaded", LG_set_singlethreaded }, |
@@ -753,9 +776,6 @@ LUAG_FUNC(configure) | |||
753 | ); // L_: settings M VERSION | 776 | ); // L_: settings M VERSION |
754 | lua_setfield(L_, -2, "version"); // L_: settings M | 777 | lua_setfield(L_, -2, "version"); // L_: settings M |
755 | 778 | ||
756 | lua_pushinteger(L_, kThreadPrioMax); // L_: settings M kThreadPrioMax | ||
757 | lua_setfield(L_, -2, "max_prio"); // L_: settings M | ||
758 | |||
759 | kCancelError.pushKey(L_); // L_: settings M kCancelError | 779 | kCancelError.pushKey(L_); // L_: settings M kCancelError |
760 | lua_setfield(L_, -2, "cancel_error"); // L_: settings M | 780 | lua_setfield(L_, -2, "cancel_error"); // L_: settings M |
761 | 781 | ||
diff --git a/src/lanes.lua b/src/lanes.lua index 3ee959c..c5b3315 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -280,6 +280,10 @@ local opt_validators = | |||
280 | local tv = type(v_) | 280 | local tv = type(v_) |
281 | return (tv == "string") and v_ or raise_option_error("name", tv, v_) | 281 | return (tv == "string") and v_ or raise_option_error("name", tv, v_) |
282 | end, | 282 | end, |
283 | native_priority = function(v_) | ||
284 | local tv = type(v_) | ||
285 | return (tv == "number") and v_ or raise_option_error("native_priority", tv, v_) | ||
286 | end, | ||
283 | package = function(v_) | 287 | package = function(v_) |
284 | local tv = type(v_) | 288 | local tv = type(v_) |
285 | return (tv == "table") and v_ or raise_option_error("package", tv, v_) | 289 | return (tv == "table") and v_ or raise_option_error("package", tv, v_) |
@@ -295,7 +299,7 @@ local opt_validators = | |||
295 | } | 299 | } |
296 | 300 | ||
297 | -- ############################################################################################# | 301 | -- ############################################################################################# |
298 | -- ##################################### lanes.gen() ########################################### | 302 | -- ################################### lanes.gen/coro() ######################################## |
299 | -- ############################################################################################# | 303 | -- ############################################################################################# |
300 | 304 | ||
301 | local process_gen_opt = function(...) | 305 | local process_gen_opt = function(...) |
@@ -367,9 +371,16 @@ local process_gen_opt = function(...) | |||
367 | opt[k] = validator(v) | 371 | opt[k] = validator(v) |
368 | end | 372 | end |
369 | end | 373 | end |
374 | |||
375 | -- special case: can't have priority and native_priority at the same time | ||
376 | if opt.priority and opt.native_priority then | ||
377 | error "priority and native_priority cannot be specified together" | ||
378 | end | ||
370 | return func, libs, opt | 379 | return func, libs, opt |
371 | end -- process_gen_opt | 380 | end -- process_gen_opt |
372 | 381 | ||
382 | -- ################################################################################################# | ||
383 | |||
373 | -- lane_h[1..n]: lane results, same as via 'lane_h:join()' | 384 | -- lane_h[1..n]: lane results, same as via 'lane_h:join()' |
374 | -- lane_h[0]: can be read to make sure a thread has finished (gives the number of available results) | 385 | -- lane_h[0]: can be read to make sure a thread has finished (gives the number of available results) |
375 | -- lane_h[negative]: error message, without propagating the error | 386 | -- lane_h[negative]: error message, without propagating the error |
@@ -408,25 +419,28 @@ end -- process_gen_opt | |||
408 | -- Calling with a function argument ('lane_func') ends the string/table | 419 | -- Calling with a function argument ('lane_func') ends the string/table |
409 | -- modifiers, and prepares a lane generator. | 420 | -- modifiers, and prepares a lane generator. |
410 | 421 | ||
411 | -- receives a sequence of strings and tables, plus a function | 422 | local make_generator = function(is_coro_, ...) |
412 | local gen = function(...) | ||
413 | local func, libs, opt = process_gen_opt(...) | 423 | local func, libs, opt = process_gen_opt(...) |
414 | local core_lane_new = assert(core.lane_new) | 424 | local core_lane_new = assert(core.lane_new) |
415 | local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level] | 425 | local prio_is_native = opt.native_priority and true or false |
426 | local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority or opt.native_priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level] | ||
416 | return function(...) | 427 | return function(...) |
417 | -- must pass functions args last else they will be truncated to the first one | 428 | -- must pass functions args last else they will be truncated to the first one |
418 | return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, error_trace_level, false, ...) | 429 | return core_lane_new(func, libs, prio_is_native, priority, globals, package, required, gc_cb, name, error_trace_level, is_coro_, ...) |
419 | end | 430 | end |
431 | end -- make_generator | ||
432 | |||
433 | -- ################################################################################################# | ||
434 | |||
435 | -- receives a sequence of strings and tables, plus a function | ||
436 | local gen = function(...) | ||
437 | return make_generator(false, ...) | ||
420 | end -- gen() | 438 | end -- gen() |
421 | 439 | ||
440 | -- ################################################################################################# | ||
441 | |||
422 | local coro = function(...) | 442 | local coro = function(...) |
423 | local func, libs, opt = process_gen_opt(...) | 443 | return make_generator(true, ...) |
424 | local core_lane_new = assert(core.lane_new) | ||
425 | local priority, globals, package, required, gc_cb, name, error_trace_level = opt.priority, opt.globals, opt.package or package, opt.required, opt.gc_cb, opt.name, error_trace_levels[opt.error_trace_level] | ||
426 | return function(...) | ||
427 | -- must pass functions args last else they will be truncated to the first one | ||
428 | return core_lane_new(func, libs, priority, globals, package, required, gc_cb, name, error_trace_level, true, ...) | ||
429 | end | ||
430 | end -- coro() | 444 | end -- coro() |
431 | 445 | ||
432 | -- ################################################################################################# | 446 | -- ################################################################################################# |
@@ -656,7 +670,8 @@ local configure_timers = function() | |||
656 | end | 670 | end |
657 | end | 671 | end |
658 | end -- timer_body() | 672 | end -- timer_body() |
659 | timer_lane = gen("lanes_core,table", { name = "LanesTimer", package = {}, priority = core.max_prio }, timer_body)() | 673 | local min_prio, max_prio = core.thread_priority_range() |
674 | timer_lane = gen("lanes_core,table", { name = "LanesTimer", package = {}, priority = max_prio }, timer_body)() | ||
660 | end -- first_time | 675 | end -- first_time |
661 | 676 | ||
662 | ----- | 677 | ----- |
@@ -876,6 +891,7 @@ local configure = function(settings_) | |||
876 | lanes.set_thread_affinity = core.set_thread_affinity | 891 | lanes.set_thread_affinity = core.set_thread_affinity |
877 | lanes.set_thread_priority = core.set_thread_priority | 892 | lanes.set_thread_priority = core.set_thread_priority |
878 | lanes.sleep = core.sleep | 893 | lanes.sleep = core.sleep |
894 | lanes.thread_priority_range = core.thread_priority_range | ||
879 | lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false | 895 | lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false |
880 | 896 | ||
881 | lanes.gen = gen | 897 | lanes.gen = gen |
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()); |
diff --git a/src/threading.hpp b/src/threading.hpp index 912c28f..07c1ab3 100644 --- a/src/threading.hpp +++ b/src/threading.hpp | |||
@@ -1,6 +1,7 @@ | |||
1 | #pragma once | 1 | #pragma once |
2 | 2 | ||
3 | #include "platform.h" | 3 | #include "platform.h" |
4 | #include "unique.hpp" | ||
4 | 5 | ||
5 | #define THREADAPI_WINDOWS 1 | 6 | #define THREADAPI_WINDOWS 1 |
6 | #define THREADAPI_PTHREAD 2 | 7 | #define THREADAPI_PTHREAD 2 |
@@ -73,8 +74,15 @@ static constexpr int kThreadPrioMax{ +3 }; | |||
73 | // ################################################################################################# | 74 | // ################################################################################################# |
74 | // ################################################################################################# | 75 | // ################################################################################################# |
75 | 76 | ||
77 | DECLARE_UNIQUE_TYPE(SudoFlag, bool); | ||
78 | DECLARE_UNIQUE_TYPE(NativePrioFlag, bool); | ||
79 | |||
80 | std::pair<int, int> THREAD_NATIVE_PRIOS(); | ||
81 | |||
76 | void THREAD_SETNAME(std::string_view const& name_); | 82 | void THREAD_SETNAME(std::string_view const& name_); |
77 | void THREAD_SET_PRIORITY(int prio_, bool sudo_); | ||
78 | void THREAD_SET_AFFINITY(unsigned int aff_); | ||
79 | 83 | ||
80 | void THREAD_SET_PRIORITY(std::thread& thread_, int prio_, bool sudo_); | 84 | void THREAD_SET_PRIORITY(lua_State* L_, int prio_, NativePrioFlag native_, SudoFlag sudo_); |
85 | |||
86 | void THREAD_SET_AFFINITY(lua_State* L_, unsigned int aff_); | ||
87 | |||
88 | void THREAD_SET_PRIORITY(lua_State* L_, std::thread& thread_, int prio_, NativePrioFlag native_, SudoFlag sudo_); | ||
diff --git a/src/universe.hpp b/src/universe.hpp index fac5f50..ab49f86 100644 --- a/src/universe.hpp +++ b/src/universe.hpp | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "cancel.hpp" | 4 | #include "cancel.hpp" |
5 | #include "keeper.hpp" | 5 | #include "keeper.hpp" |
6 | #include "lanesconf.h" | 6 | #include "lanesconf.h" |
7 | #include "threading.hpp" | ||
7 | #include "tracker.hpp" | 8 | #include "tracker.hpp" |
8 | #include "uniquekey.hpp" | 9 | #include "uniquekey.hpp" |
9 | 10 | ||
@@ -70,9 +71,9 @@ class Universe final | |||
70 | 71 | ||
71 | #ifdef PLATFORM_LINUX | 72 | #ifdef PLATFORM_LINUX |
72 | // Linux needs to check, whether it's been run as root | 73 | // Linux needs to check, whether it's been run as root |
73 | bool const sudo{ geteuid() == 0 }; | 74 | SudoFlag const sudo{ geteuid() == 0 }; |
74 | #else | 75 | #else |
75 | bool const sudo{ false }; | 76 | SudoFlag const sudo{ false }; |
76 | #endif // PLATFORM_LINUX | 77 | #endif // PLATFORM_LINUX |
77 | 78 | ||
78 | // for verbose errors | 79 | // for verbose errors |