Alright then - my first reaction was that I was dealing with a sneaky buffer overflow somewhere in my code so I wrote a stack verifier for my memory manager and made sure I wasn't trashing any unwanted memory by accident. As it is, I'm not. So I started weeding out as much code as possible to come up with the simplest test case and finally managed to reproduce the problem on a small scale. I'd like to note first:
1) the crash is spontaneous - no exceptions are thrown. Depending on something I can't quite understand yet, though, the nature of the crash may either be instantaneous or may pop up a "xxx.exe has stopped working - Windows is checking for solutions" notification. Debugging halts in an unloaded frame. Adding output tracers and a counter to the code produces repeatable results and seems to indicate that the crash happens in lua_pushlstring
2) the number of iterations the script can run depends on what I'm writing into the table - I can't tell if it's the length of the string or not, but my initial suspicion is that Lua causes a segfault internally. I don't know of any way to debug it.
3) when creating the table from scratch each time I call GetActiveItem() from the script, the number of iterations I get out of it before it crashes in my current production code is 63. By changing what I put into the table I can get more iterations out of it, but the fact remains: I'm doing something wrong
4) I can create a whole lot of tables in advance (the below code creates 40000), but even then lua_getglobal() crashes, albeit after more times I run the script
The below code won't run when you copy-paste it. I didn't create a new project so I ended up copy-pasting it together - hence it might be missing a basic include file or whatnot.
To run it, you'll also have to replace the "lout << ??? << endl;" lines with printf() or whatever output functionality you have available to you and then simply call fnTestLuaTable() from something like main() or WinMain() to run the test. The create all-tables-at-once vs create-table-as-needed approach can be toggled by uncommenting the two lines in GetActiveItem2(). The test is pretty QnD, but it should compile and it behaves on par with what I'm encountering in my main code.
Note that I'm using the latest version of LuaJIT.
#include <windows.h>
extern "C" {
#include "../lua/LuaJIT-2.0.3/src/lua.h"
#include "../lua/LuaJIT-2.0.3/src/lauxlib.h"
#include "../lua/LuaJIT-2.0.3/src/lualib.h"
}
struct IEntity;
IEntity* curent;
static int loadEntity(IN lua_State *L)
{
IEntity** udata = (IEntity**)lua_newuserdata(L, sizeof(IEntity*));
*udata = curent;
luaL_getmetatable(L, "IEntity");
lua_setmetatable(L, -2);
return 1;
}
void LUAErr(lua_State* L, int status)
{
if(status != 0)
{
lout << "LUA" << " (" << status << "): " << lua_tostring(L, -1) << std::endl;
lua_pop(L, 1); // remove error message
}
}
void luaExposeInterface(IN lua_State * L, IN const luaL_Reg listInterfaceFunctions[], IN char * sName)
{
luaL_newmetatable(L, sName);
luaL_setfuncs(L, listInterfaceFunctions, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -1, "__index");
lua_pushvalue(L, -2);
lua_setfield(L, -2, "__metatable");
lua_setglobal(L, sName);
}
static int GetActiveItem2(IN lua_State * L)
{
static bool bTableCreated = false;
static int ir = 0;
//output the iteration number
lout<< ir++ <<endl;
if(!bTableCreated)
{
//uncomment the next two lines to create a bunch of tables in advance
//for(int t = 0; t < (int)40000; t++)
{
lua_newtable(L);
for(int i = 0; i < (int)20; i++)
{
const char* ptr = "Force";
lua_pushlstring(L, ptr, STRLEN(ptr));
lua_pushnumber(L, 8);
lua_settable(L, -3);
}
char sname[1024];
sprintf_s(sname, "activeItem%d", 1);
lua_setglobal(L, sname);
}
lua_getglobal(L, "activeItem1");
lout << "create table" << endl;
//bTableCreated = true;
}
else
{
lout << "load table" << endl;
lua_getglobal(L, "activeItem1");
}
return 1;
}
static int AddVelocity2(IN lua_State * L)
{
float fVelocity = (float)luaL_checknumber(L, 2);
lout << fVelocity << endl;
return 0;
}
void fnTestLuaTable()
{
lua_State* L;
L = lua_open();
luaopen_base(L);
luaopen_table(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
luaopen_debug(L);
luaopen_ffi(L);
luaL_openlibs(L);
IEntity* entity = NULL;
curent = entity;
static const luaL_Reg listEntityFuncs[] = {
{ "load", loadEntity },
{ "GetActiveItem", GetActiveItem2 },
{ "AddVelocity", AddVelocity2 },
{ "__gc", NULL},
{ NULL, NULL }
};
luaExposeInterface(L, listEntityFuncs, "IEntity");
LUAErr(L, luaL_dofile(L, "test2.lua"));
//run it a bunch of times
for(int i = 0; i < (int)2000; i++)
{
lua_getglobal(L, "OnEntityAction");
lua_pushnumber(L, 0);
LUAErr(L, lua_pcall(L, 1, 0, 0));
}
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//my own error piping class is initialized here
//errlogAttachStream(&out);
//errlogEnableConsole(true);
fnTestLuaTable();
return 0;
}
And the corresponding Lua file:
function OnEntityAction(iAction)
player = IEntity:load()
activeItem2 = player:GetActiveItem()
player:AddVelocity(activeItem2["Force"])
end
I'm hoping the problem is something painfully simple - I'm really having difficulty stripping it down to anything more bare bones than this...
The fact that it runs okay for a number of times and then crashes with no warning or even a silent opt-out is worrisome to say the least, though.