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 end
local result, reason = coroutine.resume(co, self) sbmt.mt = mt
debug.sethook(co) sbmt.__gc = sgc
checkDeadline() mt = sbmt
deadline, hitDeadline = oldDeadline, oldHitDeadline
if not result then
error(reason, 0)
end
end)
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

@ -258,7 +258,7 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
} }
catch { catch {
case e: LuaRuntimeException => case e: LuaRuntimeException =>
OpenComputers.log.warning("Kernel crashed. This is a bug!\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat ")) OpenComputers.log.warning("Kernel crashed. This is a bug!\n" + e.toString + "\tat " + e.getLuaStackTrace.mkString("\n\tat "))
new ExecutionResult.Error("kernel panic: this is a bug, check your log file and report it") new ExecutionResult.Error("kernel panic: this is a bug, check your log file and report it")
case e: LuaGcMetamethodException => case e: LuaGcMetamethodException =>
if (e.getMessage != null) new ExecutionResult.Error("kernel panic:\n" + e.getMessage) if (e.getMessage != null) new ExecutionResult.Error("kernel panic:\n" + e.getMessage)
@ -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,26 +122,32 @@ 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()
lua.getGlobal("eris") // ... eris try {
lua.getField(-1, "persist") // ... eris persist lua.gc(LuaState.GcAction.STOP, 0)
if (lua.isFunction(-1)) { lua.getGlobal("eris") // ... eris
lua.getField(LuaState.REGISTRYINDEX, "perms") // ... eris persist perms lua.getField(-1, "persist") // ... eris persist
lua.pushValue(index) // ... eris persist perms obj if (lua.isFunction(-1)) {
try { lua.getField(LuaState.REGISTRYINDEX, "perms") // ... eris persist perms
lua.call(2, 1) // ... eris str? lua.pushValue(index) // ... eris persist perms obj
} catch { try {
case e: Throwable => lua.call(2, 1) // ... eris str?
lua.pop(1) } catch {
throw e case e: Throwable =>
} lua.pop(1)
if (lua.isString(-1)) { throw e
// ... eris str }
val result = lua.toByteArray(-1) if (lua.isString(-1)) {
lua.pop(2) // ... // ... eris str
return result val result = lua.toByteArray(-1)
lua.pop(2) // ...
return result
} // ... eris :(
} // ... eris :( } // ... eris :(
} // ... eris :( lua.pop(2) // ...
lua.pop(2) // ... }
finally {
lua.gc(LuaState.GcAction.RESTART, 0)
}
} }
Array[Byte]() Array[Byte]()
} }
@ -149,17 +155,23 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
def unpersist(value: Array[Byte]): Boolean = { def unpersist(value: Array[Byte]): Boolean = {
if (Settings.get.allowPersistence) { if (Settings.get.allowPersistence) {
configure() configure()
lua.getGlobal("eris") // ... eris try {
lua.getField(-1, "unpersist") // ... eris unpersist lua.gc(LuaState.GcAction.STOP, 0)
if (lua.isFunction(-1)) { lua.getGlobal("eris") // ... eris
lua.getField(LuaState.REGISTRYINDEX, "uperms") // ... eris persist uperms lua.getField(-1, "unpersist") // ... eris unpersist
lua.pushByteArray(value) // ... eris unpersist uperms str if (lua.isFunction(-1)) {
lua.call(2, 1) // ... eris obj lua.getField(LuaState.REGISTRYINDEX, "uperms") // ... eris persist uperms
lua.insert(-2) // ... obj eris lua.pushByteArray(value) // ... eris unpersist uperms str
lua.call(2, 1) // ... eris obj
lua.insert(-2) // ... obj eris
lua.pop(1)
return true
} // ... :(
lua.pop(1) lua.pop(1)
return true }
} // ... :( finally {
lua.pop(1) lua.gc(LuaState.GcAction.RESTART, 0)
}
} }
false false
} }