From 0de7a2d83a029c165d23c356d1bc1fe381f94cd1 Mon Sep 17 00:00:00 2001 From: yueh Date: Thu, 6 Aug 2015 23:30:26 +0200 Subject: [PATCH] Closes #1283: Add custom Callable to prevent memory leaks --- src/main/java/appeng/hooks/TickHandler.java | 25 +++++----- src/main/java/appeng/me/GridNode.java | 6 +-- .../appeng/me/cache/helpers/Connections.java | 8 +-- .../automation/PartAnnihilationPlane.java | 7 +-- .../parts/p2p/PartP2POpenComputers.java | 10 ++-- .../tile/spatial/TileSpatialIOPort.java | 9 ++-- src/main/java/appeng/util/BlockUpdate.java | 14 ++---- src/main/java/appeng/util/IWorldCallable.java | 49 +++++++++++++++++++ src/main/java/appeng/util/Platform.java | 2 +- .../appeng/worldgen/MeteoriteWorldGen.java | 24 +++++---- 10 files changed, 101 insertions(+), 53 deletions(-) create mode 100644 src/main/java/appeng/util/IWorldCallable.java diff --git a/src/main/java/appeng/hooks/TickHandler.java b/src/main/java/appeng/hooks/TickHandler.java index 4bca8b97f..92c32df00 100644 --- a/src/main/java/appeng/hooks/TickHandler.java +++ b/src/main/java/appeng/hooks/TickHandler.java @@ -25,7 +25,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.Queue; import java.util.WeakHashMap; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import com.google.common.base.Stopwatch; @@ -55,6 +54,7 @@ import appeng.entity.EntityFloatingItem; import appeng.me.Grid; import appeng.me.NetworkList; import appeng.tile.AEBaseTile; +import appeng.util.IWorldCallable; import appeng.util.Platform; @@ -62,9 +62,9 @@ public class TickHandler { public static final TickHandler INSTANCE = new TickHandler(); - final Queue serverQueue = new LinkedList(); + final Queue> serverQueue = new LinkedList>(); final Multimap craftingJobs = LinkedListMultimap.create(); - private final WeakHashMap> callQueue = new WeakHashMap>(); + private final WeakHashMap>> callQueue = new WeakHashMap>>(); private final HandlerRep server = new HandlerRep(); private final HandlerRep client = new HandlerRep(); private final HashMap cliPlayerColors = new HashMap(); @@ -80,7 +80,7 @@ public class TickHandler return this.cliPlayerColors; } - public void addCallable( World w, Callable c ) + public void addCallable( World w, IWorldCallable c ) { if( w == null ) { @@ -88,11 +88,12 @@ public class TickHandler } else { - Queue queue = this.callQueue.get( w ); + Queue> queue = this.callQueue.get( w ); if( queue == null ) { - this.callQueue.put( w, queue = new LinkedList() ); + queue = new LinkedList>(); + this.callQueue.put( w, queue ); } queue.add( c ); @@ -239,13 +240,15 @@ public class TickHandler } // cross world queue. - this.processQueue( this.serverQueue ); + this.processQueue( this.serverQueue, null ); } // world synced queue(s) if( ev.type == Type.WORLD && ev.phase == Phase.START ) { - this.processQueue( this.callQueue.get( ( (WorldTickEvent) ev ).world ) ); + final World world = ( (WorldTickEvent) ev ).world; + final Queue> queue = this.callQueue.get( world ); + this.processQueue( queue, world ); } } @@ -263,7 +266,7 @@ public class TickHandler } } - private void processQueue( Queue queue ) + private void processQueue( Queue> queue, World world ) { if( queue == null ) { @@ -272,12 +275,12 @@ public class TickHandler Stopwatch sw = Stopwatch.createStarted(); - Callable c = null; + IWorldCallable c = null; while( ( c = queue.poll() ) != null ) { try { - c.call(); + c.call( world ); if( sw.elapsed( TimeUnit.MILLISECONDS ) > 50 ) { diff --git a/src/main/java/appeng/me/GridNode.java b/src/main/java/appeng/me/GridNode.java index fd6a5eaec..6ca763f3b 100644 --- a/src/main/java/appeng/me/GridNode.java +++ b/src/main/java/appeng/me/GridNode.java @@ -25,7 +25,6 @@ import java.util.Deque; import java.util.EnumSet; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.Callable; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; @@ -52,6 +51,7 @@ import appeng.api.util.IReadOnlyCollection; import appeng.core.worlddata.WorldData; import appeng.hooks.TickHandler; import appeng.me.pathfinding.IPathItem; +import appeng.util.IWorldCallable; import appeng.util.ReadOnlyCollection; @@ -669,7 +669,7 @@ public class GridNode implements IGridNode, IPathItem return this.lastUsedChannels; } - private static class MachineSecurityBreak implements Callable + private static class MachineSecurityBreak implements IWorldCallable { private final GridNode node; @@ -679,7 +679,7 @@ public class GridNode implements IGridNode, IPathItem } @Override - public Void call() throws Exception + public Void call(World world) throws Exception { this.node.getMachine().securityBreak(); diff --git a/src/main/java/appeng/me/cache/helpers/Connections.java b/src/main/java/appeng/me/cache/helpers/Connections.java index 5ba4fe465..f99db2b9d 100644 --- a/src/main/java/appeng/me/cache/helpers/Connections.java +++ b/src/main/java/appeng/me/cache/helpers/Connections.java @@ -20,13 +20,15 @@ package appeng.me.cache.helpers; import java.util.HashMap; -import java.util.concurrent.Callable; + +import net.minecraft.world.World; import appeng.api.networking.IGridNode; import appeng.parts.p2p.PartP2PTunnelME; +import appeng.util.IWorldCallable; -public class Connections implements Callable +public class Connections implements IWorldCallable { public final HashMap connections = new HashMap(); @@ -40,7 +42,7 @@ public class Connections implements Callable } @Override - public Object call() throws Exception + public Void call( World world ) throws Exception { this.me.updateConnections( this ); diff --git a/src/main/java/appeng/parts/automation/PartAnnihilationPlane.java b/src/main/java/appeng/parts/automation/PartAnnihilationPlane.java index 930042a26..f6d7eaa1b 100644 --- a/src/main/java/appeng/parts/automation/PartAnnihilationPlane.java +++ b/src/main/java/appeng/parts/automation/PartAnnihilationPlane.java @@ -20,7 +20,6 @@ package appeng.parts.automation; import java.util.List; -import java.util.concurrent.Callable; import com.google.common.collect.Lists; @@ -34,6 +33,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.IIcon; +import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.ForgeDirection; @@ -65,11 +65,12 @@ import appeng.hooks.TickHandler; import appeng.me.GridAccessException; import appeng.parts.PartBasicState; import appeng.server.ServerHelper; +import appeng.util.IWorldCallable; import appeng.util.Platform; import appeng.util.item.AEItemStack; -public class PartAnnihilationPlane extends PartBasicState implements IGridTickable, Callable +public class PartAnnihilationPlane extends PartBasicState implements IGridTickable, IWorldCallable { private final static IIcon SIDE_ICON = CableBusTextures.PartPlaneSides.getIcon(); private final static IIcon BACK_ICON = CableBusTextures.PartTransitionPlaneBack.getIcon(); @@ -86,7 +87,7 @@ public class PartAnnihilationPlane extends PartBasicState implements IGridTickab } @Override - public TickRateModulation call() throws Exception + public TickRateModulation call(World world) throws Exception { this.breaking = false; return this.breakBlock( true ); diff --git a/src/main/java/appeng/parts/p2p/PartP2POpenComputers.java b/src/main/java/appeng/parts/p2p/PartP2POpenComputers.java index 87789beb9..10f76645a 100644 --- a/src/main/java/appeng/parts/p2p/PartP2POpenComputers.java +++ b/src/main/java/appeng/parts/p2p/PartP2POpenComputers.java @@ -19,13 +19,12 @@ package appeng.parts.p2p; -import java.util.concurrent.Callable; - import javax.annotation.Nullable; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.IIcon; +import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import cpw.mods.fml.relauncher.Side; @@ -52,6 +51,7 @@ import appeng.integration.IntegrationType; import appeng.me.GridAccessException; import appeng.transformer.annotations.Integration.Interface; import appeng.transformer.annotations.Integration.InterfaceList; +import appeng.util.IWorldCallable; @InterfaceList( value = { @Interface( iface = "li.cil.oc.api.network.Environment", iname = "OpenComputers" ), @Interface( iface = "li.cil.oc.api.network.SidedEnvironment", iname = "OpenComputers" ) } ) @@ -60,7 +60,7 @@ public final class PartP2POpenComputers extends PartP2PTunnel updateCallback; + private final IWorldCallable updateCallback; public PartP2POpenComputers( ItemStack is ) { @@ -239,11 +239,11 @@ public final class PartP2POpenComputers extends PartP2PTunnel + private final class UpdateCallback implements IWorldCallable { @Nullable @Override - public Void call() throws Exception + public Void call( World world ) throws Exception { PartP2POpenComputers.this.updateConnections(); diff --git a/src/main/java/appeng/tile/spatial/TileSpatialIOPort.java b/src/main/java/appeng/tile/spatial/TileSpatialIOPort.java index 525eddb35..43eb00c30 100644 --- a/src/main/java/appeng/tile/spatial/TileSpatialIOPort.java +++ b/src/main/java/appeng/tile/spatial/TileSpatialIOPort.java @@ -19,11 +19,10 @@ package appeng.tile.spatial; -import java.util.concurrent.Callable; - import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import appeng.api.config.Actionable; @@ -46,10 +45,11 @@ import appeng.tile.events.TileEventType; import appeng.tile.grid.AENetworkInvTile; import appeng.tile.inventory.AppEngInternalInventory; import appeng.tile.inventory.InvOperation; +import appeng.util.IWorldCallable; import appeng.util.Platform; -public class TileSpatialIOPort extends AENetworkInvTile implements Callable +public class TileSpatialIOPort extends AENetworkInvTile implements IWorldCallable { final int[] sides = { 0, 1 }; @@ -122,9 +122,8 @@ public class TileSpatialIOPort extends AENetworkInvTile implements Callable } @Override - public Object call() throws Exception + public Void call( World world ) throws Exception { - ItemStack cell = this.getStackInSlot( 0 ); if( this.isSpatialCell( cell ) && this.getStackInSlot( 1 ) == null ) { diff --git a/src/main/java/appeng/util/BlockUpdate.java b/src/main/java/appeng/util/BlockUpdate.java index ac3083956..99292014d 100644 --- a/src/main/java/appeng/util/BlockUpdate.java +++ b/src/main/java/appeng/util/BlockUpdate.java @@ -19,33 +19,29 @@ package appeng.util; -import java.util.concurrent.Callable; - import net.minecraft.world.World; -public class BlockUpdate implements Callable +public class BlockUpdate implements IWorldCallable { - final World w; final int x; final int y; final int z; - public BlockUpdate( World w, int x, int y, int z ) + public BlockUpdate( int x, int y, int z ) { - this.w = w; this.x = x; this.y = y; this.z = z; } @Override - public Object call() throws Exception + public Boolean call( World world ) throws Exception { - if( this.w.blockExists( this.x, this.y, this.z ) ) + if( world.blockExists( this.x, this.y, this.z ) ) { - this.w.notifyBlocksOfNeighborChange( this.x, this.y, this.z, Platform.AIR_BLOCK ); + world.notifyBlocksOfNeighborChange( this.x, this.y, this.z, Platform.AIR_BLOCK ); } return true; diff --git a/src/main/java/appeng/util/IWorldCallable.java b/src/main/java/appeng/util/IWorldCallable.java new file mode 100644 index 000000000..ecbd3efda --- /dev/null +++ b/src/main/java/appeng/util/IWorldCallable.java @@ -0,0 +1,49 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.util; + + +import java.util.concurrent.Callable; + +import javax.annotation.Nullable; + +import net.minecraft.world.World; + + +/** + * An interface similar to {@link Callable}, but allowing to pass the {@link World} when calling. + * + * @see Callable + * @author yueh + * @version rv3 + * @since rv3 + */ +public interface IWorldCallable +{ + /** + * Similar to {@link Callable#call()} + * + * @see Callable#call() + * @param world + * @return + * @throws Exception + */ + @Nullable + T call( @Nullable World world ) throws Exception; +} diff --git a/src/main/java/appeng/util/Platform.java b/src/main/java/appeng/util/Platform.java index d8c6baece..a8f9bee5b 100644 --- a/src/main/java/appeng/util/Platform.java +++ b/src/main/java/appeng/util/Platform.java @@ -1911,7 +1911,7 @@ public class Platform { if( !worldObj.isRemote ) { - TickHandler.INSTANCE.addCallable( worldObj, new BlockUpdate( worldObj, xCoord, yCoord, zCoord ) ); + TickHandler.INSTANCE.addCallable( worldObj, new BlockUpdate( xCoord, yCoord, zCoord ) ); } } diff --git a/src/main/java/appeng/worldgen/MeteoriteWorldGen.java b/src/main/java/appeng/worldgen/MeteoriteWorldGen.java index 90b8a6810..c852a4849 100644 --- a/src/main/java/appeng/worldgen/MeteoriteWorldGen.java +++ b/src/main/java/appeng/worldgen/MeteoriteWorldGen.java @@ -20,7 +20,6 @@ package appeng.worldgen; import java.util.Random; -import java.util.concurrent.Callable; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; @@ -33,6 +32,7 @@ import appeng.core.AEConfig; import appeng.core.features.registries.WorldGenRegistry; import appeng.core.worlddata.WorldData; import appeng.hooks.TickHandler; +import appeng.util.IWorldCallable; import appeng.util.Platform; import appeng.worldgen.meteorite.ChunkOnly; @@ -51,11 +51,11 @@ public final class MeteoriteWorldGen implements IWorldGenerator int z = r.nextInt( 16 ) + ( chunkZ << 4 ); int depth = 180 + r.nextInt( 20 ); - TickHandler.INSTANCE.addCallable( w, new MeteoriteSpawn( x, depth, z, w ) ); + TickHandler.INSTANCE.addCallable( w, new MeteoriteSpawn( x, depth, z ) ); } else { - TickHandler.INSTANCE.addCallable( w, new MeteoriteSpawn( chunkX << 4, 128, chunkZ << 4, w ) ); + TickHandler.INSTANCE.addCallable( w, new MeteoriteSpawn( chunkX << 4, 128, chunkZ << 4 ) ); } } else @@ -113,24 +113,22 @@ public final class MeteoriteWorldGen implements IWorldGenerator return WorldData.instance().spawnData().getNearByMeteorites( w.provider.dimensionId, chunkX, chunkZ ); } - class MeteoriteSpawn implements Callable + class MeteoriteSpawn implements IWorldCallable { final int x; final int z; - final World w; final int depth; - public MeteoriteSpawn( int x, int depth, int z, World w ) + public MeteoriteSpawn( int x, int depth, int z ) { this.x = x; this.z = z; - this.w = w; this.depth = depth; } @Override - public Object call() throws Exception + public Object call( World world ) throws Exception { int chunkX = this.x >> 4; int chunkZ = this.z >> 4; @@ -138,10 +136,10 @@ public final class MeteoriteWorldGen implements IWorldGenerator double minSqDist = Double.MAX_VALUE; // near by meteorites! - for( NBTTagCompound data : MeteoriteWorldGen.this.getNearByMeteorites( this.w, chunkX, chunkZ ) ) + for( NBTTagCompound data : MeteoriteWorldGen.this.getNearByMeteorites( world, chunkX, chunkZ ) ) { MeteoritePlacer mp = new MeteoritePlacer(); - mp.spawnMeteorite( new ChunkOnly( this.w, chunkX, chunkZ ), data ); + mp.spawnMeteorite( new ChunkOnly( world, chunkX, chunkZ ), data ); minSqDist = Math.min( minSqDist, mp.getSqDistance( this.x, this.z ) ); } @@ -150,11 +148,11 @@ public final class MeteoriteWorldGen implements IWorldGenerator if( minSqDist > AEConfig.instance.minMeteoriteDistanceSq || isCluster ) { - MeteoriteWorldGen.this.tryMeteorite( this.w, this.depth, this.x, this.z ); + MeteoriteWorldGen.this.tryMeteorite( world, this.depth, this.x, this.z ); } - WorldData.instance().spawnData().setGenerated( this.w.provider.dimensionId, chunkX, chunkZ ); - WorldData.instance().compassData().service().updateArea( this.w, chunkX, chunkZ ); + WorldData.instance().spawnData().setGenerated( world.provider.dimensionId, chunkX, chunkZ ); + WorldData.instance().compassData().service().updateArea( world, chunkX, chunkZ ); return null; }