lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


No, there's a solution, but it's do do in the C function itself, not in the Lua VM.
I already gave an example, all that is needed for that use is to write your C function as a loop around lua_yield(), allowing the code to bo restarted at the next loop...
But note that the C function will still not be "recalled", only a loop will occur, meaning that local variables and local function parameters are kept with their current value and not with the values they had on the first entry in the C function: if you need the function parameter values to be preserved, you need to write your C function using additional local variables that can be modified, and initialized from the function parameters.
Then use the return value of lua_yield() (or of other external variables accessible from the local varaibles or parameters or from gobal varaibles) to decide if you need to loop again.

Basically lua_yield() behaves like setjmp(): it also returns the value given in coroutine.resume(value), like setjmp() returns the value given in longjmp(); but the difference is that setjmp() will return one or more times from a single call; the first time it will return 0 after saving the current stack frame pointer, the next times it will return the value given in the second parameter to longjmp(), which "unwinds" the stack by restoring the stack frame pointer.

Note also that both lua_yield() and setjmp() are creating a "register barrier": this means that any variables that were cached in CPU registers before the call are no longer valid, they are considered "scratch registers", so the C compiler will need to reload these registers from local variables or parameters from the stack, or from external variables: setjmp() does NOT save these scratch registers, it just saves the stack frame pointer.

Implementing setjmp() is quite simple, what can be complex to implement in "longjmp()" because it may need to prepare the CPU in a state that allows unwinding the stack by overwriting the current frame pointer. But longjmp() does not free up any other resources: by unwinding the stack abruptly, it may leave leaked resources like dynamically allocated memory, open file handles (that won't be closed at all), locked mutexes and so on. If some of these leaked resources can cause the program state to become completely unworkable and invalid, special OS-specific behavior will be made (eventually there will also need to patch setjmp() so that it saves a bit more data to allow "safe" unwinding so that the program will not simply crash: this behavior is completely dependent of the OS-specific ABI and sometimes the C compiler ABI itself (and notably the way it performs optimizations, or allows debugging).

That's why "lua_yield" does not attempt to "emulate" what setjmp()/longjmp() do, it just use them directly as implemented by the C compiler and platform (doing anything else would make the Lua VM highly non-portable, and the same is true if you attempt top "patch" incorrectly what lua_yield() does by "playing" with the stack yourself: it will never be safe. So your code may work on one machine, with a specific OS, a specific compiler, a specific compilation mode, and for some specific CPU models or in specific contexts of use possibly depending on other workloads or depending if the OS is a native OS or a virtualized OS, but will crash on another machine or will have unexpected results and unpredictable behavior; it's exactly the same kind of problems you would encounter by trying to use dynamically allocated memory after if has been explicitly freed, even if it's not erased/overwritten immediately when freeing it). The C compiler never warranties you can safely return to a point of execution and state before the state that was explicitly preserved (here by setjmp() or by lua_yield(), which is not the point where your C function was first called) unless you've explicitly prepared your C code to allow such "restart" (i.e. by using an explicit loop in C).




Le mar. 20 août 2019 à 20:11, Gé Weijers <ge@weijers.org> a écrit :


On Tue, Aug 20, 2019 at 4:24 AM Andreas Falkenhahn <andreas@falkenhahn.com> wrote:
There's probably a misunderstanding here: Of course I'm not intending to have the C function continue where it left off. This is obviously impossible. I just want the C function to be called again. The C function code will always be run from its entry point, not from somewhere in between.


You'd be better off switching to Lua 5.3, where you can use lua_yieldk to specify a C function that's called on resume. This function will find the Lua stack as you left it, with the results yielded replaced by the arguments to resume.

I have not used lua_yieldk much, but I do use lua_callk an awful lot so Lua code can call C code that then calls Lua code, which can yield if it wants to.


--