Disabling Lua GC while persisting and unpersisting, might help with native crash.

This commit is contained in:
Florian Nücke 2014-08-12 18:36:18 +02:00
parent 891efc924e
commit 2c87f6059a
3 changed files with 93 additions and 51 deletions

View File

@ -41,6 +41,25 @@ local function spcall(...)
end end
end end
local function sgc(self)
local oldDeadline, oldHitDeadline = deadline, hitDeadline
local mt = debug.getmetatable(self)
mt = rawget(mt, "mt")
local gc = rawget(mt, "__gc")
if type(gc) ~= "function" then
return
end
local co = coroutine.create(gc)
debug.sethook(co, checkDeadline, "", hookInterval)
deadline, hitDeadline = math.min(oldDeadline, computer.realTime() + 0.5), true
local result, reason = coroutine.resume(co, self)
debug.sethook(co)
deadline, hitDeadline = oldDeadline, oldHitDeadline
if not result then
error(reason, 0)
end
end
--[[ This is the global environment we make available to userland programs. ]] --[[ This is the global environment we make available to userland programs. ]]
-- You'll notice that we do a lot of wrapping of native functions and adding -- You'll notice that we do a lot of wrapping of native functions and adding
-- parameter checks in those wrappers. This is to avoid errors from the host -- parameter checks in those wrappers. This is to avoid errors from the host
@ -53,8 +72,15 @@ sandbox = {
error = error, error = error,
_G = nil, -- see below _G = nil, -- see below
getmetatable = function(t) getmetatable = function(t)
if type(t) == "string" then return nil end if type(t) == "string" then -- don't allow messing with the string mt
return getmetatable(t) return nil
end
local result = getmetatable(t)
-- check if we have a wrapped __gc using mt
if type(result) == "table" and rawget(result, "__gc") == sgc then
result = rawget(result, "mt")
end
return result
end, end,
ipairs = ipairs, ipairs = ipairs,
load = function(ld, source, mode, env) load = function(ld, source, mode, env)
@ -81,29 +107,21 @@ sandbox = {
if type(mt) ~= "table" then if type(mt) ~= "table" then
return setmetatable(t, mt) return setmetatable(t, mt)
end end
local gc = rawget(mt, "__gc") if type(rawget(mt, "__gc")) == "function" then
if type(gc) == "function" then
-- For all user __gc functions we enforce a much tighter deadline. -- For all user __gc functions we enforce a much tighter deadline.
-- This is because these functions may be called from the main -- This is because these functions may be called from the main
-- thread under certain circumstanced (such as when saving the world), -- thread under certain circumstanced (such as when saving the world),
-- which can lead to noticeable lag if the __gc function behaves badly. -- which can lead to noticeable lag if the __gc function behaves badly.
rawset(mt, "__gc", function(self) local sbmt = {} -- sandboxed metatable. only for __gc stuff, so it's
local oldDeadline, oldHitDeadline = deadline, hitDeadline -- kinda ok to have a shallow copy instead... meh.
local co = coroutine.create(gc) for k, v in pairs(mt) do
debug.sethook(co, checkDeadline, "", hookInterval) sbmt[k] = v
deadline, hitDeadline = math.min(oldDeadline, computer.realTime() + 0.5), true
local result, reason = coroutine.resume(co, self)
debug.sethook(co)
checkDeadline()
deadline, hitDeadline = oldDeadline, oldHitDeadline
if not result then
error(reason, 0)
end end
end) sbmt.mt = mt
sbmt.__gc = sgc
mt = sbmt
end end
local result = setmetatable(t, mt) return setmetatable(t, mt)
rawset(mt, "__gc", gc)
return result
end, end,
tonumber = tonumber, tonumber = tonumber,
tostring = tostring, tostring = tostring,

View File

@ -347,9 +347,15 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
for (api <- apis) { for (api <- apis) {
api.load(nbt) api.load(nbt)
} }
try lua.gc(LuaState.GcAction.COLLECT, 0) catch {
case t: Throwable =>
OpenComputers.log.warning(s"Error cleaning up loaded computer @ (${machine.owner.x}, ${machine.owner.y}, ${machine.owner.z}). This either means the server is badly overloaded or a user created an evil __gc method, accidentally or not.")
machine.crash("error in garbage collector, most likely __gc method timed out")
}
} catch { } catch {
case e: LuaRuntimeException => case e: LuaRuntimeException =>
OpenComputers.log.warning("Could not unpersist computer.\n" + e.toString + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat "))) OpenComputers.log.warning(s"Could not unpersist computer @ (${machine.owner.x}, ${machine.owner.y}, ${machine.owner.z}).\n${e.toString}" + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat ")))
machine.stop() machine.stop()
machine.start() machine.start()
} }
@ -386,6 +392,12 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
for (api <- apis) { for (api <- apis) {
api.save(nbt) api.save(nbt)
} }
try lua.gc(LuaState.GcAction.COLLECT, 0) catch {
case t: Throwable =>
OpenComputers.log.warning(s"Error cleaning up loaded computer @ (${machine.owner.x}, ${machine.owner.y}, ${machine.owner.z}). This either means the server is badly overloaded or a user created an evil __gc method, accidentally or not.")
machine.crash("error in garbage collector, most likely __gc method timed out")
}
} catch { } catch {
case e: LuaRuntimeException => case e: LuaRuntimeException =>
OpenComputers.log.warning(s"Could not persist computer @ (${machine.owner.x}, ${machine.owner.y}, ${machine.owner.z}).\n${e.toString}" + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat "))) OpenComputers.log.warning(s"Could not persist computer @ (${machine.owner.x}, ${machine.owner.y}, ${machine.owner.z}).\n${e.toString}" + (if (e.getLuaStackTrace.isEmpty) "" else "\tat " + e.getLuaStackTrace.mkString("\n\tat ")))

View File

@ -122,6 +122,8 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
def persist(index: Int): Array[Byte] = { def persist(index: Int): Array[Byte] = {
if (Settings.get.allowPersistence) { if (Settings.get.allowPersistence) {
configure() configure()
try {
lua.gc(LuaState.GcAction.STOP, 0)
lua.getGlobal("eris") // ... eris lua.getGlobal("eris") // ... eris
lua.getField(-1, "persist") // ... eris persist lua.getField(-1, "persist") // ... eris persist
if (lua.isFunction(-1)) { if (lua.isFunction(-1)) {
@ -143,12 +145,18 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
} // ... eris :( } // ... eris :(
lua.pop(2) // ... lua.pop(2) // ...
} }
finally {
lua.gc(LuaState.GcAction.RESTART, 0)
}
}
Array[Byte]() Array[Byte]()
} }
def unpersist(value: Array[Byte]): Boolean = { def unpersist(value: Array[Byte]): Boolean = {
if (Settings.get.allowPersistence) { if (Settings.get.allowPersistence) {
configure() configure()
try {
lua.gc(LuaState.GcAction.STOP, 0)
lua.getGlobal("eris") // ... eris lua.getGlobal("eris") // ... eris
lua.getField(-1, "unpersist") // ... eris unpersist lua.getField(-1, "unpersist") // ... eris unpersist
if (lua.isFunction(-1)) { if (lua.isFunction(-1)) {
@ -161,6 +169,10 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
} // ... :( } // ... :(
lua.pop(1) lua.pop(1)
} }
finally {
lua.gc(LuaState.GcAction.RESTART, 0)
}
}
false false
} }
} }