the snippet below roughly demonstrates what we have in the real code in our project. ComplexStructC is a structure bound to lua composed of other structure(s) (ComplexStructB, ComplexStructA) also bound to Lua. We return ComplexStructC as a result of a calculation from a function called by lua (named GetComplexStructC in this snippet).
The lua script however saves only part of the result and discards the rest. In this sample it stores csc.b to bb and clears csc.
Also notice the garbage collector calls in between script invocations.
What seems to happen is that lua deallocates the memory of csc (which is of type ComplexStructC), because there are no more valid references left pointing to it from Lua, but that also takes down with it every part of csc, because it is in the same chunk of memory, so bb basically becomes a dangling reference.
Does this sound plausible ? Are there any solutions/workarounds to this ?
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
struct ComplexStructA
{
int a = 10;
};
struct ComplexStructB
{
ComplexStructA a;
int b = 20;
};
struct ComplexStructC
{
ComplexStructB b;
int c = 30;
};
static ComplexStructC GetComplexStructC(const int a, const int b, const int c)
{
ComplexStructC csc;
csc.b.a.a = a;
csc.b.b = b;
csc.c = c;
return csc;
}
static ComplexStructC GetComplexStructC()
{
return GetComplexStructC(10, 20, 30);
}
static const char* CallStatusToString(sol::call_status s)
{
#define CASE(lbl) case sol::call_status::lbl: return #lbl
switch (s)
{
CASE(ok);
CASE(yielded);
CASE(runtime);
CASE(memory);
CASE(handler);
CASE(gc);
CASE(syntax);
CASE(file);
default: return "";
}
#undef CASE
}
int main()
{
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::coroutine, sol::lib::string, sol::lib::os, sol::lib::math, sol::lib::table, sol::lib::io, sol::lib::debug);
lua.new_usertype<ComplexStructA>("ComplexStructA", sol::call_constructor, sol::constructors<ComplexStructA()>(), "a", &ComplexStructA::a);
lua.new_usertype<ComplexStructB>("ComplexStructB", sol::call_constructor, sol::constructors<ComplexStructB()>(), "a", &ComplexStructB::a, "b", &ComplexStructB::b);
lua.new_usertype<ComplexStructC>("ComplexStructC", sol::call_constructor, sol::constructors<ComplexStructC()>(), "b", &ComplexStructC::b, "c", &ComplexStructC::c);
lua.set("GetComplexStructC", sol::overload(static_cast<ComplexStructC(*)(int, int, int)>(&GetComplexStructC), static_cast<ComplexStructC(*)()>(&GetComplexStructC)));
for (int i = 0; i < 20; ++i)
{
lua["testNum"] = i + 1;
{
auto res = lua.safe_script(R"(
print('==================== ' .. tostring(testNum) .. ' ====================')
csc = GetComplexStructC()
print('csc.b.a.a', csc.b.a.a)
print('csc.b.b', csc.b.b)
print('csc.c', csc.c)
bb = csc.b
bb.b = 100
print('csc.b.a.a', csc.b.a.a)
print('csc.b.b', csc.b.b)
print('csc.c', csc.c)
print('bb.a.a', bb.a.a)
print('bb.b', bb.b)
csc = nil
)", sol::script_pass_on_error);
if (!res.valid())
std::cerr << "status='" << CallStatusToString(res.status()) << "', message='" << res.get<sol::error>().what() << "'";
}
lua.collect_garbage();
lua.collect_garbage(); // as reading in manuals, this function must be called twice to destroy all garbage.
{
auto res = lua.safe_script(R"(
-- here bb points to undefined memory
print('bb.a.a', bb.a.a)
print('bb.b', bb.b)
)", sol::script_pass_on_error);
if (!res.valid())
std::cerr << "status='" << CallStatusToString(res.status()) << "', message='" << res.get<sol::error>().what() << "'";
}
}
return 0;
}
Hello,
the snippet below roughly demonstrates what we have in the real code in our project.
ComplexStructC
is a structure bound to lua composed of other structure(s) (ComplexStructB
,ComplexStructA
) also bound to Lua. We returnComplexStructC
as a result of a calculation from a function called by lua (namedGetComplexStructC
in this snippet).The lua script however saves only part of the result and discards the rest. In this sample it stores
csc.b
tobb
and clearscsc
.Also notice the garbage collector calls in between script invocations.
What seems to happen is that lua deallocates the memory of
csc
(which is of typeComplexStructC
), because there are no more valid references left pointing to it from Lua, but that also takes down with it every part ofcsc
, because it is in the same chunk of memory, sobb
basically becomes a dangling reference.Does this sound plausible ? Are there any solutions/workarounds to this ?
This sample can also be viewed and tested here: https://godbolt.org/z/Eb6zGaKjr