Hacker News new | ask | show | jobs
by mikepurvis 2447 days ago
My info might be a bit out of date, but something that surprised me when I looked into it was how little control I had over the embedded lua interpreter from my outer app. I basically had to hand it control by calling lua_pcall, and either wait for a result or manage a timeout from another thread.

I expected to be able to do things like tell Lua to run for X milliseconds or Y opcodes or whatever, but it didn't seem that anything like this existed.

So perhaps my disappointment with the documentation was really just disappointment that the implementation wasn't set up to do what I wanted in the way I wanted to do it. But in the course of coming to this realization, I also found the documentation frustrating and opaque.

2 comments

Running for X opcodes isn't too difficult, but isn't immediately straightforward either. Instead of calling a function with lua_pcall, start a coroutine using lua_resume. Then, you can have a callback using lua_sethook and the LUA_MASKCOUNT option, which triggers the callback after a fixed number of opcodes. Inside the hook, you make a call to lua_yield. That forces the coroutine that was running to yield control to the outer app. The outer app can then resume the coroutine if desired.

There was a while that I wanted use this to design a programming-based video game, where simulated agents worked in real time. Most of the programming games I have seen use a fixed timeout, and kill any scripts still running after that time. What I was thinking was to instead have the script constantly running, and any slow scripts continue running on the next frame (e.g. all players get 100 opcodes per frame).

Thats clever. I used the same approach with lua_sethook to run a hook every X opcodes and check if time or memory constraints have been exceeded. But instead of yielding I used longjmp to terminate execution (basically like raising an exception and catching it a few levels up in the stack).

It's not bulletproof though and the script can still block e.g. when calling into native code.

Yeah, native code can block, and in those cases requires additional checks in that exposed native code. That runs into all the usual multithreading headaches, and depends on exactly which functions have been exposed to the lua interpreter.

The advantage to this method over longjmp is that the function call is still valid and can be resumed at any point.

Ah. Yeah applying resource limitations outside of memory usage is always a bit awkward. The only really supported way to my knowledge is to externally set a hook and trigger an error when called: https://github.com/lua/lua/blob/master/lua.c#L40-L59. I think this doesn't always work in tight loops though.

I remember patching Lua over 10 years ago for a programming game. I essentially counted VM instructions and stopped executing when reaching the limit. It survived malicious players on a hacker conference back then ;-)

The probably badly aged code is still available here: https://github.com/dividuum/infon/blob/c25eb749b17df3fd9b7a5...