From 074b9ae6553a439cf2d11bc1024d7616f00ba5a1 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Mon, 11 Sep 2017 16:12:23 -0400 Subject: [PATCH] add xmove constructors for function, (light )userdata, table, reference and object add additional coroutine example improve traits add tests for thread transfers update single --- docs/source/safety.rst | 13 ++-- docs/source/threading.rst | 57 +++++++++++++++ examples/coroutine_state.cpp | 46 ++++++++++++ single/sol/sol.hpp | 138 ++++++++++++++++++++++++++++------- sol/compatibility.hpp | 3 +- sol/object.hpp | 12 ++- sol/protected_function.hpp | 12 ++- sol/reference.hpp | 40 +++++++++- sol/stack_reference.hpp | 28 +++++-- sol/table_core.hpp | 7 +- sol/traits.hpp | 3 + sol/types.hpp | 11 ++- sol/unsafe_function.hpp | 7 +- sol/userdata.hpp | 14 +++- tests/test_coroutines.cpp | 47 +++++++++++- 15 files changed, 382 insertions(+), 56 deletions(-) create mode 100644 examples/coroutine_state.cpp diff --git a/docs/source/safety.rst b/docs/source/safety.rst index 83073c16..38127c3b 100644 --- a/docs/source/safety.rst +++ b/docs/source/safety.rst @@ -16,14 +16,19 @@ Note that you can obtain safety with regards to functions you bind by using the ``SOL_SAFE_FUNCTION`` triggers the following change: * All uses of ``sol::function`` and ``sol::stack_function`` will default to ``sol::protected_function`` and ``sol::stack_protected_function``, respectively, rather than ``sol::unsafe_function`` and ``sol::stack_unsafe_function``. - * **Not** turned on by default under any detectible compiler settings: *you MUST turn this one on manually* + * Will make any ``sol::state_view::script`` calls default to their safe variants if there is no supplied environment or error handler function + * **Not** turned on by default under any detectible compiler settings: *this MUST be turned on manually* ``SOL_STRINGS_ARE_NUMBERS`` triggers the following changes: * ``sol::stack::get`` and ``sol::stack::check_get`` will allow anything that Lua thinks is number-worthy to be number-worthy * This includes: integers, numbers, and strings * Does **not** include: booleans, types with ``__tostring`` enabled, and everything else * Overrides ``SOL_CHECK_ARGUMENTS`` and always applies if it is turned on - * **Not** turned on by default under any settings: *you MUST be turned on manually* + * **Not** turned on by default under any settings: *this MUST be turned on manually* + +``SOL_NO_CHECK_NUMBER_PRECISION`` triggers the following changes: + * If ``SOL_CHECK_ARGUMENTS`` is defined, turns off number precision and integer precision fitting when pushing numbers into sol2 + * **Not** turned on by default under any settings: *thus MUSt be turned on manually* ``SOL_CHECK_ARGUMENTS`` triggers the following changes: * ``sol::stack::get`` (used everywhere) defaults to using ``sol::stack::check_get`` and dereferencing the argument. It uses ``sol::type_panic`` as the handler if something goes wrong @@ -31,10 +36,8 @@ Note that you can obtain safety with regards to functions you bind by using the * ``sol::stack::call`` and its variants will, if no templated boolean is specified, check all of the arguments for a function call * If ``SOL_SAFE_USERTYPE`` is not defined, it gets defined to turn being on and the effects described above kick in * Numbers will also be checked to see if they fit within a ``lua_Number`` if there is no ``lua_Integer`` type available that can fit your signed or unsigned number. You can opt-out of this behavior with ``SOL_NO_CHECK_NUMBER_PRECISION`` + * **Not** turned on by default under any settings: *this MUST be turned on manually* -``SOL_NO_CHECK_NUMBER_PRECISION`` triggers the following changes: - * If ``SOL_CHECK_ARGUMENTS`` is defined, turns off number precision and integer precision fitting when pushing numbers into sol2 - Tests are compiled with this on to ensure everything is going as expected. Remember that if you want these features, you must explicitly turn them on all of them to be sure you are getting them. memory diff --git a/docs/source/threading.rst b/docs/source/threading.rst index 55fa4e40..f1b4f3bb 100644 --- a/docs/source/threading.rst +++ b/docs/source/threading.rst @@ -6,3 +6,60 @@ Lua has no thread safety. sol2 does not force thread safety bottlenecks anywhere Assume any access or any call on Lua affects the whole global state (because it does, in a fair bit of cases). Therefore, every call to a state should be blocked off in C++ with some kind of access control. When you start hitting the same state from multiple threads, race conditions (data or instruction) can happen. Individual Lua coroutines might be able to run on separate C++-created threads without tanking the state utterly, since each Lua coroutine has the capability to run on an independent Lua thread which has its own stack, as well as some other associated bits and pieces that won't quite interfere with the global state. To handle multithreaded environments, it is encouraged to either spawn mutliple Lua states for each thread you are working with and keep inter-state communication to synchronized serialization points. Using coroutines and Lua's threads might also buy you some concurrency and parallelism, but remember that Lua's threading technique is ultimately cooperative and requires explicit yielding and resuming (simplified as function calls for :doc:`sol::coroutine`). + + +working with multiple Lua threads +--------------------------------- + +You can mitigate some of the pressure of using coroutines and threading by using the ``lua_xmove`` constructors that sol implements. Simply keep a reference to your ``sol::state_view`` or ``sol::state`` or the target ``lua_State*`` pointer, and pass it into the constructor along with the object you want to copy. For example: + +.. codeblock:: cpp + :caption: transfer from state function + :name: state-transfer + + #define SOL_CHECK_ARGUMENTS + #include + + #include + + int main (int, char*[]) { + + sol::state lua; + lua.open_libraries(); + sol::function transferred_into; + lua["f"] = [&lua, &transferred_into](sol::object t, sol::this_state this_L) { + std::cout << "state of main : " << (void*)lua.lua_state() << std::endl; + std::cout << "state of function : " << (void*)this_L.lua_state() << std::endl; + // pass original lua_State* (or sol::state/sol::state_view) + // transfers ownership from the state of "t", + // to the "lua" sol::state + transferred_into = sol::function(lua, t); + }; + + lua.script(R"( + i = 0 + function test() + co = coroutine.create( + function() + local g = function() i = i + 1 end + f(g) + g = nil + collectgarbage() + end + ) + coroutine.resume(co) + co = nil + collectgarbage() + end + )"); + + // give it a try + lua.safe_script("test()"); + // should call 'g' from main thread, increment i by 1 + transferred_into(); + // check + int i = lua["i"]; + assert(i == 1); + + return 0; + } diff --git a/examples/coroutine_state.cpp b/examples/coroutine_state.cpp new file mode 100644 index 00000000..2f0770c8 --- /dev/null +++ b/examples/coroutine_state.cpp @@ -0,0 +1,46 @@ +#define SOL_CHECK_ARGUMENTS +#include + +#include + +int main(int, char*[]) { + + sol::state lua; + lua.open_libraries(); + sol::function transferred_into; + lua["f"] = [&lua, &transferred_into](sol::object t, sol::this_state this_L) { + std::cout << "state of main : " << (void*)lua.lua_state() << std::endl; + std::cout << "state of function : " << (void*)this_L.lua_state() << std::endl; + // pass original lua_State* (or sol::state/sol::state_view) + // transfers ownership from the state of "t", + // to the "lua" sol::state + transferred_into = sol::function(lua, t); + }; + + lua.script(R"( + i = 0 + function test() + co = coroutine.create( + function() + local g = function() i = i + 1 end + f(g) + g = nil + collectgarbage() + end + ) + coroutine.resume(co) + co = nil + collectgarbage() + end + )"); + + // give it a try + lua.safe_script("test()"); + // should call 'g' from main thread, increment i by 1 + transferred_into(); + // check + int i = lua["i"]; + assert(i == 1); + + return 0; +} diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index 728fed0c..54d80b10 100644 --- a/single/sol/sol.hpp +++ b/single/sol/sol.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2017-09-11 17:24:33.633292 UTC -// This header was generated with sol v2.18.2 (revision ac849a5) +// Generated 2017-09-11 20:10:45.557320 UTC +// This header was generated with sol v2.18.2 (revision 85c81f6) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -980,6 +980,9 @@ namespace sol { template using enable = std::enable_if_t::value, enable_t>; + template + using enable_any = std::enable_if_t::value, enable_t>; + template using disable = std::enable_if_t>::value, enable_t>; @@ -4603,10 +4606,15 @@ namespace sol { struct this_state { lua_State* L; - operator lua_State* () const { - return L; + operator lua_State* () const noexcept { + return lua_state(); } - lua_State* operator-> () const { + + lua_State* operator-> () const noexcept { + return lua_state(); + } + + lua_State* lua_state() const noexcept { return L; } }; @@ -5522,7 +5530,7 @@ namespace sol { namespace sol { class stack_reference { private: - lua_State* L = nullptr; + lua_State* luastate = nullptr; int index = 0; protected: @@ -5533,11 +5541,25 @@ namespace sol { public: stack_reference() noexcept = default; stack_reference(lua_nil_t) noexcept : stack_reference() {}; - stack_reference(lua_State* L, lua_nil_t) noexcept : L(L), index(0) {} - stack_reference(lua_State* L, int i) noexcept : L(L), index(lua_absindex(L, i)) {} - stack_reference(lua_State* L, absolute_index i) noexcept : L(L), index(i) {} - stack_reference(lua_State* L, raw_index i) noexcept : L(L), index(i) {} + stack_reference(lua_State* L, lua_nil_t) noexcept : luastate(L), index(0) {} + stack_reference(lua_State* L, int i) noexcept : stack_reference(L, absolute_index(L, i)) {} + stack_reference(lua_State* L, absolute_index i) noexcept : luastate(L), index(i) {} + stack_reference(lua_State* L, raw_index i) noexcept : luastate(L), index(i) {} stack_reference(lua_State* L, ref_index i) noexcept = delete; + stack_reference(lua_State* L, const reference& r) noexcept = delete; + stack_reference(lua_State* L, const stack_reference& r) noexcept : luastate(L) { + if (!r.valid()) { + index = 0; + return; + } + int i = r.stack_index(); + if (r.lua_state() != luastate) { + lua_pushvalue(r.lua_state(), r.index); + lua_xmove(r.lua_state(), luastate, 1); + i = absolute_index(luastate, -1); + } + index = i; + } stack_reference(stack_reference&& o) noexcept = default; stack_reference& operator=(stack_reference&&) noexcept = default; stack_reference(const stack_reference&) noexcept = default; @@ -5568,12 +5590,12 @@ namespace sol { } type get_type() const noexcept { - int result = lua_type(L, index); + int result = lua_type(lua_state(), index); return static_cast(result); } lua_State* lua_state() const noexcept { - return L; + return luastate; } bool valid() const noexcept { @@ -5722,6 +5744,41 @@ namespace sol { reference(lua_nil_t) noexcept : reference() {} reference(const stack_reference& r) noexcept : reference(r.lua_state(), r.stack_index()) {} reference(stack_reference&& r) noexcept : reference(r.lua_state(), r.stack_index()) {} + reference(lua_State* L, const reference& r) noexcept : luastate(L) { + if (r.ref == LUA_NOREF) { + ref = LUA_NOREF; + return; + } + int p = r.push(); + if (r.lua_state() != luastate) { + lua_xmove(r.lua_state(), L, p); + } + ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); + } + reference(lua_State* L, reference&& r) noexcept : luastate(L) { + if (r.ref == LUA_NOREF) { + ref = LUA_NOREF; + return; + } + if (r.lua_state() != luastate) { + int p = r.push(); + lua_xmove(r.lua_state(), L, p); + ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); + } + else { + ref = r.ref; + r.luastate = nullptr; + r.ref = LUA_NOREF; + } + } + reference(lua_State* L, const stack_reference& r) noexcept : luastate(L) { + if (!r.valid()) { + ref = LUA_NOREF; + return; + } + r.push(luastate); + ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); + } reference(lua_State* L, int index = -1) noexcept : luastate(L) { lua_pushvalue(lua_state(), index); ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); @@ -5774,7 +5831,10 @@ namespace sol { } int push(lua_State* Ls) const noexcept { - lua_rawgeti(Ls, LUA_REGISTRYINDEX, ref); + lua_rawgeti(lua_state(), LUA_REGISTRYINDEX, ref); + if (Ls != lua_state()) { + lua_xmove(lua_state(), Ls, 1); + } return 1; } @@ -11737,8 +11797,11 @@ namespace sol { basic_function& operator=(basic_function&&) = default; basic_function(const stack_reference& r) : basic_function(r.lua_state(), r.stack_index()) {} basic_function(stack_reference&& r) : basic_function(r.lua_state(), r.stack_index()) {} - template >>> = meta::enabler> - basic_function(lua_State* L, T&& r) : basic_function(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_function(lua_State* L, T&& r) : base_t(L, std::forward(r)) {} basic_function(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; @@ -12085,10 +12148,16 @@ namespace sol { > = meta::enabler> basic_protected_function(Proxy&& p, Handler&& eh) : basic_protected_function(detail::force_cast(p), std::forward(eh)) {} - template >>> = meta::enabler> + template >, + std::is_base_of> + > = meta::enabler> basic_protected_function(lua_State* L, T&& r) : basic_protected_function(L, std::forward(r), get_default_handler(L)) {} - template >>> = meta::enabler> - basic_protected_function(lua_State* L, T&& r, handler_t eh) : basic_protected_function(L, sol::ref_index(r.registry_index()), std::move(eh)) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_protected_function(lua_State* L, T&& r, handler_t eh) : base_t(L, std::forward(r)), error_handler(std::move(eh)) {} basic_protected_function(lua_State* L, int index = -1) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, int index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { @@ -12551,8 +12620,11 @@ namespace sol { basic_userdata& operator=(basic_userdata&&) = default; basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {} basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {} - template >>> = meta::enabler> - basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_userdata(lua_State* L, T&& r) : base_t(L, std::forward(r)) {} basic_userdata(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; @@ -12590,8 +12662,11 @@ namespace sol { basic_lightuserdata& operator=(basic_lightuserdata&&) = default; basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} - template >> = meta::enabler> - basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, std::forward(r)) {} basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; @@ -13025,8 +13100,17 @@ namespace sol { public: basic_object() noexcept = default; - template , basic_object>>, meta::neg>, std::is_base_of>> = meta::enabler> + template , basic_object>>, + meta::neg>, + std::is_base_of> + > = meta::enabler> basic_object(T&& r) : base_t(std::forward(r)) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_object(lua_State* L, T&& r) : base_t(L, std::forward(r)) {} basic_object(lua_nil_t r) : base_t(r) {} basic_object(const basic_object&) = default; basic_object(basic_object&&) = default; @@ -13036,6 +13120,7 @@ namespace sol { basic_object(const proxy_base& r) noexcept : basic_object(r.operator basic_object()) {} template basic_object(proxy_base&& r) noexcept : basic_object(r.operator basic_object()) {} + basic_object(lua_State* L, lua_nil_t r) noexcept : base_t(L, r) {} basic_object(lua_State* L, int index = -1) noexcept : base_t(L, index) {} basic_object(lua_State* L, absolute_index index) noexcept : base_t(L, index) {} basic_object(lua_State* L, raw_index index) noexcept : base_t(L, index) {} @@ -16518,8 +16603,11 @@ namespace sol { basic_table_core& operator=(basic_table_core&&) = default; basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} - template >>> = meta::enabler> - basic_table_core(lua_State* L, T&& r) : basic_table_core(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_table_core(lua_State* L, T&& r) : basic_table_core(L, std::forward(r)) {} basic_table_core(lua_State* L, new_table nt) : base_t(L, (lua_createtable(L, nt.sequence_hint, nt.map_hint), -1)) { if (!std::is_base_of::value) { lua_pop(L, 1); diff --git a/sol/compatibility.hpp b/sol/compatibility.hpp index 66a62357..380bb1fd 100644 --- a/sol/compatibility.hpp +++ b/sol/compatibility.hpp @@ -23,7 +23,7 @@ #define SOL_COMPATIBILITY_HPP // The various pieces of the compatibility layer -// comes from https://github.com/keplerproject/lua-compat-5.2 +// comes from https://github.com/keplerproject/lua-compat-5.3 // but has been modified in many places for use with Sol and luajit, // though the core abstractions remain the same @@ -40,7 +40,6 @@ #include "compatibility//compat-5.3.h" - #endif // SOL_NO_COMPAT #endif // SOL_COMPATIBILITY_HPP diff --git a/sol/object.hpp b/sol/object.hpp index 278ed460..1da8bd41 100644 --- a/sol/object.hpp +++ b/sol/object.hpp @@ -66,8 +66,17 @@ namespace sol { public: basic_object() noexcept = default; - template , basic_object>>, meta::neg>, std::is_base_of>> = meta::enabler> + template , basic_object>>, + meta::neg>, + std::is_base_of> + > = meta::enabler> basic_object(T&& r) : base_t(std::forward(r)) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_object(lua_State* L, T&& r) : base_t(L, std::forward(r)) {} basic_object(lua_nil_t r) : base_t(r) {} basic_object(const basic_object&) = default; basic_object(basic_object&&) = default; @@ -77,6 +86,7 @@ namespace sol { basic_object(const proxy_base& r) noexcept : basic_object(r.operator basic_object()) {} template basic_object(proxy_base&& r) noexcept : basic_object(r.operator basic_object()) {} + basic_object(lua_State* L, lua_nil_t r) noexcept : base_t(L, r) {} basic_object(lua_State* L, int index = -1) noexcept : base_t(L, index) {} basic_object(lua_State* L, absolute_index index) noexcept : base_t(L, index) {} basic_object(lua_State* L, raw_index index) noexcept : base_t(L, index) {} diff --git a/sol/protected_function.hpp b/sol/protected_function.hpp index 7f337c24..c7d6bdb9 100644 --- a/sol/protected_function.hpp +++ b/sol/protected_function.hpp @@ -214,10 +214,16 @@ namespace sol { > = meta::enabler> basic_protected_function(Proxy&& p, Handler&& eh) : basic_protected_function(detail::force_cast(p), std::forward(eh)) {} - template >>> = meta::enabler> + template >, + std::is_base_of> + > = meta::enabler> basic_protected_function(lua_State* L, T&& r) : basic_protected_function(L, std::forward(r), get_default_handler(L)) {} - template >>> = meta::enabler> - basic_protected_function(lua_State* L, T&& r, handler_t eh) : basic_protected_function(L, sol::ref_index(r.registry_index()), std::move(eh)) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_protected_function(lua_State* L, T&& r, handler_t eh) : base_t(L, std::forward(r)), error_handler(std::move(eh)) {} basic_protected_function(lua_State* L, int index = -1) : basic_protected_function(L, index, get_default_handler(L)) {} basic_protected_function(lua_State* L, int index, handler_t eh) : base_t(L, index), error_handler(std::move(eh)) { diff --git a/sol/reference.hpp b/sol/reference.hpp index 6cab3ee8..5d170624 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -138,6 +138,41 @@ namespace sol { reference(lua_nil_t) noexcept : reference() {} reference(const stack_reference& r) noexcept : reference(r.lua_state(), r.stack_index()) {} reference(stack_reference&& r) noexcept : reference(r.lua_state(), r.stack_index()) {} + reference(lua_State* L, const reference& r) noexcept : luastate(L) { + if (r.ref == LUA_NOREF) { + ref = LUA_NOREF; + return; + } + int p = r.push(); + if (r.lua_state() != luastate) { + lua_xmove(r.lua_state(), L, p); + } + ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); + } + reference(lua_State* L, reference&& r) noexcept : luastate(L) { + if (r.ref == LUA_NOREF) { + ref = LUA_NOREF; + return; + } + if (r.lua_state() != luastate) { + int p = r.push(); + lua_xmove(r.lua_state(), L, p); + ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); + } + else { + ref = r.ref; + r.luastate = nullptr; + r.ref = LUA_NOREF; + } + } + reference(lua_State* L, const stack_reference& r) noexcept : luastate(L) { + if (!r.valid()) { + ref = LUA_NOREF; + return; + } + r.push(luastate); + ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); + } reference(lua_State* L, int index = -1) noexcept : luastate(L) { lua_pushvalue(lua_state(), index); ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX); @@ -190,7 +225,10 @@ namespace sol { } int push(lua_State* Ls) const noexcept { - lua_rawgeti(Ls, LUA_REGISTRYINDEX, ref); + lua_rawgeti(lua_state(), LUA_REGISTRYINDEX, ref); + if (Ls != lua_state()) { + lua_xmove(lua_state(), Ls, 1); + } return 1; } diff --git a/sol/stack_reference.hpp b/sol/stack_reference.hpp index 97de8408..57720651 100644 --- a/sol/stack_reference.hpp +++ b/sol/stack_reference.hpp @@ -27,7 +27,7 @@ namespace sol { class stack_reference { private: - lua_State* L = nullptr; + lua_State* luastate = nullptr; int index = 0; protected: @@ -38,11 +38,25 @@ namespace sol { public: stack_reference() noexcept = default; stack_reference(lua_nil_t) noexcept : stack_reference() {}; - stack_reference(lua_State* L, lua_nil_t) noexcept : L(L), index(0) {} - stack_reference(lua_State* L, int i) noexcept : L(L), index(lua_absindex(L, i)) {} - stack_reference(lua_State* L, absolute_index i) noexcept : L(L), index(i) {} - stack_reference(lua_State* L, raw_index i) noexcept : L(L), index(i) {} + stack_reference(lua_State* L, lua_nil_t) noexcept : luastate(L), index(0) {} + stack_reference(lua_State* L, int i) noexcept : stack_reference(L, absolute_index(L, i)) {} + stack_reference(lua_State* L, absolute_index i) noexcept : luastate(L), index(i) {} + stack_reference(lua_State* L, raw_index i) noexcept : luastate(L), index(i) {} stack_reference(lua_State* L, ref_index i) noexcept = delete; + stack_reference(lua_State* L, const reference& r) noexcept = delete; + stack_reference(lua_State* L, const stack_reference& r) noexcept : luastate(L) { + if (!r.valid()) { + index = 0; + return; + } + int i = r.stack_index(); + if (r.lua_state() != luastate) { + lua_pushvalue(r.lua_state(), r.index); + lua_xmove(r.lua_state(), luastate, 1); + i = absolute_index(luastate, -1); + } + index = i; + } stack_reference(stack_reference&& o) noexcept = default; stack_reference& operator=(stack_reference&&) noexcept = default; stack_reference(const stack_reference&) noexcept = default; @@ -73,12 +87,12 @@ namespace sol { } type get_type() const noexcept { - int result = lua_type(L, index); + int result = lua_type(lua_state(), index); return static_cast(result); } lua_State* lua_state() const noexcept { - return L; + return luastate; } bool valid() const noexcept { diff --git a/sol/table_core.hpp b/sol/table_core.hpp index 5b1208ee..5db151d2 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -176,8 +176,11 @@ namespace sol { basic_table_core& operator=(basic_table_core&&) = default; basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} - template >>> = meta::enabler> - basic_table_core(lua_State* L, T&& r) : basic_table_core(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_table_core(lua_State* L, T&& r) : basic_table_core(L, std::forward(r)) {} basic_table_core(lua_State* L, new_table nt) : base_t(L, (lua_createtable(L, nt.sequence_hint, nt.map_hint), -1)) { if (!std::is_base_of::value) { lua_pop(L, 1); diff --git a/sol/traits.hpp b/sol/traits.hpp index 13d35dde..24c2fdcc 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -144,6 +144,9 @@ namespace sol { template using enable = std::enable_if_t::value, enable_t>; + template + using enable_any = std::enable_if_t::value, enable_t>; + template using disable = std::enable_if_t>::value, enable_t>; diff --git a/sol/types.hpp b/sol/types.hpp index ee85477f..822b84de 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -443,10 +443,15 @@ namespace sol { struct this_state { lua_State* L; - operator lua_State* () const { - return L; + operator lua_State* () const noexcept { + return lua_state(); } - lua_State* operator-> () const { + + lua_State* operator-> () const noexcept { + return lua_state(); + } + + lua_State* lua_state() const noexcept { return L; } }; diff --git a/sol/unsafe_function.hpp b/sol/unsafe_function.hpp index 087c78e3..69c7fc4e 100644 --- a/sol/unsafe_function.hpp +++ b/sol/unsafe_function.hpp @@ -82,8 +82,11 @@ namespace sol { basic_function& operator=(basic_function&&) = default; basic_function(const stack_reference& r) : basic_function(r.lua_state(), r.stack_index()) {} basic_function(stack_reference&& r) : basic_function(r.lua_state(), r.stack_index()) {} - template >>> = meta::enabler> - basic_function(lua_State* L, T&& r) : basic_function(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_function(lua_State* L, T&& r) : base_t(L, std::forward(r)) {} basic_function(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; diff --git a/sol/userdata.hpp b/sol/userdata.hpp index bf82f80e..1fd21c37 100644 --- a/sol/userdata.hpp +++ b/sol/userdata.hpp @@ -48,8 +48,11 @@ namespace sol { basic_userdata& operator=(basic_userdata&&) = default; basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {} basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {} - template >>> = meta::enabler> - basic_userdata(lua_State* L, T&& r) : basic_userdata(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_userdata(lua_State* L, T&& r) : base_t(L, std::forward(r)) {} basic_userdata(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; @@ -87,8 +90,11 @@ namespace sol { basic_lightuserdata& operator=(basic_lightuserdata&&) = default; basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {} - template >> = meta::enabler> - basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, sol::ref_index(r.registry_index())) {} + template >, + std::is_base_of> + > = meta::enabler> + basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, std::forward(r)) {} basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; diff --git a/tests/test_coroutines.cpp b/tests/test_coroutines.cpp index 317e76cc..11b42495 100644 --- a/tests/test_coroutines.cpp +++ b/tests/test_coroutines.cpp @@ -61,4 +61,49 @@ end } counter -= 1; REQUIRE(counter == 30); -} \ No newline at end of file +} + +TEST_CASE("coroutines/transfer", "test that things created inside of a coroutine can have their state transferred using lua_xmove constructors") { + for (std::size_t tries = 0; tries < 200; ++tries) { + sol::state lua; + lua.open_libraries(); + { + sol::function f2; + lua["f"] = [&lua, &f2](sol::object t) { + f2 = sol::function(lua, t); + }; + + lua.script(R"( +i = 0 +function INIT() + co = coroutine.create( + function() + local g = function() i = i + 1 end + f(g) + g = nil + collectgarbage() + end + ) + coroutine.resume(co) + co = nil + collectgarbage() +end +)"); + sol::function f3; + sol::function f1; + + lua.safe_script("INIT()"); + f2(); + sol::function update = lua.safe_script("return function() collectgarbage() end"); + update(); + f3 = f2; + f3(); + update(); + f1 = f2; + f1(); + update(); + int i = lua["i"]; + REQUIRE(i == 3); + } + } +}