I'm trying to call lua_yield inside a debug hook, and get this error in my output. I'm wanting to yield after a certain number of instructions have been processed and was hoping this was the way to do it.
I'm writing this using some Python ctypes bindings.
yielding
b'test.lua:1: attempt to yield across C-call boundary'
I assumed this should work since I'm using LuaJIT and it has a fully resumable VM.
#lua_Hook
def l_dbg_count(L: lua_State_p, ar: ctypes.POINTER(lua_Debug)):
if ar.contents.event == EventCode.HookCount:
print("yielding")
lua_yield(L, 0)
#main method
def main():
...
lua_sethook(L, l_dbg_count, DebugEventMask.Count, 1)
luaL_loadfile(L, b"test.lua")
ret = lua_pcall(L, 0, 0, 0)
while True:
if ret != LuaError.Ok and ret != LuaError.Yield:
print(lua_tostring(L, -1))
break
elif ret == LuaError.Yield:
print("resuming")
ret = lua_resume(L, None, 0)
lua_close(L)
I first must push a new thread using lua_newthread, then calling luaL_loadfile and instead of lua_pcall, calling lua_resume.
I rewrote this in C to check if there was possible stack unwinding issues from Lua to Python.
void l_dbg_count(lua_State *L, lua_Debug *ar) {
if(ar->event == LUA_HOOKCOUNT) {
printf("yielding\n");
lua_yield(L, 0);
}
}
...
int main(int argc, char **argv) {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, l_dbg_count, LUA_MASKCOUNT, 5);
lua_State *L_t = lua_newthread(L);
luaL_loadfile(L_t, "test.lua");
int ret = lua_resume(L_t, 0);
while(true) {
if(ret != 0 && ret != LUA_YIELD) {
fprintf(stderr, "%s", lua_tostring(L_t, -1));
break;
} else if(ret == LUA_YIELD) {
printf("resuming\n");
ret = lua_resume(L_t, 0);
} else {
break;
}
}
lua_close(L);
return EXIT_SUCCESS;
}
This however does break the coroutine library from working it seems, so currently looking into a possible fix for that.
I just figured out my app sometimes crashes when lua_getglobal(L, "name"); is called many times.
I tried placing lua_pop(L, 1); after lua_getglobal(L, "name"); and it no longer crashes.
Can calling lua_getglobal(L, "name"); many times cause memory leaks?
Does anybody have a clue why my app crashes without lua_pop(L, 1);?
Lua has a (implementation-defined) limited stack size. If you keep pushing onto the stack without ever popping, the stack will be full at some point and trying to push more will crash your program.
If you check the documentation for lua_getglobal you'll find “Pushes onto the stack the value of the global”. You are responsible for removing it, either calling a function which implicitly pops it (e.g. lua_pcall) or by explictly popping it with lua_pop.
#include <iostream>
#include <lua.hpp>
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if (luaL_dostring(L, "name = 'Zack Lee'") != 0) {
std::cerr << "lua:" << lua_tostring(L, -1) << '\n';
lua_close(L);
return 1;
}
for (int i = 0; i < 200; ++i) {
lua_getglobal(L, "name");
std::cout << i << ' ' << lua_tostring(L, -1) << '\n';
//lua_pop(L, 1);
}
lua_close(L);
}
$ clang++ -Wall -Wextra -Wpedantic -I/usr/include/lua5.2/ test.cpp -llua5.2
$ ./a.out
0 Zack Lee
<...snip...>
41 Zack Lee
Segmentation fault
If I uncomment the lua_pop line, it works as expected.
You can also resize the Lua stack, but this has already been answered elsewhere.
Given a lua file like
-- foo.lua
return function (i)
return i
end
How can I load this file with the C API and call the returned function?
I just need function calls beginning with luaL_loadfile/luaL_dostring.
A loaded chunk is just a regular function. Loading the module from C can be thought of like this:
return (function() -- this is the chunk compiled by load
-- foo.lua
return function (i)
return i
end
end)() -- executed with call/pcall
All you have to do is load the chunk and call it, its return value is your function:
// load the chunk
if (luaL_loadstring(L, script)) {
return luaL_error(L, "Error loading script: %s", lua_tostring(L, -1));
}
// call the chunk (function will be on top of the stack)
if (lua_pcall(L, 0, 1, 0)) {
return luaL_error(L, "Error running chunk: %s", lua_tostring(L, -1));
}
// call the function
lua_pushinteger(L, 42); // function arg (i)
if (lua_pcall(L, 1, 1, 0)) {
return luaL_error(L, "Error calling function: %s", lua_tostring(L, -1));
}
in this code i load and run test.lua file
int main (){
L = luaL_newstate();
luaL_openlibs(L);
luaL_dofile(L, "test.lua");
lua_close(L);
return 0;
}
my test.lua file contents
print ("s1");
r=require 'simple';
print ("s2");
the simple module is installed before
when run ./lua_c ; output is only: s1
but when run lua test.lua; output is
s1
s2
and r in't nil
simple is failing to load or parse or execute. To find problem, use luaL_loadfile instead of luaL_dofile and check the return value. If non zero, there was a load error, which you can pop off the Lua stack and print. If no error, do the lua_pcall(L, 0, LUA_MULTRET, 0)) to run the chuck created by loadfile, and again check return code for error, pop off stack and print. It would be somehting like this:
int main ()
{
L = luaL_newstate();
luaL_openlibs(L);
if (luaL_loadfile(L, "test.lua"))
{
cout << "Error: " << lua_tostring(L, -1) << endl;
}
else if (lua_pcall(L, 0, LUA_MULTRET, 0))
{
cout << "Error: " << lua_tostring(L, -1) << endl;
}
else
{
// call successful
}
lua_close(L);
return 0;
}
Update: now that you know from the error message that simple.so has undefined symbol: lua_gettop, you know that there is a link error. Perhaps simple.so isn't linked to lua51.so, but since it works from lua.exe, which is linked to lua lib, one would it would work from your app, which is surely linked to it too. Another possibility is that lua.exe is statically linked but simple.so is not linked. Verify that simple.so is linked to lua51.so and that the lib it links to can be found such as via LD_LIBRARY_PATH. Verify lua.exe is linked to same .so.
How do you end a long running Lua script?
I have two threads, one runs the main program and the other controls a user supplied Lua script. I need to kill the thread that's running Lua, but first I need the script to exit.
Is there a way to force a script to exit?
I have read that the suggested approach is to return a Lua exception. However, it's not garanteed that the user's script will ever call an api function ( it could be in a tight busy loop). Further, the user could prevent errors from causing his script to exit by using a pcall.
You could use setjmp and longjump, just like the Lua library does internally. That will get you out of pcalls and stuff just fine without need to continuously error, preventing the script from attempting to handle your bogus errors and still getting you out of execution. (I have no idea how well this plays with threads though.)
#include <stdio.h>
#include <setjmp.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
jmp_buf place;
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!\n", countdown);
}
else
{
longjmp(place, 1);
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
if (setjmp(place) == 0)
luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
You could set a variable somewhere in your program and call it something like forceQuitLuaScript. Then, you use a hook, described here to run every n instructions. After n instructions, it'll run your hook which just checks if forceQuitLuaScript is set, and if it is do any clean up you need to do and kill the thread.
Edit: Here's a cheap example of how it could work, only this is single threaded. This is just to illustrate how you might handle pcall and such:
#include <stdlib.h>
#include "lauxlib.h"
void hook(lua_State* L, lua_Debug *ar)
{
static int countdown = 10;
if (countdown > 0)
{
--countdown;
printf("countdown: %d!\n", countdown);
}
else
{
// From now on, as soon as a line is executed, error
// keep erroring until you're script reaches the top
lua_sethook(L, hook, LUA_MASKLINE, 0);
luaL_error(L, "");
}
}
int main(int argc, const char *argv[])
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_sethook(L, hook, LUA_MASKCOUNT, 100);
// Infinitely recurse into pcalls
luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
lua_close(L);
printf("Done!");
return 0;
}
The way to end a script is to raise an error by calling error. However, if the user has called the script via pcall then this error will be caught.
It seems like you could terminate the thread externally (from your main thread) since the lua script is user supplied and you can't signal it to exit.
If that isn't an option, you could try the debug API. You could use lua_sethook to enable you to regain control assuming you have a way to gracefully terminate your thread in the hook.
I haven't found a way to cleanly kill a thread that is executing a long running lua script without relying on some intervention from the script itself. Here are some approaches I have taken in the past:
If the script is long running it is most likely in some loop. The script can check the value of some global variable on each iteration. By setting this variable from outside of the script you can then terminate the thread.
You can start the thread by using lua_resume. The script can then exit by using yield().
You could provide your own implementation of pcall that checks for a specific type of error. The script could then call error() with a custom error type that your version of pcall could watch for:
function()
local there_is_an_error = do_something()
if (there_is_an_error) then
error({code = 900, msg = "Custom error"})
end
end
possibly useless, but in the lua I use (luaplayer or PGELua), I exit with
os.exit()
or
pge.exit()
If you're using coroutines to start the threads, you could maybe use coroutine.yield() to stop it.
You might wanna take look at
https://github.com/amilamad/preemptive-task-scheduler-for-lua
project. its preemptive scheduler for lua.
It uses a lua_yeild function inside the hook. So you can suspend your lua thread. It also uses longjmp inside but its is much safer.
session:destroy();
Use this single line code on that where you are want to destroy lua script.
lua_KFunction cont(lua_State* L);
int my_yield_with_res(lua_State* L, int res) {
cout << " my_yield_with_res \n" << endl;
return lua_yieldk(L, 0, lua_yield(L, res), cont(L));/* int lua_yieldk(lua_State * L, int res, lua_KContext ctx, lua_KFunction k);
Приостанавливает выполнение сопрограммы(поток). Когда функция C вызывает lua_yieldk, работающая
сопрограмма приостанавливает свое выполнение и вызывает lua_resume, которая начинает возврат данной сопрограммы.
Параметр res - это число значений из стека, которые будут переданы в качестве результатов в lua_resume.
Когда сопрограмма снова возобновит выполнение, Lua вызовет заданную функцию продолжения k для продолжения выполнения
приостановленной C функции(смотрите §4.7). */
};
int hookFunc(lua_State* L, lua_Debug* ar) {
cout << " hookFunc \n" << endl;
return my_yield_with_res(L, 0);// хук./
};
lua_KFunction cont(lua_State* L) {// функция продолжения.
cout << " hooh off \n" << endl;
lua_sethook(L, (lua_Hook)hookFunc, LUA_MASKCOUNT, 0);// отключить хук foo.
return 0;
};
struct Func_resume {
Func_resume(lua_State* L, const char* funcrun, unsigned int Args) : m_L(L), m_funcrun(funcrun), m_Args(Args) {}
//имена функций, кол-во агрументов.
private:
void func_block(lua_State* L, const char* functionName, unsigned int Count, unsigned int m_Args) {
lua_sethook(m_L, (lua_Hook)hookFunc, LUA_MASKCOUNT, Count); //вызов функции с заданной паузой.
if (m_Args == 0) {
lua_getglobal(L, functionName);// получить имя функции.
lua_resume(L, L, m_Args);
}
if (m_Args != 0) {
int size = m_Args + 1;
lua_getglobal(L, functionName);
for (int i = 1; i < size; i++) {
lua_pushvalue(L, i);
}
lua_resume(L, L, m_Args);
}
};
public:
void Update(float dt) {
unsigned int Count = dt * 100.0;// Время работы потока.
func_block(m_L, m_funcrun, Count, m_Args);
};
~Func_resume() {}
private:
lua_State* m_L;
const char* m_funcrun; // имя функции.
unsigned int m_Count;// число итерации.
unsigned int m_Args;
};
const char* LUA = R"(
function main(y)
--print(" func main arg, a = ".. a.." y = ".. y)
for i = 1, y do
print(" func main count = ".. i)
end
end
)";
int main(int argc, char* argv[]) {
lua_State* L = luaL_newstate();/*Функция создает новое Lua состояние. */
luaL_openlibs(L);
luaL_dostring(L, LUA);
//..pushlua(L, 12);
pushlua(L, 32);
//do {
Func_resume func_resume(L, "main", 2);
func_resume.Update(1.7);
lua_close(L);
// } while (LUA_OK != lua_status(L)); // Пока поток не завершен.
return 0;
};