5

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...

Virus721
  • 8,061
  • 12
  • 67
  • 123

3 Answers3

1

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.

abligh
  • 24,573
  • 4
  • 47
  • 84
  • Thanks for your help. If some libraries are allocating resources, shouldn't these resources be automatically release / garbage collected when deleting all user variables ? Also could I simply manually remove all values in _ENV before running a script and do something like lua_settop( 0 ) to reset the stack ? – Virus721 Feb 16 '16 at 15:06
  • And another question that might solve my problem in another way : what if all user-created variables are defined using local, and then the scripted wrapped into a function before parsing it ? When the function would end, all it's locals would be destroyed right ? – Virus721 Feb 16 '16 at 15:08
  • @Virus721 - sure, if the program effectively resets itself (or rather does not need resetting), that would work. What we do is very similar to that, in that at a global level it's just returning a pointer to a function (IIRC - long time since I've looked at it), and that function should itself run in a manner that clears up after itself. But I'm not sure there is a way of resetting an *arbitrary* Lua program from outside the program. I don't think the 'delete all user variables' approach will fly as you may well need to know the order in which to delete them. – abligh Feb 16 '16 at 15:15
  • So you're telling me that my idea of wrapping the user's script into a function before parsing it (i.e `script = "function main() " + userCode + " end"`) would make resetting the script unnecessary as long as it properly clears any allocated resource before returning right ? – Virus721 Feb 16 '16 at 15:23
  • 1
    @Virus721 yes I think so. That is the approach we take save that the code is already wrapped. – abligh Feb 16 '16 at 15:33
  • Ok i'll go for that in the first place then/ Thanks for your help. If you add this workaround to your answer i'll accept it. – Virus721 Feb 16 '16 at 15:36
  • The thing is that, in my case, the LUA scripts will be written by very unexperienced people, so I would like to keep what they have to write in their script as small and simple as possible. Though i'll still think about that suggestion you gave me. Also i'm planning to have a single LUA state per script file with only one entry point for each state, so that's ok. – Virus721 Feb 16 '16 at 16:11
  • I wonder if it's possible to use this approach with coroutines, and if it's going to be possible to "reset" the script when a coroutine is in progress? I can't seem to be able to cleanup the lua state in a way that I can run a coroutine without it holding the state of the previous execution... – Lake Jan 12 '22 at 08:46
  • Actually I expanded my comment into a question here: https://stackoverflow.com/questions/70679013/sol3-is-it-possible-to-reset-the-lua-state-so-that-any-coroutine-function-can-b – Lake Jan 12 '22 at 09:26
1

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.

ILikeLua19
  • 11
  • 1
0

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.

EinsteinK
  • 745
  • 3
  • 8
  • Is the "user environement" seprate from the lua_State ? – Virus721 Feb 16 '16 at 14:45
  • There is a global environment that contains stuff as 'table' and 'print' etc. Depending on the implementation, that's also the user environment. If a user does ```a = 123``` then there's a field _a_ in the global environment. You can separate it C-side (I dont know how) or Lua side, with a simple getfenv/setfenv (or _ENV), creating a new environment with a metafield __index pointing to the old one. – EinsteinK Feb 16 '16 at 15:54