From e94927259112011ec0e1efe70732e0b71290e327 Mon Sep 17 00:00:00 2001 From: Daniel Ostashev Date: Mon, 17 May 2021 21:53:49 +0300 Subject: [PATCH 1/2] Add test for CreateFunction memory leak --- tests/Makefile.am | 5 ++++- tests/testfunctiondoesntleak.cpp | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/testfunctiondoesntleak.cpp diff --git a/tests/Makefile.am b/tests/Makefile.am index c28ae94..9fbfd39 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,7 +3,7 @@ if COND_GCOV MAYBE_COVERAGE=-fprofile-arcs endif -TESTS = demonstration1 demonstration2 demonstration3 demonstration4 luacpp testcallabletable testchartowideconvert testcoroutineexception testcoroutineexpectedbutnot testcreate testctoluafunction testexceptioninluafunction testextractingasfunctionbutistable testfunction testfunctionerror testfunctionexpectedbutnotcallable testfunctionparams testfunctionparamsreturn testfunctionreturn testgetmetatableonclosedstate testgetsetfunction testgetsetfunction2 testgetsetinsidetable testgetsetinteger testgetsetstring testgetsettable testgetstate testgettypeofvalueat testinvalidscript testpassingfunction testreadinglightuserdatafromuserdata testregistry testresumenoscript testreturnfromnative testreturnfromscript testreturnfromyieldingfunction testreturntablefromlua testreturntablefromnative testscriptexception testsetstring testsetwstring testtable testtableexpectedbutnotable testtableforeachintegerkey testtableforeachstringkey testtypeintstringmorph testtypemorphintwstring testtypestringintmorph testuserdata testuserdataconstructor testuserdatadispose testuserdatamethod testwidetocharconvert +TESTS = demonstration1 demonstration2 demonstration3 demonstration4 luacpp testcallabletable testchartowideconvert testcoroutineexception testcoroutineexpectedbutnot testcreate testctoluafunction testexceptioninluafunction testextractingasfunctionbutistable testfunction testfunctiondoesntleak testfunctionerror testfunctionexpectedbutnotcallable testfunctionparams testfunctionparamsreturn testfunctionreturn testgetmetatableonclosedstate testgetsetfunction testgetsetfunction2 testgetsetinsidetable testgetsetinteger testgetsetstring testgetsettable testgetstate testgettypeofvalueat testinvalidscript testpassingfunction testreadinglightuserdatafromuserdata testregistry testresumenoscript testreturnfromnative testreturnfromscript testreturnfromyieldingfunction testreturntablefromlua testreturntablefromnative testscriptexception testsetstring testsetwstring testtable testtableexpectedbutnotable testtableforeachintegerkey testtableforeachstringkey testtypeintstringmorph testtypemorphintwstring testtypestringintmorph testuserdata testuserdataconstructor testuserdatadispose testuserdatamethod testwidetocharconvert check_PROGRAMS = $(TESTS) @@ -53,6 +53,9 @@ testextractingasfunctionbutistable_LDADD = $(REQUIRED_LIBS) testfunction_SOURCES = testfunction.cpp lua testfunction_LDADD = $(REQUIRED_LIBS) +testfunctiondoesntleak_SOURCES = testfunctiondoesntleak.cpp lua +testfunctiondoesntleak_LDADD = $(REQUIRED_LIBS) + testfunctionerror_SOURCES = testfunctionerror.cpp lua testfunctionerror_LDADD = $(REQUIRED_LIBS) diff --git a/tests/testfunctiondoesntleak.cpp b/tests/testfunctiondoesntleak.cpp new file mode 100644 index 0000000..7e49530 --- /dev/null +++ b/tests/testfunctiondoesntleak.cpp @@ -0,0 +1,31 @@ +// Test for CreateFunction memory leak + +#include +#include + +struct Functor { + std::shared_ptr ptr; + + void operator()() const { + // no-op + } +}; + +int main() +{ + Lua lua; + + // Create a LuaFunction from a functor that holds a shared_ptr to + // a resource. If the function created via CreateFunction gets + // collected by the GC, the weak pointer will expire after that + std::weak_ptr weak; + { + auto ptr = std::make_shared(0); + weak = ptr; + auto function = lua.CreateFunction(Functor{ptr}); + } + + lua.CollectGarbage(); + + return !weak.expired(); +} From 9bde0cd10330ce4987a534c38f0f79b9bc511fcd Mon Sep 17 00:00:00 2001 From: Daniel Ostashev Date: Mon, 17 May 2021 21:55:57 +0300 Subject: [PATCH 2/2] Fix CreateFunction memory leak --- LuaCppInterface/luacppinterface.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/LuaCppInterface/luacppinterface.h b/LuaCppInterface/luacppinterface.h index f70c39a..575d529 100644 --- a/LuaCppInterface/luacppinterface.h +++ b/LuaCppInterface/luacppinterface.h @@ -23,7 +23,10 @@ class Lua template LuaFunction internalCreateFunction( std::shared_ptr< std::function > func, lua_CFunction cfunc) { - LuaFunction** ptr = (LuaFunction**)lua_newuserdata(state.get(), sizeof(LuaFunction**)); + auto* ptr = (std::shared_ptr< std::function >*) + lua_newuserdata(state.get(), sizeof(std::shared_ptr< std::function >*)); + + ptr = new std::shared_ptr< std::function >(func); lua_newtable(state.get()); @@ -45,11 +48,8 @@ class Lua // set the metatable lua_setmetatable(state.get(), -2); - LuaFunction function = LuaFunction(state, -1, func); - // instantiate a luafunction that has a weak reference to the state - // that the lua garbage collector will collect. - *ptr = new LuaFunction(LuaFunction(LuaNoDestructor(state.get()), -1, func)); + LuaFunction function(LuaNoDestructor(state.get()), -1, func); lua_pop(state.get(), 1); return function; @@ -58,8 +58,8 @@ class Lua template static int lua_finalizer(lua_State* state) { - LuaFunction** func = (LuaFunction**)lua_touserdata(state, lua_upvalueindex(1)); - delete *func; + auto* func = (std::shared_ptr< std::function >*)lua_touserdata(state, lua_upvalueindex(1)); + delete func; return 0; };