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
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. ]]
-- 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
@ -53,8 +72,15 @@ sandbox = {
error = error,
_G = nil, -- see below
getmetatable = function(t)
if type(t) == "string" then return nil end
return getmetatable(t)
if type(t) == "string" then -- don't allow messing with the string mt
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,
ipairs = ipairs,
load = function(ld, source, mode, env)
@ -81,29 +107,21 @@ sandbox = {
if type(mt) ~= "table" then
return setmetatable(t, mt)
end
local gc = rawget(mt, "__gc")
if type(gc) == "function" then
if type(rawget(mt, "__gc")) == "function" then
-- For all user __gc functions we enforce a much tighter deadline.
-- This is because these functions may be called from the main
-- thread under certain circumstanced (such as when saving the world),
-- which can lead to noticeable lag if the __gc function behaves badly.
rawset(mt, "__gc", function(self)
local oldDeadline, oldHitDeadline = deadline, hitDeadline
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)
checkDeadline()
deadline, hitDeadline = oldDeadline, oldHitDeadline
if not result then
error(reason, 0)
end
end)
local sbmt = {} -- sandboxed metatable. only for __gc stuff, so it's
-- kinda ok to have a shallow copy instead... meh.
for k, v in pairs(mt) do
sbmt[k] = v
end
sbmt.mt = mt
sbmt.__gc = sgc
mt = sbmt
end
local result = setmetatable(t, mt)
rawset(mt, "__gc", gc)
return result
return setmetatable(t, mt)
end,
tonumber = tonumber,
tostring = tostring,

View File

@ -258,7 +258,7 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
}
catch {
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")
case e: LuaGcMetamethodException =>
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) {
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 {
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.start()
}
@ -386,6 +392,12 @@ class NativeLuaArchitecture(val machine: api.machine.Machine) extends Architectu
for (api <- apis) {
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 {
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 ")))

View File

@ -122,26 +122,32 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
def persist(index: Int): Array[Byte] = {
if (Settings.get.allowPersistence) {
configure()
lua.getGlobal("eris") // ... eris
lua.getField(-1, "persist") // ... eris persist
if (lua.isFunction(-1)) {
lua.getField(LuaState.REGISTRYINDEX, "perms") // ... eris persist perms
lua.pushValue(index) // ... eris persist perms obj
try {
lua.call(2, 1) // ... eris str?
} catch {
case e: Throwable =>
lua.pop(1)
throw e
}
if (lua.isString(-1)) {
// ... eris str
val result = lua.toByteArray(-1)
lua.pop(2) // ...
return result
try {
lua.gc(LuaState.GcAction.STOP, 0)
lua.getGlobal("eris") // ... eris
lua.getField(-1, "persist") // ... eris persist
if (lua.isFunction(-1)) {
lua.getField(LuaState.REGISTRYINDEX, "perms") // ... eris persist perms
lua.pushValue(index) // ... eris persist perms obj
try {
lua.call(2, 1) // ... eris str?
} catch {
case e: Throwable =>
lua.pop(1)
throw e
}
if (lua.isString(-1)) {
// ... eris str
val result = lua.toByteArray(-1)
lua.pop(2) // ...
return result
} // ... eris :(
} // ... eris :(
} // ... eris :(
lua.pop(2) // ...
lua.pop(2) // ...
}
finally {
lua.gc(LuaState.GcAction.RESTART, 0)
}
}
Array[Byte]()
}
@ -149,17 +155,23 @@ class PersistenceAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) {
def unpersist(value: Array[Byte]): Boolean = {
if (Settings.get.allowPersistence) {
configure()
lua.getGlobal("eris") // ... eris
lua.getField(-1, "unpersist") // ... eris unpersist
if (lua.isFunction(-1)) {
lua.getField(LuaState.REGISTRYINDEX, "uperms") // ... eris persist uperms
lua.pushByteArray(value) // ... eris unpersist uperms str
lua.call(2, 1) // ... eris obj
lua.insert(-2) // ... obj eris
try {
lua.gc(LuaState.GcAction.STOP, 0)
lua.getGlobal("eris") // ... eris
lua.getField(-1, "unpersist") // ... eris unpersist
if (lua.isFunction(-1)) {
lua.getField(LuaState.REGISTRYINDEX, "uperms") // ... eris persist uperms
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)
return true
} // ... :(
lua.pop(1)
}
finally {
lua.gc(LuaState.GcAction.RESTART, 0)
}
}
false
}