diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2025-07-04 14:54:36 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2025-07-04 14:54:36 +0200 |
| commit | 7bddb06461d8f80030b3b86175ec737dbb16ac3b (patch) | |
| tree | 031238469090658f7f20535137edb2620bc77182 | |
| parent | 042055968ab0c48faec607889814e38c50c09efa (diff) | |
| download | lanes-7bddb06461d8f80030b3b86175ec737dbb16ac3b.tar.gz lanes-7bddb06461d8f80030b3b86175ec737dbb16ac3b.tar.bz2 lanes-7bddb06461d8f80030b3b86175ec737dbb16ac3b.zip | |
Added Lua 5.5 support
* some unit tests fail/segfault/freeze, but that could be because Lua 5.5 is still in beta yet
| -rw-r--r-- | CHANGES | 1 | ||||
| -rw-r--r-- | docs/index.html | 6 | ||||
| -rw-r--r-- | lanes-4.0.0-0.rockspec | 2 | ||||
| -rw-r--r-- | src/allocator.hpp | 2 | ||||
| -rw-r--r-- | src/compat.cpp | 38 | ||||
| -rw-r--r-- | src/compat.hpp | 71 | ||||
| -rw-r--r-- | src/universe.cpp | 2 | ||||
| -rw-r--r-- | unit_tests/embedded_tests.cpp | 2 | ||||
| -rw-r--r-- | unit_tests/linda_tests.cpp | 2 | ||||
| -rw-r--r-- | unit_tests/scripts/coro/yielding_in_non_coro_errors.lua | 3 |
10 files changed, 97 insertions, 32 deletions
| @@ -6,6 +6,7 @@ CHANGE 2: BGe 05-Jun-25 | |||
| 6 | - Almost all platform-specific code is gone (only a small bit for thread priority and affinity remains). | 6 | - Almost all platform-specific code is gone (only a small bit for thread priority and affinity remains). |
| 7 | - Decoda support inactive by default. | 7 | - Decoda support inactive by default. |
| 8 | - Deep userdata interface fully revamped to C++20 too. | 8 | - Deep userdata interface fully revamped to C++20 too. |
| 9 | - Supports Lua 5.5 | ||
| 9 | * Lanes API changes | 10 | * Lanes API changes |
| 10 | - Version is now 4.0.0 | 11 | - Version is now 4.0.0 |
| 11 | - Lanes module: | 12 | - Lanes module: |
diff --git a/docs/index.html b/docs/index.html index f6201cc..96151fe 100644 --- a/docs/index.html +++ b/docs/index.html | |||
| @@ -67,7 +67,7 @@ | |||
| 67 | <br /> | 67 | <br /> |
| 68 | <i>Copyright © 2007-25 Asko Kauppi, Benoit Germain. All rights reserved.</i> | 68 | <i>Copyright © 2007-25 Asko Kauppi, Benoit Germain. All rights reserved.</i> |
| 69 | <br /> | 69 | <br /> |
| 70 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, 5.3 and 5.4. | 70 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, 5.3, 5.4 and 5.5. |
| 71 | </p> | 71 | </p> |
| 72 | 72 | ||
| 73 | <p> | 73 | <p> |
| @@ -89,7 +89,7 @@ | |||
| 89 | Lanes is included into your software by the regular <tt>require "lanes"</tt> method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should work seamlessly together with the multiple lanes. | 89 | Lanes is included into your software by the regular <tt>require "lanes"</tt> method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should work seamlessly together with the multiple lanes. |
| 90 | </p> | 90 | </p> |
| 91 | <p> | 91 | <p> |
| 92 | Lanes should build and run identically with either Lua 5.1 to Lua 5.4, as well as LuaJIT. | 92 | Lanes should build and run identically with either Lua 5.1 to Lua 5.5, as well as LuaJIT. |
| 93 | </p> | 93 | </p> |
| 94 | <p> | 94 | <p> |
| 95 | See <A HREF="comparison.html">comparison</A> of Lua Lanes with other Lua multithreading solutions. | 95 | See <A HREF="comparison.html">comparison</A> of Lua Lanes with other Lua multithreading solutions. |
| @@ -893,7 +893,7 @@ | |||
| 893 | The name is stored inside the Lua state registry so that it is available for error reporting. Changing <tt>decoda_name</tt> doesn't affect this hidden name or the OS thread name reported by MSVC.<br /> | 893 | The name is stored inside the Lua state registry so that it is available for error reporting. Changing <tt>decoda_name</tt> doesn't affect this hidden name or the OS thread name reported by MSVC.<br /> |
| 894 | When Lanes is initialized by the first <a href="#initialization"><tt>lanes.configure()</tt></a> call, <tt>"main"</tt> is stored in the registry in the same fashion (but <tt>decoda_name</tt> and the OS thread name are left unchanged).<br /> | 894 | When Lanes is initialized by the first <a href="#initialization"><tt>lanes.configure()</tt></a> call, <tt>"main"</tt> is stored in the registry in the same fashion (but <tt>decoda_name</tt> and the OS thread name are left unchanged).<br /> |
| 895 | The lane also has a method <tt>lane:get_threadname()</tt> that gives access to that name from the caller side (returns <tt>"<unnamed>"</tt> if unset).<br /> | 895 | The lane also has a method <tt>lane:get_threadname()</tt> that gives access to that name from the caller side (returns <tt>"<unnamed>"</tt> if unset).<br /> |
| 896 | With Lua 5.4, Lanes have a <tt>__close</tt> metamethod, meaning they can be declared to-be-closed. <tt>__close</tt> calls <tt>lane:join(nil)</tt>. | 896 | With Lua 5.4+, Lanes have a <tt>__close</tt> metamethod, meaning they can be declared to-be-closed. <tt>__close</tt> calls <tt>lane:join(nil)</tt>. |
| 897 | </p> | 897 | </p> |
| 898 | 898 | ||
| 899 | <p> | 899 | <p> |
diff --git a/lanes-4.0.0-0.rockspec b/lanes-4.0.0-0.rockspec index e3c8709..f4de050 100644 --- a/lanes-4.0.0-0.rockspec +++ b/lanes-4.0.0-0.rockspec | |||
| @@ -36,7 +36,7 @@ supported_platforms= { "win32", | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | dependencies= { | 38 | dependencies= { |
| 39 | "lua >= 5.1", -- builds with either 5.1/LuaJIT, 5.2, 5.3 and 5.4 | 39 | "lua >= 5.1", -- builds with either 5.1/LuaJIT, 5.2, 5.3, 5.4 and 5.5 |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | build = { | 42 | build = { |
diff --git a/src/allocator.hpp b/src/allocator.hpp index ce65a4b..b578f12 100644 --- a/src/allocator.hpp +++ b/src/allocator.hpp | |||
| @@ -66,7 +66,7 @@ namespace lanes { | |||
| 66 | [[nodiscard]] | 66 | [[nodiscard]] |
| 67 | lua_State* newState() const | 67 | lua_State* newState() const |
| 68 | { | 68 | { |
| 69 | return lua_newstate(allocF, allocUD); | 69 | return luaW_newstate(allocF, allocUD, luaL_makeseed(nullptr)); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | [[nodiscard]] | 72 | [[nodiscard]] |
diff --git a/src/compat.cpp b/src/compat.cpp index 08f22ca..c833480 100644 --- a/src/compat.cpp +++ b/src/compat.cpp | |||
| @@ -149,3 +149,41 @@ int lua_setiuservalue(lua_State* const L_, StackIndex const idx_, UserValueIndex | |||
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | #endif // LUA_VERSION_NUM | 151 | #endif // LUA_VERSION_NUM |
| 152 | |||
| 153 | // ################################################################################################# | ||
| 154 | |||
| 155 | #if LUA_VERSION_NUM < 505 | ||
| 156 | |||
| 157 | #include <time.h> | ||
| 158 | |||
| 159 | /* Size for the buffer, in bytes */ | ||
| 160 | #define BUFSEEDB (sizeof(void*) + sizeof(time_t)) | ||
| 161 | |||
| 162 | /* Size for the buffer in int's, rounded up */ | ||
| 163 | #define BUFSEED ((BUFSEEDB + sizeof(int) - 1) / sizeof(int)) | ||
| 164 | |||
| 165 | /* | ||
| 166 | ** Copy the contents of variable 'v' into the buffer pointed by 'b'. | ||
| 167 | ** (The '&b[0]' disguises 'b' to fix an absurd warning from clang.) | ||
| 168 | */ | ||
| 169 | #define addbuff(b, v) (memcpy(&b[0], &(v), sizeof(v)), b += sizeof(v)) | ||
| 170 | |||
| 171 | // Copied from Lua 5.5 lauxlib.c | ||
| 172 | unsigned int luaL_makeseed(lua_State*) | ||
| 173 | { | ||
| 174 | unsigned int buff[BUFSEED]; | ||
| 175 | unsigned int res; | ||
| 176 | unsigned int i; | ||
| 177 | time_t t = time(nullptr); | ||
| 178 | char* b = (char*) buff; | ||
| 179 | addbuff(b, b); /* local variable's address */ | ||
| 180 | addbuff(b, t); /* time */ | ||
| 181 | /* fill (rare but possible) remain of the buffer with zeros */ | ||
| 182 | memset(b, 0, sizeof(buff) - BUFSEEDB); | ||
| 183 | res = buff[0]; | ||
| 184 | for (i = 1; i < BUFSEED; i++) | ||
| 185 | res ^= (res >> 3) + (res << 7) + buff[i]; | ||
| 186 | return res; | ||
| 187 | } | ||
| 188 | |||
| 189 | #endif // LUA_VERSION_NUM < 505 | ||
diff --git a/src/compat.hpp b/src/compat.hpp index 99c468c..49029df 100644 --- a/src/compat.hpp +++ b/src/compat.hpp | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | #endif // LUA_OK | 21 | #endif // LUA_OK |
| 22 | 22 | ||
| 23 | #ifndef LUA_ERRGCMM | 23 | #ifndef LUA_ERRGCMM |
| 24 | #define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1 and Lua 5.4, we don't care about the actual value | 24 | #define LUA_ERRGCMM 666 // doesn't exist in Lua 5.1 and Lua 5.4/5.5, we don't care about the actual value |
| 25 | #endif // LUA_ERRGCMM | 25 | #endif // LUA_ERRGCMM |
| 26 | 26 | ||
| 27 | 27 | ||
| @@ -29,7 +29,7 @@ | |||
| 29 | #define LUA_LOADED_TABLE "_LOADED" // doesn't exist before Lua 5.3 | 29 | #define LUA_LOADED_TABLE "_LOADED" // doesn't exist before Lua 5.3 |
| 30 | #endif // LUA_LOADED_TABLE | 30 | #endif // LUA_LOADED_TABLE |
| 31 | 31 | ||
| 32 | // code is now preferring Lua 5.4 API | 32 | // code is now preferring Lua 5.5 API |
| 33 | 33 | ||
| 34 | // ################################################################################################# | 34 | // ################################################################################################# |
| 35 | 35 | ||
| @@ -76,18 +76,6 @@ int luaL_getsubtable(lua_State* L_, StackIndex idx_, char const* fname_); | |||
| 76 | 76 | ||
| 77 | // ################################################################################################# | 77 | // ################################################################################################# |
| 78 | 78 | ||
| 79 | // wrap Lua 5.3 calls under Lua 5.1 API when it is simpler that way | ||
| 80 | #if LUA_VERSION_NUM == 503 | ||
| 81 | |||
| 82 | inline int luaL_optint(lua_State* L_, int n_, lua_Integer d_) | ||
| 83 | { | ||
| 84 | return static_cast<int>(luaL_optinteger(L_, n_, d_)); | ||
| 85 | } | ||
| 86 | |||
| 87 | #endif // LUA_VERSION_NUM == 503 | ||
| 88 | |||
| 89 | // ################################################################################################# | ||
| 90 | |||
| 91 | #if LUA_VERSION_NUM < 504 | 79 | #if LUA_VERSION_NUM < 504 |
| 92 | 80 | ||
| 93 | void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_); | 81 | void* lua_newuserdatauv(lua_State* L_, size_t sz_, UserValueCount nuvalue_); |
| @@ -100,15 +88,11 @@ int lua_setiuservalue(lua_State* L_, StackIndex idx_, UserValueIndex n_); | |||
| 100 | 88 | ||
| 101 | // ################################################################################################# | 89 | // ################################################################################################# |
| 102 | 90 | ||
| 103 | // wrap Lua 5.4 calls under Lua 5.1 API when it is simpler that way | 91 | #if LUA_VERSION_NUM < 505 |
| 104 | #if LUA_VERSION_NUM == 504 | ||
| 105 | 92 | ||
| 106 | inline int luaL_optint(lua_State* L_, StackIndex n_, lua_Integer d_) | 93 | unsigned int luaL_makeseed(lua_State*); |
| 107 | { | ||
| 108 | return static_cast<int>(luaL_optinteger(L_, n_, d_)); | ||
| 109 | } | ||
| 110 | 94 | ||
| 111 | #endif // LUA_VERSION_NUM == 504 | 95 | #endif // LUA_VERSION_NUM < 505 |
| 112 | 96 | ||
| 113 | // ################################################################################################# | 97 | // ################################################################################################# |
| 114 | 98 | ||
| @@ -284,6 +268,47 @@ LuaType luaW_getmodule(lua_State* L_, std::string_view const& name_); | |||
| 284 | 268 | ||
| 285 | // ################################################################################################# | 269 | // ################################################################################################# |
| 286 | 270 | ||
| 271 | template <typename LUA_NEWSTATE> | ||
| 272 | concept RequiresOldLuaNewState = requires(LUA_NEWSTATE f_) | ||
| 273 | { | ||
| 274 | { | ||
| 275 | f_(nullptr, 0) | ||
| 276 | } -> std::same_as<lua_State*>; | ||
| 277 | }; | ||
| 278 | |||
| 279 | template <RequiresOldLuaNewState LUA_NEWSTATE> | ||
| 280 | static inline lua_State* WrapLuaNewState(LUA_NEWSTATE const lua_newstate_, lua_Alloc const allocf_, void* const ud_, [[maybe_unused]] unsigned int const seed_) | ||
| 281 | { | ||
| 282 | // until Lua 5.5, lua_newstate has only 2 parameters | ||
| 283 | return lua_newstate_(allocf_, ud_); | ||
| 284 | } | ||
| 285 | |||
| 286 | // ------------------------------------------------------------------------------------------------- | ||
| 287 | |||
| 288 | template <typename LUA_NEWSTATE> | ||
| 289 | concept RequiresNewLuaNewState = requires(LUA_NEWSTATE f_) | ||
| 290 | { | ||
| 291 | { | ||
| 292 | f_(nullptr, nullptr, 0) | ||
| 293 | } -> std::same_as<lua_State*>; | ||
| 294 | }; | ||
| 295 | |||
| 296 | template <RequiresNewLuaNewState LUA_NEWSTATE> | ||
| 297 | static inline lua_State* WrapLuaNewState(LUA_NEWSTATE const lua_newstate_, lua_Alloc const allocf_, void* const ud_, unsigned int const seed_) | ||
| 298 | { | ||
| 299 | // starting with Lua 5.5, lua_newstate has 3 parameters | ||
| 300 | return lua_newstate_(allocf_, ud_, seed_); | ||
| 301 | } | ||
| 302 | |||
| 303 | // ------------------------------------------------------------------------------------------------- | ||
| 304 | |||
| 305 | static inline lua_State* luaW_newstate(lua_Alloc const allocf_, void* const ud_, unsigned int const seed_) | ||
| 306 | { | ||
| 307 | return WrapLuaNewState(lua_newstate, allocf_, ud_, seed_); | ||
| 308 | } | ||
| 309 | |||
| 310 | // ################################################################################################# | ||
| 311 | |||
| 287 | template<typename ENUM> | 312 | template<typename ENUM> |
| 288 | requires std::is_enum_v<ENUM> | 313 | requires std::is_enum_v<ENUM> |
| 289 | [[nodiscard]] | 314 | [[nodiscard]] |
| @@ -364,7 +389,7 @@ template <typename LUA_RAWGET> | |||
| 364 | concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; }; | 389 | concept RequiresOldLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<void>; }; |
| 365 | 390 | ||
| 366 | template <RequiresOldLuaRawget LUA_RAWGET> | 391 | template <RequiresOldLuaRawget LUA_RAWGET> |
| 367 | static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_) | 392 | static inline LuaType WrapLuaRawget(LUA_RAWGET const lua_rawget_, lua_State* const L_, StackIndex const idx_) |
| 368 | { | 393 | { |
| 369 | // until Lua 5.3, lua_rawget -> void | 394 | // until Lua 5.3, lua_rawget -> void |
| 370 | lua_rawget_(L_, idx_); | 395 | lua_rawget_(L_, idx_); |
| @@ -377,7 +402,7 @@ template <typename LUA_RAWGET> | |||
| 377 | concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; }; | 402 | concept RequiresNewLuaRawget = requires(LUA_RAWGET f_) { { f_(nullptr, 0) } -> std::same_as<int>; }; |
| 378 | 403 | ||
| 379 | template <RequiresNewLuaRawget LUA_RAWGET> | 404 | template <RequiresNewLuaRawget LUA_RAWGET> |
| 380 | static inline LuaType WrapLuaRawget(LUA_RAWGET lua_rawget_, lua_State* const L_, StackIndex const idx_) | 405 | static inline LuaType WrapLuaRawget(LUA_RAWGET const lua_rawget_, lua_State* const L_, StackIndex const idx_) |
| 381 | { | 406 | { |
| 382 | // starting with Lua 5.3, lua_rawget -> int (the type of the extracted value) | 407 | // starting with Lua 5.3, lua_rawget -> int (the type of the extracted value) |
| 383 | return static_cast<LuaType>(lua_rawget_(L_, idx_)); | 408 | return static_cast<LuaType>(lua_rawget_(L_, idx_)); |
diff --git a/src/universe.cpp b/src/universe.cpp index 044c841..4db036b 100644 --- a/src/universe.cpp +++ b/src/universe.cpp | |||
| @@ -447,7 +447,7 @@ int Universe::UniverseGC(lua_State* const L_) | |||
| 447 | if (!lua_isnil(L_, -1)) { | 447 | if (!lua_isnil(L_, -1)) { |
| 448 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool | 448 | lua_pushboolean(L_, _allLanesTerminated); // L_: U finalizer bool |
| 449 | // no protection. Lua rules for errors in finalizers apply normally: | 449 | // no protection. Lua rules for errors in finalizers apply normally: |
| 450 | // Lua 5.4: error is propagated in the warn system | 450 | // Lua 5.4+: error is propagated in the warn system |
| 451 | // older: error is swallowed | 451 | // older: error is swallowed |
| 452 | lua_call(L_, 1, 1); // L_: U msg? | 452 | lua_call(L_, 1, 1); // L_: U msg? |
| 453 | // phew, no error in finalizer, since we reached that point | 453 | // phew, no error in finalizer, since we reached that point |
diff --git a/unit_tests/embedded_tests.cpp b/unit_tests/embedded_tests.cpp index 58d6a8a..388548d 100644 --- a/unit_tests/embedded_tests.cpp +++ b/unit_tests/embedded_tests.cpp | |||
| @@ -220,7 +220,7 @@ TEST_CASE("lanes.embedding.with_custom_allocator") | |||
| 220 | 220 | ||
| 221 | static constexpr auto launch_lane = +[](lua_CFunction on_state_create_, int id_, int n_) { | 221 | static constexpr auto launch_lane = +[](lua_CFunction on_state_create_, int id_, int n_) { |
| 222 | char script[500]; | 222 | char script[500]; |
| 223 | lua_State* L = lua_newstate(local::allocf, nullptr); | 223 | lua_State* L = luaW_newstate(local::allocf, nullptr, luaL_makeseed(nullptr)); |
| 224 | // _G.ID = id_ | 224 | // _G.ID = id_ |
| 225 | luaL_openlibs(L); | 225 | luaL_openlibs(L); |
| 226 | luaL_dostring(L, "lanes = require 'lanes'"); | 226 | luaL_dostring(L, "lanes = require 'lanes'"); |
diff --git a/unit_tests/linda_tests.cpp b/unit_tests/linda_tests.cpp index d956ae2..a36723a 100644 --- a/unit_tests/linda_tests.cpp +++ b/unit_tests/linda_tests.cpp | |||
| @@ -35,7 +35,7 @@ TEST_CASE("linda.single_keeper.creation/close_handler") | |||
| 35 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | 35 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; |
| 36 | S.requireSuccess("lanes = require 'lanes'"); | 36 | S.requireSuccess("lanes = require 'lanes'"); |
| 37 | 37 | ||
| 38 | if constexpr (LUA_VERSION_NUM == 504) { | 38 | if constexpr (LUA_VERSION_NUM >= 504) { |
| 39 | // a function is acceptable as a __close handler | 39 | // a function is acceptable as a __close handler |
| 40 | S.requireSuccess("local l <close> = lanes.linda{close_handler = function() end}"); | 40 | S.requireSuccess("local l <close> = lanes.linda{close_handler = function() end}"); |
| 41 | // a callable table too (a callable full userdata as well, but I have none here) | 41 | // a callable table too (a callable full userdata as well, but I have none here) |
diff --git a/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua b/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua index 88d5fe0..fc0c072 100644 --- a/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua +++ b/unit_tests/scripts/coro/yielding_in_non_coro_errors.lua | |||
| @@ -20,7 +20,8 @@ local msgs = { | |||
| 20 | ["Lua 5.1"] = jit and "attempt to yield across C-call boundary" or "attempt to yield across metamethod/C-call boundary", | 20 | ["Lua 5.1"] = jit and "attempt to yield across C-call boundary" or "attempt to yield across metamethod/C-call boundary", |
| 21 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", | 21 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", |
| 22 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", | 22 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", |
| 23 | ["Lua 5.4"] = "attempt to yield from outside a coroutine" | 23 | ["Lua 5.4"] = "attempt to yield from outside a coroutine", |
| 24 | ["Lua 5.5"] = "attempt to yield from outside a coroutine" | ||
| 24 | } | 25 | } |
| 25 | local expected_msg = msgs[_VERSION] | 26 | local expected_msg = msgs[_VERSION] |
| 26 | PRINT("expected_msg = " .. expected_msg) | 27 | PRINT("expected_msg = " .. expected_msg) |
