Somewhat experimental fix for memory leaks seen in Microcontrollers. If you notice any new issues after this, please do let me know.

This essentially schedules machines for full cleanup at a later point, to allow executor threads to finish (can't close states while they're running, don't want to wait for them to finish to avoid lagging servers).
This commit is contained in:
Florian Nücke 2015-03-29 22:45:43 +02:00
parent 5e9861d04a
commit d13cdc0cd8
3 changed files with 40 additions and 13 deletions

View File

@ -22,6 +22,7 @@ import li.cil.oc.common.tileentity.traits.power
import li.cil.oc.integration.Mods
import li.cil.oc.integration.util
import li.cil.oc.server.component.Keyboard
import li.cil.oc.server.machine.Machine
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import li.cil.oc.util._
import net.minecraft.client.Minecraft
@ -49,12 +50,16 @@ object EventHandler {
private val keyboards = java.util.Collections.newSetFromMap[Keyboard](new java.util.WeakHashMap[Keyboard, java.lang.Boolean])
private val machines = mutable.Set.empty[Machine]
def onRobotStart(robot: Robot): Unit = runningRobots += robot
def onRobotStopped(robot: Robot): Unit = runningRobots -= robot
def addKeyboard(keyboard: Keyboard): Unit = keyboards += keyboard
def scheduleClose(machine: Machine) = machines += machine
def schedule(tileEntity: TileEntity) {
if (SideTracker.isServer) pending.synchronized {
pending += (() => Network.joinOrCreateNetwork(tileEntity))
@ -130,6 +135,10 @@ object EventHandler {
else if (robot.world != null) robot.machine.update()
})
runningRobots --= invalid
val closed = mutable.ArrayBuffer.empty[Machine]
machines.foreach(machine => if (machine.tryClose()) closed += machine)
machines --= closed
}
@SubscribeEvent
@ -243,7 +252,7 @@ object EventHandler {
didRecraft = recraft(e, tablet, stack => {
// Restore EEPROM currently used in tablet.
new TabletData(stack).items.collect { case Some(item) => item}.find(api.Items.get(_) == eeprom)
new TabletData(stack).items.collect { case Some(item) => item }.find(api.Items.get(_) == eeprom)
}) || didRecraft
// Presents?

View File

@ -127,6 +127,11 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B
ServerPacketSender.sendComputerState(this)
}
override def dispose(): Unit = {
super.dispose()
if (machine != null) machine.stop()
}
// ----------------------------------------------------------------------- //
override def readFromNBTForServer(nbt: NBTTagCompound) {

View File

@ -23,6 +23,7 @@ import li.cil.oc.api.network.Message
import li.cil.oc.api.network.Node
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.common.EventHandler
import li.cil.oc.common.SaveHandler
import li.cil.oc.common.Slot
import li.cil.oc.common.tileentity
@ -257,6 +258,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
false
case _ =>
state.push(Machine.State.Stopping)
EventHandler.scheduleClose(this)
true
})
@ -392,6 +394,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
// ----------------------------------------------------------------------- //
def isExecuting = state.synchronized(state.contains(Machine.State.Running))
override val canUpdate = true
override def update() = if (state.synchronized(state.top != Machine.State.Stopped)) {
@ -514,7 +518,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
inSynchronizedCall = false
}
assert(state.top != Machine.State.Running)
assert(!isExecuting)
case _ => // Nothing special to do, just avoid match errors.
})
@ -643,7 +647,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
super.load(nbt)
state.pushAll(nbt.getIntArray("state").reverse.map(Machine.State(_)))
state.pushAll(nbt.getIntArray("state").reverseMap(Machine.State(_)))
nbt.getTagList("users", NBT.TAG_STRING).foreach((tag: NBTTagString) => _users += tag.func_150285_a_())
if (nbt.hasKey("message")) {
message = Some(nbt.getString("message"))
@ -703,7 +707,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
}
override def save(nbt: NBTTagCompound): Unit = Machine.this.synchronized {
assert(state.top != Machine.State.Running) // Lock on 'this' should guarantee this.
assert(!isExecuting) // Lock on 'this' should guarantee this.
// Make sure we don't continue running until everything has saved.
pause(0.05)
@ -798,16 +802,25 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
false
}
def tryClose(): Boolean =
if (isExecuting) false
else {
close()
true
}
private def close() = state.synchronized(
if (state.size == 0 || state.top != Machine.State.Stopped) {
state.clear()
state.push(Machine.State.Stopped)
Option(architecture).foreach(_.close())
signals.clear()
uptime = 0
cpuTotal = 0
cpuStart = 0
remainIdle = 0
this.synchronized {
state.clear()
state.push(Machine.State.Stopped)
Option(architecture).foreach(_.close())
signals.clear()
uptime = 0
cpuTotal = 0
cpuStart = 0
remainIdle = 0
}
// Mark state change in owner, to send it to clients.
host.markChanged()
@ -909,7 +922,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach
case Machine.State.Stopping => // Nothing to do, we'll die anyway.
case _ => throw new AssertionError("Invalid state in executor post-processing.")
}
assert(state.top != Machine.State.Running)
assert(!isExecuting)
}
}
catch {