Fixed objects which store Lua callback functions to avoid attempting to use dead coroutines when deleting the reference to the callback function. Should fix crashes when coroutines are mixed with those objects.

Also improved the performance of World:rayCast and World:queryBoundingBox.
This commit is contained in:
Alex Szpakowski
2015-07-18 03:26:07 -03:00
parent 64b9d40f00
commit 2cce18946d
14 changed files with 139 additions and 122 deletions
+14 -26
View File
@@ -26,13 +26,13 @@ namespace love
const char REFERENCE_TABLE_NAME[] = "love-references";
Reference::Reference()
: L(nullptr)
: pinnedL(nullptr)
, idx(LUA_REFNIL)
{
}
Reference::Reference(lua_State *L)
: L(L)
: pinnedL(nullptr)
, idx(LUA_REFNIL)
{
ref(L);
@@ -46,7 +46,7 @@ Reference::~Reference()
void Reference::ref(lua_State *L)
{
unref(); // Just to be safe.
this->L = L;
pinnedL = luax_getpinnedthread(L);
luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
lua_insert(L, -2); // Move reference table behind value.
idx = luaL_ref(L, -2);
@@ -57,38 +57,26 @@ void Reference::unref()
{
if (idx != LUA_REFNIL)
{
luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
luaL_unref(L, -1, idx);
lua_pop(L, 1);
// We use a pinned thread/coroutine for the Lua state because we know it
// hasn't been garbage collected and is valid, as long as the whole lua
// state is still open.
luax_insist(pinnedL, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
luaL_unref(pinnedL, -1, idx);
lua_pop(pinnedL, 1);
idx = LUA_REFNIL;
}
}
void Reference::push(lua_State *newL)
void Reference::push(lua_State *L)
{
if (idx != LUA_REFNIL)
{
luax_insist(newL, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
lua_rawgeti(newL, -1, idx);
lua_remove(newL, -2);
luax_insist(L, LUA_REGISTRYINDEX, REFERENCE_TABLE_NAME);
lua_rawgeti(L, -1, idx);
lua_remove(L, -2);
}
else
lua_pushnil(newL);
}
void Reference::push()
{
push(L);
}
lua_State *Reference::getL() const
{
return L;
}
void Reference::setL(lua_State *newL)
{
L = newL;
lua_pushnil(L);
}
} // love
+7 -26
View File
@@ -64,36 +64,17 @@ public:
void unref();
/**
* Pushes the referred value onto the stack of a different coroutine
* in the same main Lua state.
* THIS SHOULD NOT BE USED FOR DIFFERENT LUA STATES (created with
* luaL_newstate)! Only with different coroutines!
* Pushes the referred value onto the stack of the specified Lua coroutine.
* NOTE: The coroutine *must* belong to the same Lua state that was used for
* Reference::ref.
**/
void push(lua_State *newL);
/**
* Pushes the referred value onto the stack.
**/
void push();
/**
* Gets the Lua state associated with this
* reference.
**/
lua_State *getL() const;
/**
* Associates a new Lua state with this reference.
* THIS IS DANGEROUS! It is only designed to be
* used with different coroutines from the same
* main Lua state!
**/
void setL(lua_State *newL);
void push(lua_State *L);
private:
// The Lua state in which the reference resides.
lua_State *L;
// A pinned coroutine (probably the main thread) belonging to the Lua state
// in which the reference resides.
lua_State *pinnedL;
// Index to the Lua reference.
int idx;
+34 -5
View File
@@ -83,7 +83,7 @@ Reference *luax_refif(lua_State *L, int type)
void luax_printstack(lua_State *L)
{
for (int i = 1; i<=lua_gettop(L); i++)
for (int i = 1; i <= lua_gettop(L); i++)
std::cout << i << " - " << luaL_typename(L, i) << std::endl;
}
@@ -590,8 +590,6 @@ int luax_insistregistry(lua_State *L, Registry r)
{
switch (r)
{
case REGISTRY_GC:
return luax_insistlove(L, "_gc");
case REGISTRY_MODULES:
return luax_insistlove(L, "_modules");
case REGISTRY_OBJECTS:
@@ -605,8 +603,6 @@ int luax_getregistry(lua_State *L, Registry r)
{
switch (r)
{
case REGISTRY_GC:
return luax_getlove(L, "_gc");
case REGISTRY_MODULES:
return luax_getlove(L, "_modules");
case REGISTRY_OBJECTS:
@@ -617,6 +613,39 @@ int luax_getregistry(lua_State *L, Registry r)
}
}
static const char *MAIN_THREAD_KEY = "_love_mainthread";
lua_State *luax_insistpinnedthread(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, MAIN_THREAD_KEY);
if (lua_isnoneornil(L, -1))
{
lua_pop(L, 1);
// lua_pushthread returns 1 if it's actually the main thread, but we
// can't actually get the real main thread if lua_pushthread doesn't
// return it (in Lua 5.1 at least), so we ignore that for now...
// We do store a strong reference to the current thread/coroutine in
// the registry, however.
lua_pushthread(L);
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, MAIN_THREAD_KEY);
}
lua_State *thread = lua_tothread(L, -1);
lua_pop(L, 1);
return thread;
}
lua_State *luax_getpinnedthread(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, MAIN_THREAD_KEY);
lua_State *thread = lua_tothread(L, -1);
lua_pop(L, 1);
return thread;
}
extern "C" int luax_typerror(lua_State *L, int narg, const char *tname)
{
int argtype = lua_type(L, narg);
+18 -1
View File
@@ -49,7 +49,6 @@ class Reference;
**/
enum Registry
{
REGISTRY_GC,
REGISTRY_MODULES,
REGISTRY_OBJECTS
};
@@ -390,6 +389,24 @@ int luax_insistregistry(lua_State *L, Registry r);
**/
int luax_getregistry(lua_State *L, Registry r);
/**
* Gets (and pins if needed) a "pinned" Lua thread (coroutine) in the specified
* Lua state. This will usually be the main Lua thread, unless the first call
* to this function for a specific Lua state is made from within a coroutine.
* NOTE: This does not push anything to the stack.
**/
lua_State *luax_insistpinnedthread(lua_State *L);
/**
* Gets a "pinned" Lua thread (coroutine) in the specified Lua state. This will
* usually be the main Lua thread. This can be used to access global variables
* in a specific Lua state without needing another alive lua_State value.
* PRECONDITION: luax_insistpinnedthread must have been called on a lua_State
* value corresponding to the Lua state which will be used with this function.
* NOTE: This does not push anything to the stack.
**/
lua_State *luax_getpinnedthread(lua_State *L);
extern "C" { // Also called from luasocket
int luax_typerror(lua_State *L, int narg, const char *tname);
}