5
votes

I have an application that runs Lua scripts. Each Lua script is likely to run several times. Some scripts may even run every time a key is pressed.

I would like these scripts to be "reset" between each run. I.e if a user sets a variable Foo, then Foo should not exist in the script the next time it runs until the user defines it again.

The problem is that, if I want to have such a behaviour, I need to create a new lua_State everytime, then open the libraries into it every time, and then parse the script file everytime, which seems very unoptimized.

Loading the libraries may be a rather lightweight operation (I assume), but parsing the scripts is probably not.

Is there a way to reset the state of a Lua script (i.e clear user-code-defined variables) without creating a new lua_State and reparsing the whole Lua script file ? I would just like the script files to be parsed once on application startup since they are not modifed at run-time.

Thank you. :)

EDIT : I found this topic but it is not detailed about to do that : http://lua-users.org/lists/lua-l/2006-01/msg00493.html

EDIT : lua_setfenv seems to be related to that. I'll dig a bit more.

EDIT : It seems like there is no more lua_setfenv as of LUA 5.2. Since i'm using 5.3 I would have to set the environement (i.e a hidden table nammed _ENV where variables are stored) in order to do that, and thus reload everything, which is what I want not to do...

3
You can do it Lua side with coroutines perhaps?warspyking

3 Answers

1
votes

Last time I looked into this the answer was no, unfortunately.

You also need to remember that the Lua may call libraries, that may open files, malloc() memory etc., and that any 'reset' needs to deal with closing those files, freeing that memory, etc.

As an alternative to 'resetting' the Lua state, you could simply organise your code so that it did not need a reset; this obviously requires the Lua code to be written in a specific way. One way to do that would be to insist your Lua code was entirely (or almost entirely) contained within function(s) and call one or more functions for each action. The code outside the functions might (e.g.) return a Lua table consisting of references to call for particular entry points; this would only be called once. The function(s), when called, would clear up after itself, including clearing up any library allocated items, open files, etc. Global variables should be avoided (unless constant). We successfully use this approach to ensure Lua is only parsed once, the entry points determined once, but relative small functions can be called very rapidly with little overhead.

In the comments you suggested you could lexically wrap the Lua code in a function block. I think this is less flexible than the above approach, and has the following disadvantages:

  • You lose the opportunity to do a 'one time init' (e.g. for instance reading a fixed constant from disk)

  • You risk unpredictable things if a user inserts (e.g.) a mismatched end ... function B() in their code

  • You constrain yourself to one entry point per Lua state.

It does mean that the Lua code must be written in a different way (in essence the Lua coder is supplying the code in the form required). One possible way around this is to use a fixed framework to do this and require in the code to be called as libraries. I have not tried that approach.

1
votes

If you want to make sure that the lua_State is the same upon each script invocation, you can also try the following approach which worked for my case:

  • Pass a custom memory allocator to lua_newstate, which allocates memory from a memory pool
  • after parsing the scripts, and before the first "run", create a backup of the memory pool in some other memory location
  • after each "run" restore the memory pool from the backup, in the original location

Please note that this only takes care of resources completely contained in the Lua data structures, and not of "external" resources referenced in any way from Lua or Lua libraries (e.g. file descriptors, userdata, ...)

Therefore in my case I also constrained the abilities of the script writer to some sandbox only providing operations considered safe for the above usage, by means of replacing the globals table.

Using this approach the reset basically boils down to some memcpy invocations after each "run", and therefore takes as long as is needed to copy the memory used by the Lua structures for your script.

0
votes

Can't you clear the lua_State? Remove all threads and the manually set globals. You might need to have the user environment separate from the global environment.