diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | docs/index.html | 21 | ||||
-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 | ||||
-rw-r--r-- | unit_tests/embedded_tests.cpp | 8 | ||||
-rw-r--r-- | unit_tests/init_and_shutdown.cpp | 12 | ||||
-rw-r--r-- | unit_tests/lane_tests.cpp | 217 |
13 files changed, 357 insertions, 181 deletions
@@ -16,6 +16,7 @@ CHANGE 2: BGe 27-Nov-24 | |||
16 | - new function lanes.finally(). Installs a function that gets called at Lanes shutdown after attempting to terminate all lanes. | 16 | - new function lanes.finally(). Installs a function that gets called at Lanes shutdown after attempting to terminate all lanes. |
17 | If some lanes still run after the finalizer, Universe::__gc with raise an error or freeze, depending on its return value. | 17 | If some lanes still run after the finalizer, Universe::__gc with raise an error or freeze, depending on its return value. |
18 | - new function lanes.collectgarbage(), to force a full GC cycle in the keeper states. | 18 | - new function lanes.collectgarbage(), to force a full GC cycle in the keeper states. |
19 | - new function lanes.thread_priority_range(), to query the valid range of priorities. | ||
19 | - Configuration settings: | 20 | - Configuration settings: |
20 | - Boolean parameters only accept boolean values. | 21 | - Boolean parameters only accept boolean values. |
21 | - allocator provider function is called with a string hint to distinguish internal allocations, lane and keeper states. | 22 | - allocator provider function is called with a string hint to distinguish internal allocations, lane and keeper states. |
@@ -26,6 +27,8 @@ CHANGE 2: BGe 27-Nov-24 | |||
26 | - verbose_errors removed. Use lane error_trace_level instead. | 27 | - verbose_errors removed. Use lane error_trace_level instead. |
27 | - with_timers is false by default. | 28 | - with_timers is false by default. |
28 | - Non-deep full userdata are processed during module registration just like ordinary module C functions, making them valid transferable (up)values (for example: io.stdin). | 29 | - Non-deep full userdata are processed during module registration just like ordinary module C functions, making them valid transferable (up)values (for example: io.stdin). |
30 | - thread API errors cause a Lua error instead of aborting the program. | ||
31 | - thread priorities can now be set using the native range of values, if desired. | ||
29 | - Lanes: | 32 | - Lanes: |
30 | - Can no longer be "killed" by hard-stopping their thread without any resource cleanup (see lane:cancel()). | 33 | - Can no longer be "killed" by hard-stopping their thread without any resource cleanup (see lane:cancel()). |
31 | - lanes.gen() settings: | 34 | - lanes.gen() settings: |
@@ -80,7 +80,7 @@ build_DUE: | |||
80 | run_unit_tests: build_lanes build_unit_tests build_DUE | 80 | run_unit_tests: build_lanes build_unit_tests build_DUE |
81 | @echo ========================================================================================= | 81 | @echo ========================================================================================= |
82 | $(_PREFIX) $(_UNITTEST_TARGET) --list-tests | 82 | $(_PREFIX) $(_UNITTEST_TARGET) --list-tests |
83 | $(_PREFIX) $(_UNITTEST_TARGET) --rng-seed 0 -s scripted_tests.lane.tasking_cancelling | 83 | $(_PREFIX) $(_UNITTEST_TARGET) --rng-seed 0 |
84 | 84 | ||
85 | debug_unit_tests: build_lanes build_unit_tests build_DUE | 85 | debug_unit_tests: build_lanes build_unit_tests build_DUE |
86 | @echo ========================================================================================= | 86 | @echo ========================================================================================= |
diff --git a/docs/index.html b/docs/index.html index e3fbd0b..be8ad7f 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -71,7 +71,7 @@ | |||
71 | </p> | 71 | </p> |
72 | 72 | ||
73 | <p> | 73 | <p> |
74 | This document was revised on 23-Apr-25, and applies to version <tt>4.0.0</tt>. | 74 | This document was revised on 07-May-25, and applies to version <tt>4.0.0</tt>. |
75 | </p> | 75 | </p> |
76 | </font> | 76 | </font> |
77 | </center> | 77 | </center> |
@@ -106,7 +106,7 @@ | |||
106 | <li>Threads can be given priorities.</li> | 106 | <li>Threads can be given priorities.</li> |
107 | <li>Lanes are cancellable, with proper cleanup.</li> | 107 | <li>Lanes are cancellable, with proper cleanup.</li> |
108 | <li>No Lua-side application level locking - ever!</li> | 108 | <li>No Lua-side application level locking - ever!</li> |
109 | <li>Several totally independant Lanes universes may coexist in an application, one per "master" Lua state.</li> | 109 | <li>Several totally independent Lanes universes may coexist in an application, one per "master" Lua state.</li> |
110 | </ul> | 110 | </ul> |
111 | 111 | ||
112 | 112 | ||
@@ -117,7 +117,7 @@ | |||
117 | <li>Sharing full userdata between states needs special C side preparations (-> <A HREF="#deep_userdata">deep userdata</A> and -> <A HREF="#clonable_userdata">clonable userdata</A>).</li> | 117 | <li>Sharing full userdata between states needs special C side preparations (-> <A HREF="#deep_userdata">deep userdata</A> and -> <A HREF="#clonable_userdata">clonable userdata</A>).</li> |
118 | <li>Network level parallelism not included.</li> | 118 | <li>Network level parallelism not included.</li> |
119 | <li>Multi-CPU is done with OS threads, not processes. A lane is a Lua full userdata, therefore it will exist only as long as the Lua state that created it still exists. Therefore, a lane won't continue execution after the main program's termination.</li> | 119 | <li>Multi-CPU is done with OS threads, not processes. A lane is a Lua full userdata, therefore it will exist only as long as the Lua state that created it still exists. Therefore, a lane won't continue execution after the main program's termination.</li> |
120 | <li>Just like independant Lua states, Lanes universes cannot communicate together.</li> | 120 | <li>Just like independent Lua states, Lanes universes cannot communicate together.</li> |
121 | </ul> | 121 | </ul> |
122 | </p> | 122 | </p> |
123 | 123 | ||
@@ -190,6 +190,7 @@ | |||
190 | <li><tt>lanes.genlock()</tt>: obtain an atomic-like data stack</li> | 190 | <li><tt>lanes.genlock()</tt>: obtain an atomic-like data stack</li> |
191 | <li><tt>lanes.linda()</tt>: create a Linda</li> | 191 | <li><tt>lanes.linda()</tt>: create a Linda</li> |
192 | <li><tt>lanes.nameof()</tt>: find where a value exists</li> | 192 | <li><tt>lanes.nameof()</tt>: find where a value exists</li> |
193 | <li><tt>lanes.thread_priority_range()</tt>: obtain the valid range of thread priorities</li> | ||
193 | <li><tt>lanes.now_secs()</tt>: obtain the current clock value</li> | 194 | <li><tt>lanes.now_secs()</tt>: obtain the current clock value</li> |
194 | <li><tt>lanes.register()</tt>: scan modules so that functions using them can be transferred</li> | 195 | <li><tt>lanes.register()</tt>: scan modules so that functions using them can be transferred</li> |
195 | <li><tt>lanes.set_thread_priority()</tt>: change thread priority</li> | 196 | <li><tt>lanes.set_thread_priority()</tt>: change thread priority</li> |
@@ -863,12 +864,13 @@ | |||
863 | </tr> | 864 | </tr> |
864 | <tr valign=top> | 865 | <tr valign=top> |
865 | <td> | 866 | <td> |
866 | <code>.priority</code> | 867 | <code>.priority</code><br /> |
868 | <code>.native_priority</code> | ||
867 | </td> | 869 | </td> |
868 | <td>integer</td> | 870 | <td>integer</td> |
869 | <td> | 871 | <td> |
870 | The priority of lanes generated in the range -3..+3 (default is 0). | 872 | <tt>priority</tt>: The priority of lanes in the range <tt>[-3,+3]</tt> (default is 0). These values are a mapping over the actual priority range of the underlying implementation.<br /> |
871 | These values are a mapping over the actual priority range of the underlying implementation.<br /> | 873 | <tt>native_priority</tt>: The priority of lanes in a platform-dependent the range. Use <a href="#priority"><tt>lanes.thread_priority_range()</tt></a> to query said range. |
872 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode.<br /> | 874 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode.<br /> |
873 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. | 875 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. |
874 | </td> | 876 | </td> |
@@ -957,14 +959,17 @@ | |||
957 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | 959 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> |
958 | <tr> | 960 | <tr> |
959 | <td> | 961 | <td> |
960 | <pre> lanes.set_thread_priority(prio)</pre> | 962 | <pre> prio_min, prio_max = lanes.thread_priority_range(prio [,"native"])</pre> |
963 | <pre> lanes.set_thread_priority(prio [,"native"])</pre> | ||
961 | </td> | 964 | </td> |
962 | </tr> | 965 | </tr> |
963 | </table> | 966 | </table> |
964 | <p> | 967 | <p> |
965 | Besides setting a default priority in the generator <a href="#generator_settings">settings</a>, each thread can change its own priority at will. This is also true for the main Lua state. | 968 | Besides setting a default priority in the generator <a href="#generator_settings">settings</a>, each thread can change its own priority at will. This is also true for the main Lua state. |
966 | <br /> | 969 | <br /> |
967 | The priority must be in the range <tt>[-3,+3]</tt>. | 970 | <tt>lanes.thread_priority_range()</tt> returns the range of acceptable mapped values. If nothing is specified, should be <tt>[-3,3]</tt> or <tt>[0,3]</tt>, depending on the threading implementation. |
971 | <br /> | ||
972 | <tt>lanes.thread_priority_range('native')</tt> returns the range of acceptable native values. The actual values are threading implementation dependent. And some implementations can only accept some values inside that range. YMMV. | ||
968 | </p> | 973 | </p> |
969 | 974 | ||
970 | 975 | ||
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 |
diff --git a/unit_tests/embedded_tests.cpp b/unit_tests/embedded_tests.cpp index 04a8f87..6dc84a1 100644 --- a/unit_tests/embedded_tests.cpp +++ b/unit_tests/embedded_tests.cpp | |||
@@ -139,13 +139,13 @@ namespace | |||
139 | 139 | ||
140 | // ################################################################################################# | 140 | // ################################################################################################# |
141 | 141 | ||
142 | TEST_CASE("lanes.embedding.with default allocator") | 142 | TEST_CASE("lanes.embedding.with_default_allocator") |
143 | { | 143 | { |
144 | local::EmbeddedLuaState S; | 144 | local::EmbeddedLuaState S; |
145 | 145 | ||
146 | // --------------------------------------------------------------------------------------------- | 146 | // --------------------------------------------------------------------------------------------- |
147 | 147 | ||
148 | SECTION("single state") | 148 | SECTION("single_state") |
149 | { | 149 | { |
150 | // this sends data in a linda. current contents: | 150 | // this sends data in a linda. current contents: |
151 | // key: short string | 151 | // key: short string |
@@ -172,7 +172,7 @@ TEST_CASE("lanes.embedding.with default allocator") | |||
172 | 172 | ||
173 | // --------------------------------------------------------------------------------------------- | 173 | // --------------------------------------------------------------------------------------------- |
174 | 174 | ||
175 | SECTION("manual registration") | 175 | SECTION("manual_registration") |
176 | { | 176 | { |
177 | S.requireSuccess("require 'lanes'.configure{with_timers = false}"); | 177 | S.requireSuccess("require 'lanes'.configure{with_timers = false}"); |
178 | 178 | ||
@@ -204,7 +204,7 @@ TEST_CASE("lanes.embedding.with default allocator") | |||
204 | 204 | ||
205 | // this is not really a test yet, just something sitting here until it is converted properly | 205 | // this is not really a test yet, just something sitting here until it is converted properly |
206 | // currently it crashes with moonjit (but maybe I just need a more recent moonjit version) | 206 | // currently it crashes with moonjit (but maybe I just need a more recent moonjit version) |
207 | TEST_CASE("lanes.embedding.with custom allocator") | 207 | TEST_CASE("lanes.embedding.with_custom_allocator") |
208 | { | 208 | { |
209 | static constexpr auto logPrint = +[](lua_State* L) { | 209 | static constexpr auto logPrint = +[](lua_State* L) { |
210 | lua_getglobal(L, "ID"); // ID | 210 | lua_getglobal(L, "ID"); // ID |
diff --git a/unit_tests/init_and_shutdown.cpp b/unit_tests/init_and_shutdown.cpp index 515a35a..69e4f1b 100644 --- a/unit_tests/init_and_shutdown.cpp +++ b/unit_tests/init_and_shutdown.cpp | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | // ################################################################################################# | 4 | // ################################################################################################# |
5 | 5 | ||
6 | TEST_CASE("lanes.require 'lanes'") | 6 | TEST_CASE("Lua.require_lanes") |
7 | { | 7 | { |
8 | LuaState L{ LuaState::WithBaseLibs{ false }, LuaState::WithFixture{ false } }; | 8 | LuaState L{ LuaState::WithBaseLibs{ false }, LuaState::WithFixture{ false } }; |
9 | 9 | ||
@@ -418,7 +418,7 @@ TEST_CASE("lanes.configure.nb_user_keepers") | |||
418 | 418 | ||
419 | // ################################################################################################# | 419 | // ################################################################################################# |
420 | 420 | ||
421 | TEST_CASE("lanes.configure.on_state_create") | 421 | TEST_CASE("lanes.configure.on_state_create/configuration") |
422 | { | 422 | { |
423 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 423 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
424 | 424 | ||
@@ -775,7 +775,7 @@ TEST_CASE("lanes.configure.unknown_setting") | |||
775 | 775 | ||
776 | #if LUAJIT_FLAVOR() == 0 | 776 | #if LUAJIT_FLAVOR() == 0 |
777 | // TODO: this test crashes inside S.close() against LuaJIT. to be investigated | 777 | // TODO: this test crashes inside S.close() against LuaJIT. to be investigated |
778 | TEST_CASE("lanes.finally.no fixture") | 778 | TEST_CASE("lanes.finally.no_fixture") |
779 | { | 779 | { |
780 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 780 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
781 | // we need Lanes to be up. Since we run several 'scripts', we store it as a global | 781 | // we need Lanes to be up. Since we run several 'scripts', we store it as a global |
@@ -796,7 +796,7 @@ TEST_CASE("lanes.finally.no fixture") | |||
796 | 796 | ||
797 | // ################################################################################################# | 797 | // ################################################################################################# |
798 | 798 | ||
799 | TEST_CASE("lanes.finally.with fixture") | 799 | TEST_CASE("lanes.finally.with_fixture") |
800 | { | 800 | { |
801 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | 801 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; |
802 | 802 | ||
@@ -814,7 +814,7 @@ TEST_CASE("lanes.finally.with fixture") | |||
814 | 814 | ||
815 | // ################################################################################################# | 815 | // ################################################################################################# |
816 | 816 | ||
817 | TEST_CASE("lanes.finally.shutdown with an uncooperative lane") | 817 | TEST_CASE("lanes.finally.shutdown_with_an_uncooperative_lane") |
818 | { | 818 | { |
819 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | 819 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; |
820 | S.requireSuccess("lanes = require 'lanes'.configure()"); | 820 | S.requireSuccess("lanes = require 'lanes'.configure()"); |
@@ -863,7 +863,7 @@ namespace | |||
863 | 863 | ||
864 | // ################################################################################################# | 864 | // ################################################################################################# |
865 | 865 | ||
866 | TEST_CASE("lanes.on_state_create setting") | 866 | TEST_CASE("lanes.configure.on_state_create/details") |
867 | { | 867 | { |
868 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 868 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
869 | 869 | ||
diff --git a/unit_tests/lane_tests.cpp b/unit_tests/lane_tests.cpp index 1367ae5..3e5da2b 100644 --- a/unit_tests/lane_tests.cpp +++ b/unit_tests/lane_tests.cpp | |||
@@ -1,5 +1,7 @@ | |||
1 | #include "_pch.hpp" | 1 | #include "_pch.hpp" |
2 | |||
2 | #include "shared.h" | 3 | #include "shared.h" |
4 | #include "lanes/src/threading.hpp" | ||
3 | 5 | ||
4 | // ################################################################################################# | 6 | // ################################################################################################# |
5 | // ################################################################################################# | 7 | // ################################################################################################# |
@@ -33,6 +35,76 @@ TEST_CASE("lanes.nameof") | |||
33 | // ################################################################################################# | 35 | // ################################################################################################# |
34 | // ################################################################################################# | 36 | // ################################################################################################# |
35 | 37 | ||
38 | TEST_CASE("lanes.thread_priority_range") | ||
39 | { | ||
40 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
41 | S.requireSuccess("lanes = require 'lanes'.configure()"); | ||
42 | |||
43 | S.requireSuccess("a, b = lanes.thread_priority_range(); print(a, b)"); | ||
44 | S.requireSuccess("assert(type(a) == 'number' and type(b) == 'number' and b > a)"); | ||
45 | S.requireSuccess("c, d = lanes.thread_priority_range('native'); print(c, d)"); | ||
46 | S.requireSuccess("assert(type(c) == 'number' and type(d) == 'number' and d > c)"); | ||
47 | |||
48 | // can't really test the range of values from pthread as they are platform-dependent | ||
49 | if constexpr (THREADAPI == THREADAPI_WINDOWS) { | ||
50 | // windows constants THREAD_PRIORITY_IDLE and THREAD_PRIORITY_TIME_CRITICAL | ||
51 | S.requireSuccess("assert(c == -15 and d == 15)"); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | // ################################################################################################# | ||
56 | // ################################################################################################# | ||
57 | |||
58 | TEST_CASE("lanes.set_thread_priority") | ||
59 | { | ||
60 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
61 | S.requireSuccess("lanes = require 'lanes'.configure()"); | ||
62 | |||
63 | SECTION("mapped priorities") | ||
64 | { | ||
65 | std::string_view const _script{ | ||
66 | " min_prio, max_prio = lanes.thread_priority_range()" | ||
67 | " for prio = min_prio, max_prio do" | ||
68 | " lanes.set_thread_priority(prio)" | ||
69 | " end" | ||
70 | }; | ||
71 | S.requireSuccess(_script); | ||
72 | |||
73 | S.requireFailure("lanes.set_thread_priority(min_prio - 1)"); | ||
74 | S.requireFailure("lanes.set_thread_priority(max_prio + 1)"); | ||
75 | } | ||
76 | |||
77 | SECTION("native priorities") | ||
78 | { | ||
79 | S.requireSuccess("min_prio, max_prio = lanes.thread_priority_range('native')"); | ||
80 | if constexpr (THREADAPI == THREADAPI_WINDOWS) { | ||
81 | // Win32 range is -15 to 15, but only some values are accepted | ||
82 | S.requireSuccess("lanes.set_thread_priority(-15, 'native')"); // THREAD_PRIORITY_IDLE | ||
83 | S.requireFailure("lanes.set_thread_priority(-3, 'native')"); | ||
84 | S.requireSuccess("lanes.set_thread_priority(-2, 'native')"); // THREAD_PRIORITY_LOWEST | ||
85 | S.requireSuccess("lanes.set_thread_priority(-1, 'native')"); // THREAD_PRIORITY_BELOW_NORMAL | ||
86 | S.requireSuccess("lanes.set_thread_priority(0, 'native')"); // THREAD_PRIORITY_NORMAL | ||
87 | S.requireSuccess("lanes.set_thread_priority(1, 'native')"); // THREAD_PRIORITY_ABOVE_NORMAL | ||
88 | S.requireSuccess("lanes.set_thread_priority(2, 'native')"); // THREAD_PRIORITY_HIGHEST | ||
89 | S.requireFailure("lanes.set_thread_priority(3, 'native')"); | ||
90 | S.requireSuccess("lanes.set_thread_priority(-15, 'native')"); // THREAD_PRIORITY_TIME_CRITICAL | ||
91 | } else { | ||
92 | // until proven otherwise, the full set of values is supported by pthread | ||
93 | std::string_view const _script{ | ||
94 | " for prio = min_prio, max_prio do" | ||
95 | " lanes.set_thread_priority(prio, 'native')" | ||
96 | " end" | ||
97 | }; | ||
98 | S.requireSuccess(_script); | ||
99 | } | ||
100 | S.requireFailure("lanes.set_thread_priority(min_prio - 1)"); | ||
101 | S.requireFailure("lanes.set_thread_priority(max_prio + 1)"); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | // ################################################################################################# | ||
106 | // ################################################################################################# | ||
107 | |||
36 | TEST_CASE("lanes.sleep.argument validation") | 108 | TEST_CASE("lanes.sleep.argument validation") |
37 | { | 109 | { |
38 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 110 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
@@ -97,88 +169,109 @@ TEST_CASE("lanes.sleep.interactions with timers") | |||
97 | // ################################################################################################# | 169 | // ################################################################################################# |
98 | // ################################################################################################# | 170 | // ################################################################################################# |
99 | 171 | ||
100 | TEST_CASE("lanes.gen") | 172 | TEST_CASE("lanes.gen.argument_checks") |
101 | { | 173 | { |
102 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 174 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
103 | S.requireSuccess("lanes = require 'lanes'.configure()"); | 175 | S.requireSuccess("lanes = require 'lanes'.configure()"); |
104 | 176 | ||
105 | // --------------------------------------------------------------------------------------------- | 177 | // --------------------------------------------------------------------------------------------- |
106 | 178 | ||
107 | SECTION("argument checks") | 179 | // no argument is bad |
108 | { | 180 | S.requireFailure("lanes.gen()"); |
109 | // no parameter is bad | 181 | |
110 | S.requireFailure("lanes.gen()"); | 182 | // minimal generator needs a function |
111 | 183 | S.requireSuccess("lanes.gen(function() end)"); | |
112 | // minimal generator needs a function | 184 | |
113 | S.requireSuccess("lanes.gen(function() end)"); | 185 | // acceptable arguments for the generator are strings, tables, nil, followed by the function body |
114 | 186 | S.requireSuccess("lanes.gen(nil, function() end)"); | |
115 | // acceptable parameters for the generator are strings, tables, nil, followed by the function body | 187 | S.requireSuccess("lanes.gen('', function() end)"); |
116 | S.requireSuccess("lanes.gen(nil, function() end)"); | 188 | S.requireSuccess("lanes.gen({}, function() end)"); |
117 | S.requireSuccess("lanes.gen('', function() end)"); | 189 | S.requireSuccess("lanes.gen('', {}, function() end)"); |
118 | S.requireSuccess("lanes.gen({}, function() end)"); | 190 | S.requireSuccess("lanes.gen({}, '', function() end)"); |
119 | S.requireSuccess("lanes.gen('', {}, function() end)"); | 191 | S.requireSuccess("lanes.gen('', '', function() end)"); |
120 | S.requireSuccess("lanes.gen({}, '', function() end)"); | 192 | S.requireSuccess("lanes.gen({}, {}, function() end)"); |
121 | S.requireSuccess("lanes.gen('', '', function() end)"); | 193 | |
122 | S.requireSuccess("lanes.gen({}, {}, function() end)"); | 194 | // anything different should fail: booleans, numbers, any userdata |
123 | 195 | S.requireFailure("lanes.gen(false, function() end)"); | |
124 | // anything different should fail: booleans, numbers, any userdata | 196 | S.requireFailure("lanes.gen(true, function() end)"); |
125 | S.requireFailure("lanes.gen(false, function() end)"); | 197 | S.requireFailure("lanes.gen(42, function() end)"); |
126 | S.requireFailure("lanes.gen(true, function() end)"); | 198 | S.requireFailure("lanes.gen(io.stdin, function() end)"); |
127 | S.requireFailure("lanes.gen(42, function() end)"); | 199 | S.requireFailure("lanes.gen(lanes.linda(), function() end)"); |
128 | S.requireFailure("lanes.gen(io.stdin, function() end)"); | 200 | S.requireFailure("lanes.gen(lanes.linda():deep(), function() end)"); |
129 | S.requireFailure("lanes.gen(lanes.linda(), function() end)"); | 201 | |
130 | S.requireFailure("lanes.gen(lanes.linda():deep(), function() end)"); | 202 | // even if argument types are correct, the function must come last |
131 | 203 | S.requireFailure("lanes.gen(function() end, '')"); | |
132 | // even if parameter types are correct, the function must come last | 204 | |
133 | S.requireFailure("lanes.gen(function() end, '')"); | 205 | // the strings should only list "known base libraries", in any order, or "*" |
134 | 206 | // if the particular Lua flavor we build for doesn't support them, they raise an error unless postfixed by '?' | |
135 | // the strings should only list "known base libraries", in any order, or "*" | 207 | S.requireSuccess("lanes.gen('base', function() end)"); |
136 | // if the particular Lua flavor we build for doesn't support them, they raise an error unless postfixed by '?' | 208 | |
137 | S.requireSuccess("lanes.gen('base', function() end)"); | 209 | // bit, ffi, jit are LuaJIT-specific |
138 | |||
139 | // bit, ffi, jit are LuaJIT-specific | ||
140 | #if LUAJIT_FLAVOR() == 0 | 210 | #if LUAJIT_FLAVOR() == 0 |
141 | S.requireFailure("lanes.gen('bit,ffi,jit', function() end)"); | 211 | S.requireFailure("lanes.gen('bit,ffi,jit', function() end)"); |
142 | S.requireSuccess("lanes.gen('bit?,ffi?,jit?', function() end)"); | 212 | S.requireSuccess("lanes.gen('bit?,ffi?,jit?', function() end)"); |
143 | #endif // LUAJIT_FLAVOR() | 213 | #endif // LUAJIT_FLAVOR() |
144 | 214 | ||
145 | // bit32 library existed only in Lua 5.2, there is still a loader that will raise an error in Lua 5.3 | 215 | // bit32 library existed only in Lua 5.2, there is still a loader that will raise an error in Lua 5.3 |
146 | #if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 | 216 | #if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 |
147 | S.requireSuccess("lanes.gen('bit32', function() end)"); | 217 | S.requireSuccess("lanes.gen('bit32', function() end)"); |
148 | #else // LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 | 218 | #else // LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 |
149 | S.requireFailure("lanes.gen('bit32', function() end)"); | 219 | S.requireFailure("lanes.gen('bit32', function() end)"); |
150 | S.requireSuccess("lanes.gen('bit32?', function() end)"); | 220 | S.requireSuccess("lanes.gen('bit32?', function() end)"); |
151 | #endif // LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 | 221 | #endif // LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 |
152 | 222 | ||
153 | // coroutine library appeared with Lua 5.2 | 223 | // coroutine library appeared with Lua 5.2 |
154 | #if LUA_VERSION_NUM == 501 | 224 | #if LUA_VERSION_NUM == 501 |
155 | S.requireFailure("lanes.gen('coroutine', function() end)"); | 225 | S.requireFailure("lanes.gen('coroutine', function() end)"); |
156 | S.requireSuccess("lanes.gen('coroutine?', function() end)"); | 226 | S.requireSuccess("lanes.gen('coroutine?', function() end)"); |
157 | #endif // LUA_VERSION_NUM == 501 | 227 | #endif // LUA_VERSION_NUM == 501 |
158 | 228 | ||
159 | S.requireSuccess("lanes.gen('debug', function() end)"); | 229 | S.requireSuccess("lanes.gen('debug', function() end)"); |
160 | S.requireSuccess("lanes.gen('io', function() end)"); | 230 | S.requireSuccess("lanes.gen('io', function() end)"); |
161 | S.requireSuccess("lanes.gen('math', function() end)"); | 231 | S.requireSuccess("lanes.gen('math', function() end)"); |
162 | S.requireSuccess("lanes.gen('os', function() end)"); | 232 | S.requireSuccess("lanes.gen('os', function() end)"); |
163 | S.requireSuccess("lanes.gen('package', function() end)"); | 233 | S.requireSuccess("lanes.gen('package', function() end)"); |
164 | S.requireSuccess("lanes.gen('string', function() end)"); | 234 | S.requireSuccess("lanes.gen('string', function() end)"); |
165 | S.requireSuccess("lanes.gen('table', function() end)"); | 235 | S.requireSuccess("lanes.gen('table', function() end)"); |
166 | 236 | ||
167 | // utf8 library appeared with Lua 5.3 | 237 | // utf8 library appeared with Lua 5.3 |
168 | #if LUA_VERSION_NUM < 503 | 238 | #if LUA_VERSION_NUM < 503 |
169 | S.requireFailure("lanes.gen('utf8', function() end)"); | 239 | S.requireFailure("lanes.gen('utf8', function() end)"); |
170 | S.requireSuccess("lanes.gen('utf8?', function() end)"); | 240 | S.requireSuccess("lanes.gen('utf8?', function() end)"); |
171 | #endif // LUA_VERSION_NUM < 503 | 241 | #endif // LUA_VERSION_NUM < 503 |
172 | 242 | ||
173 | S.requireSuccess("lanes.gen('lanes_core', function() end)"); | 243 | S.requireSuccess("lanes.gen('lanes_core', function() end)"); |
174 | // "*" repeated or combined with anything else is forbidden | 244 | // "*" repeated or combined with anything else is forbidden |
175 | S.requireFailure("lanes.gen('*', '*', function() end)"); | 245 | S.requireFailure("lanes.gen('*', '*', function() end)"); |
176 | S.requireFailure("lanes.gen('base', '*', function() end)"); | 246 | S.requireFailure("lanes.gen('base', '*', function() end)"); |
177 | // unknown names are forbidden | 247 | // unknown names are forbidden |
178 | S.requireFailure("lanes.gen('Base', function() end)"); | 248 | S.requireFailure("lanes.gen('Base', function() end)"); |
179 | // repeating the same library more than once is forbidden | 249 | // repeating the same library more than once is forbidden |
180 | S.requireFailure("lanes.gen('base,base', function() end)"); | 250 | S.requireFailure("lanes.gen('base,base', function() end)"); |
181 | } | 251 | } |
252 | |||
253 | // ################################################################################################# | ||
254 | // ################################################################################################# | ||
255 | |||
256 | TEST_CASE("lanes.gen.priority") | ||
257 | { | ||
258 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
259 | S.requireSuccess("lanes = require 'lanes'.configure()"); | ||
260 | |||
261 | S.requireSuccess("lanes.gen({priority=1}, function() end)"); | ||
262 | // AFAICT, 1 is accepted by all pthread flavors and win32 API | ||
263 | S.requireSuccess("lanes.gen({native_priority=1}, function() end)"); | ||
264 | // shouldn't be able to provide 2 priority settings | ||
265 | S.requireFailure("lanes.gen({priority=1, native_priority=1}, function() end)"); | ||
266 | } | ||
267 | |||
268 | // ################################################################################################# | ||
269 | // ################################################################################################# | ||
270 | |||
271 | TEST_CASE("lanes.gen.thread_naming") | ||
272 | { | ||
273 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
274 | S.requireSuccess("lanes = require 'lanes'.configure()"); | ||
182 | 275 | ||
183 | // --------------------------------------------------------------------------------------------- | 276 | // --------------------------------------------------------------------------------------------- |
184 | 277 | ||