1
votes

I'm trying to make a very simple lua wrapper that can be used to load & run multiple Lua scripts. I'm concerned because I don't see any documentation on how to properly destroy/delete loaded scripts without completely destroying the lua_State itself.

Is it possible to delete/unload loaded lua scripts? Is this unnecessary or will continuously calling luaL_dofile lead to a memory leak?

Simplified question.... If I call luaL_dofile on the same lua_State object, will this lead to a memory leak or issues or does lua handle this in the back end as it loads a new script?

Here's a demo...

lua_State* m_lua_state = luaL_newstate();
lua_gc(m_lua_state, LUA_GCSTOP, 0);
luaL_openlibs(m_lua_state);
lua_gc(m_lua_state, LUA_GCRESTART, 0);

for(int i = 0; i < 99999999; i++)
{
// Since I don't unload the previous file, does this cause a memory leak until  lua_close is called?
if (luaL_dofile(m_lua_state, file_path.c_str()) != LUA_OK)
{
   std::string error_msg = lua_tostring(m_lua_state, -1);
   std::cout << "Error: " << error_msg << std::endl;
   return false;
}
else
{
   lua_getglobal(m_lua_state, function_name.c_str());

   if (lua_isfunction(m_lua_state, -1))
   {
     int stack_size = lua_gettop(m_lua_state);

     int number_of_args = 0;
     if (lua_pcall(m_lua_state, number_of_args, 0, 0) != LUA_OK)
     {
        std::cout << "Error Calling Function In Script: " << file_path << "::" << function_name << " - " << lua_tostring(m_lua_state, -1) << std::endl;
     }
     int total_return_values = lua_gettop(m_lua_state) - stack_size;
  }
  else
  {
     std::cout << "Error Invalid Function In Script: " << file_path << "::" << function_name << std::endl;
  }
  }
}

lua_close(m_lua_state);
2
No need to "unload" the script if it just defines a function. Redefining a global function multiple times does not leak the memory. Previous function will be collected at the nearest GC event. But the function itself (when being executed) is able to eat memory. - Egor Skriptunoff

2 Answers

2
votes

If by unload you mean undo the effects of running some Lua code, then it cannot be done unless you save the state of what is precious before running the code. This can be done by sandboxing the code.

If you just want to remove a Lua table, set to nil all references to it and let the table be garbage collected automatically or manually.

0
votes

When you load a lua file you are really executing it's code. It's not like a dll or something in other languages:

int luaL_dofile (lua_State *L, const char *filename);
Loads and runs the given file. It is defined as the following macro:

If your file is something like this:

function sayHello()
  print('Hello, world!')
end

Then loading the file will create a function 'sayHello' on the global object. If you load the file again it will replace the existing function with the new one and there should be no memory leak. Anyone calling 'sayHello' will call the new function on the global object, unless they saved a reference to the original function. Code commonly does this because it is slightly faster to call a local variable instead of a global function. If another file has local sayhi = sayHello at the top for instance then any calls to 'sayhi' will call the original function.

If you are creating classes and expect the data to remain, any objects (lua tables) created before the reload will still reference the metatables for the old classes. The code for your new classes will not automatically apply to classes created before the reload.

Take an example in the LUA docs:


Account = {}

function Account:deposit (v)
  self.balance = self.balance + v
end

function Account:new (o)
  o = o or {}   -- create object if user does not provide one
  setmetatable(o, self)
  self.__index = self
  return o
end

a = Account:new{balance = 0}
a:deposit(100.00)

a is now a table with a 'balance' property of 100.00 and a metatable pointing to the Account table which has a deposit function. If you make a change to the deposit function to print the balance and reload the file, it will replace the global 'Account' table and functions, but the table 'a' will still have the original Account table as it's metatable. So calling 'a.deposit' will still not print the value.